5

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

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

  • 救命稻草

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

API

动态组件:
动态组件

异步组件:
图片描述

  • 思路分析

有了动态组件这个东西之后,我们就可以根据: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。
溜了溜了....


前端肥智
116 声望7 粉丝

前端工程师