利用VUE异步组件动态加载组件实现自定义组件顺序动态绑定传入子组件的props动态绑定监听子组件的emit事件

48次阅读

共计 3091 个字符,预计需要花费 8 分钟才能阅读完成。

推动我实现这个功能的业务背景

最近接到一个让我很头疼的需求:产品要求我们系统页面上所有的模块都支持顺序的变动。
比如有 模块 A、B、C、D,可以无序的展示在页面上,我刚听到这个需求的时候我是崩溃的,如果是在项目开发之前提出这个需求,那么我的前期页面的架构肯定不会直接写死的。现在,如果想满足这个需求,我只能翻掉之前的页面重新开发 …..
目前项目是有八个模块,我是每个模块封装一个单独的组件,然后再 index 页面统一引入。

  • 救命稻草

浏览了一番 vue 的官网,还是有所收获的。发现了动态组件 & 异步组件这个东西!!!简直是救命啊!!!

动态组件:

异步组件:

  • 思路分析

有了动态组件这个东西之后,我们就可以根据:is 绑定不同的值来渲染不同的组件。比如,拿到后台给我们返回的要渲染组件顺序的数组,我们通过循环数组,构建出一个最终我们想要的数据格式。关键点在于动态修改 () => import(”) 里面的值。每个组件要传给子组件的值和接收子组件 emit 的事件也可以动态的绑定上去。好了,废话不多说了,贴代码吧!

  • 代码

首先是 HTML 层:

<template>
    <div class="temp">
        <!-- tempList 是经过处理后的数组 -->
        <div v-for="com in tempList" :key="com.key">
            <component v-bind="com.props" v-on="com.fn" :is="com.app" />
        </div>
    </div>
</template>

js 层:
<script>

import axios from 'axios';
import Volume from "com/Volume";
import ServiceStatus from "com/ServiceStatus";
import FixStatus from "com/FixStatus";

export default {
    name: 'about',
    props: {msg: String},
    data() {
        return {
            isShow: false,
            tempList: [],
            tempData: [],
            VolumeOptions: 'VolumeOptionsValueFromParent',
            ServiceStatusOptions: 'ServiceStatusOptionsValueFromParent',
            FixStatusOptions: 'FixStatusOptionsValueFromParent'
        };
    },
    created() {this.createTempData()
    },
    methods: {
        // 获取组件的顺序
        getTempList() {
            // 这里是我自己用 nodejs mock 的一个接口,返回的数据在下面贴出来
            return axios.get('http://localhost:9999/search/detail').then(res => {return res.data.data})
        },
        async createTempData() {const result = await this.getTempList();
            // 动态修改 options 绑定的变量
            result.forEach((val) => {
                let key = val.tempName;
                switch (key) {
                    case 'Volume':
                        val.options = this.VolumeOptions
                        break;
                    case 'ServiceStatus':
                        val.options = this.ServiceStatusOptions
                        break;
                    case 'FixStatus':
                        val.options = this.FixStatusOptions
                        break;
                }
            })
            this.tempData = result;
            console.log(this.tempData);
            this.init()},
        init() {
            // 构建渲染页面组件的数组
            this.tempList = this.tempData.map((value, index) => {
                return {app: () => import(`com/${value.tempName}`), // 异步组件
                    key: index,
                    props: {options: value.options, // 传给子组件的 options},
                    fn: {change: this.changeTest // 接收来自子组件的 $emit 事件}
                };
            });
        },
        changeTest(e) {console.log('监听子组件得到的值是:' + e)
        }
    }
};


子组件代码:
1.Volume 组件:

<template>
    <div id="test">
        Volume page 
        <div @click="$emit('change','Volume')">props 的值:{{options}}</div>
    </div>
</template>

<script>
    export default {
        name: 'Volume',
        props: {
            options: {
                type: String,
                default: ''
            }
        }
    }
</script>

2.FixStatus 组件:

<template>
    <div id="FixStatus" class="comm">
        组件名:FixStatus page 
        <div @click="$emit('change','FixStatus')">props 的值:{{options}}</div>
    </div>
</template>

<script>
    export default {
        name: 'FixStatus',
        props: {
            options: {
                type: String,
                default: ''
            }
        }
    }
</script>
<style lang="scss" scoped>
    @import url('../assets/scss/comm.scss');
</style>


3.ServiceStatus 组件:

<template>
    <div id="ServiceStatus" class="comm">
        组件名:ServiceStatus page
        <div @click="$emit('change','ServiceStatus')">props 的值:{{options}}</div>
    </div>
</template>

<script>
    export default {
        name: 'ServiceStatus',
        props: {
            options: {
                type: String,
                default: ''
            }
        }
    }
</script>
<style lang="scss" scoped>
    @import url('../assets/scss/comm.scss');
</style>

接口返回的数据:

{"code":"0000","msg":"请求成功!","data":[{"id":0,"tempName":"ServiceStatus","options":"''"},{"id":1,"tempName":"Volume","options":"''"},{"id":2,"tempName":"FixStatus","options":"''"}]}

data 的数组就是我们可以自定义顺序的数组。好了,是不是可以随意的玩起来了!下面看一下 demo 页面效果吧。

  • 五百万项目的效果

可以看到:页面组件的排列顺序就是根据接口返回的顺序排列的、每个子组件 props 得到的值也是可以的、控制台 console 是我点击不同组件,emit 给父组件的值。
这是我目前想到最妥当的方案,如果有巨佬有更好的思路,欢迎指导!扣扣 602353272。
溜了溜了 ….

正文完
 0