vue3动态渲染组件

文章讨论了一个使用Vue开发地图类页面时遇到的代码组织问题。作者通过动态引入、注册和使用组件来优化代码结构,减少了冗余。同时,尝试使用Vuex进行状态管理,但在父子组件通信和CSS样式方面遇到了挑战。最后,作者分享了代码示例,展示了如何动态加载和渲染组件。
摘要由CSDN通过智能技术生成

      问题:我用vue在写一个地图类的页面时,小组件比较多,包括,书签,图层列表,资源目录...,刚开始我就是用父子组件嵌套,但随着小组件越来越多,页面越来越长,很多子组件的方法,也在父组件中调用,虽然也都是组件化,但是有1200多行,看着有些乱,各种方法调用,其他人看越来越费劲,

     想法:我发现有一些重复的部分,重复的部分包括,组件引入,组件的注册,组件的使用,属性的传递。。。想着看看重复的部分能不能优化下,引入vuex等等,让代码看着不那么乱。

     解决方法:于是想着能不能用参数配置,动态引入组件,动态注册组件,动态使用组件。

先看下效果图,工具箱参数配置(一些对象配置,包括工具的位置,大小等等)

1.原先引入组件

import BookMarks from "./widgets/BookMarks";
....

优化后,动态引入组件:

//正则匹配查找widgets文件夹下,以.vue结尾的文件
const files = require.context("../widgets", true, /\.vue$/);
let widgets = files.keys().map((file) => {
    return files(file).default;
});

2.原先注册组件

components: {
     BookMarks,
     //其他的组件...
},

优化后,动态注册组件:

widgets.forEach((widget) => {
      // 动态注册子组件
      this.$options.components[widget.name] = widget;
});

3.原先使用组件

<template>
    <div style="width: 100%; height: 100%">
       //其他省略的代码
        <BookMarks></BookMarks>
    </div>
</template>

优化后动态使用组件,使用h(),render优化后

render的官网中使用方法:渲染函数 & JSX | Vue.js

//widget为第二部widgets中,符合条件的widget
let vnode = h(widget, { 'id': 'id'}, )

let newElement = document.createElement('div');    
document.body.appendChild(newElement)
newElement.id = 'newDiv'; 

//render渲染子组件
render(vnode ,document.getElementById("newDiv"))

 我最先开始也是用的render,但是在使用中,需要父子组件传值,试了prop,vuex都没能实现传值,antdesign 组件的css样式也丢失了,所以我又用了内置组件component,使用component也有些其他的问题,如何渲染多个组件等等,如果v-for循环component,那component绑定不同is属性感觉也不太好,希望评论区踊跃探讨。

4.接下来,看看源代码吧。

我用的是arcgis js api,本来是想将mapview属性用vuex管理,但是报错,所以才用事件传值的方式

<template>
    <div style="width: 100%; height: 100%">
        <component :is="selectLayout.componentName">
            <template #header>
                <map-app-header />
            </template>
            <template #content>
                <BaseMap @getMapview="getMapview" />
            </template>
            <slot>
                <div :style="toolBox.position">
                    <MapAppTool @clickMapTool="clickTool" />
                </div>
                <div id="mapAppContainer" ref="mapAppContainer">
                    <KeepAlive :max="10">
                        <component :is="toolComponentName" :mapview="mapview" >
                        </component>
                    </KeepAlive>
                </div>
            </slot>
        </component>
    </div>
</template>

<script>
// import { h, render,reactive,ref } from "vue";
import BaseMap from "../map/BaseMap.vue";
import MapAppHeader from "../map/components/MapAppHeader.vue";
import MapAppTool from "../map/components/MapAppTool.vue";
import { mapGetters, mapMutations } from "vuex";

// 动态引入子组件
const files = require.context("../map/widgets", true, /\.vue$/);
let widgets = files.keys().map((file) => {
    return files(file).default;
});

export default {
    components: {
        BaseMap,//自定义的地图组件
        MapAppHeader,//自定义的头部组件
        MapAppTool,//自定义的工具箱组件
    },
    computed: {
        ...mapGetters("map", ["selectLayout"]),
        toolBox: function () {
            return this.selectLayout.layout.find((item) => item.name == "tool");
        },
    },
    data() {
        return {
            toolComponentName: null,
        };
    },
    created() {
        widgets.forEach((widget) => {
            // 动态注册子组件
            this.$options.components[widget.name] = widget;
        });
    },
    mounted() {},
    methods: {
        ...mapMutations("map", ["updateWidgetInfo"]),
        //获取地图
        getMapview(mapView) {            
            this.mapview = mapView;
        },
        clickTool(tool) {
            this.updateWidgetInfo({
                name: tool.name,
                field: "display",
                val: !tool.display,
            })

            if(tool.componentName){
                document.getElementById("mapAppContainer").style = tool.position;
                this.toolComponentName = tool.componentName
            }

            if(tool.componentName && tool.type == "button"){
                if(tool.display){
                    document.getElementById("mapAppContainer").style.display = 'none'
                }else{
                    document.getElementById("mapAppContainer").style.display = 'block'
                }
            }

            //通过render渲染,也能渲染出来,但是antdesign的css样式丢失了

            // let widget = widgets.find(item=>item.name==tool.componentName)
            // const div2 = h(widget);

            // div2.type.props.config = reactive(tool)//没效果
            // div2.type.data().mapview = ref('mapview') //没效果
           

            // const asyncComponent = defineAsyncComponent(div);
            // render(div2,document.getElementById("mapAppContainer"))
        },
    },
};
</script>
<style scoped>
.map_app {
    width: 100%;
    height: 100%;
    position: relative;
}
</style>

 //布局,也可以自定义多个想要的样式,我是全局引入的自定义布局样式,所以在上边调用的时候,就没有显示引入

<!--自定义布局样式1头部和内容-->
<template>
    <a-layout>
        <a-layout-header :style="header.style"><slot name="header"></slot></a-layout-header>
        <a-layout-content>
            <slot name="content"></slot>
        </a-layout-content>
        <slot></slot>
    </a-layout>
</template>
<script>
import { mapGetters } from "vuex";
export default {
    computed: {
        ...mapGetters('map',[
            "selectLayout"
        ]),
        header: function () {
            return this.selectLayout.layout.find(item=>item.name.toLowerCase() == 'header')
        },
    },
};
</script>
<style scoped>
.ant-layout-content {
    width: 100%;
    height: calc(100vh - 64px);
    position: relative;
}
</style>
/*书签组件配置*/
export default {
    id: 'map-tool-bookmarks',
    name: "bookmarks",
    componentName: "BookMarks",
    visible: true,
    display: true,
    alt: "书签",
    type: "button",
    icon: "&#xe7ae;",
    iconClass:"icon-shuqian",
    event: "handleToggleVisibleClick",
    params: "bookmarks",
    clearEvent: "closeWidgetCard",
    position: "position: absolute;bottom: 10px;right: 50px; border: '1px red solid'"
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值