目前网上查询动态组件渲染的问题,大部分都是借助h(createVNode)函数、createApp、createRender函数之类的来渲染vnode,然后把vnode加载到某个dom节点中,总感觉这样做不太合适,很可能会挖坑。
所以我想要一种更优雅的方式,对项目原有架构、编码习惯破坏最小,不会增加后面维护代码小伙伴的心智负担。
项目背景:现在项目里有个需求,我想要把页面渲染和页面数据解耦,页面如下:
后端大概有七八个组件需要维护配置项,当然最简单的方式肯定是直接在页面里硬编码,以后多一个组件,我就多写一个tab,多写一部分静态html代码来渲染组件,当然还需要多写一部分scripts来引入组件和定义组件。
我简简单单的一个增加组件的需求,需要大致做三项工作:
上面三项工作都很简单,毕竟这就是大部分前端开发者的工作模式,但是它至少有以下几个缺点:
所以依据优化组件配置页面这个需求,我需要有个统一入口,来渲染所有页面元素,以后增加、删除、修改组件只需要修改这一个变量即可。
最好的方式就是定义一个tabList数组,页面渲染逻辑定义好后,所有数据的修改都通过修改tabList来实现。
template部分
<template>
<Tabs value="name1">
<TabPane :label="tabItem.label" :name="tabItem.name" v-for="tabItem in tabList">
{{tabItem.content}}
</TabPane>
</Tabs>
</template>
script部分
import {ref} from 'vue'
const tabList = [{
name: 'midstation',
label: '监听组件',
content:''
},
{
name: 'restart',
label: '重启组件',
content:''
},
]
这是最基础的结构,以后可以把tabList数据部分通过import方式引入进来,这样渲染有变动时,并不会影响tabList,tabList有变动时,不会影响渲染部分,才能真正将渲染与数据解耦,当同一套基础代码分出许多分支时,才能更自如地拉取与合并代码。
因为针对大部分场景,数据经常变动,渲染逻辑基本不变,渲染变化后也应该能够拉取到各个分支而不影响其他分支。(如果理解不了,不用强求,说明目前接触的项目并不需要考虑这些)
上面的代码有个问题,就是每个标签页的内容通过content字段渲染,这个字段是个字符串,实际情况中,标签页中可能是表单、可能是各种工具集合,它可能是很复杂的页面,所以一定得有组件引入的方式。
这就是动态组件引用了。
为什么不能直接通过import引入静态组件,然后把静态组件定义在这个content字段,原因上面概述说过了,我们只是想增加删除一个组件,这个功能很单一,应该只有一个入口——tabList,如果import静态引入组件,我们就需要先引入,再修改tabList,对于这种批量、机械化定义的组件而言,引入的这一步,就是个累赘。
所以我们要用动态引入组件的方式,把变量控制在tabList这一个区域。
概述中的挖坑思路就不多讲了,我最初的想法是通过import函数动态引入后再定义组件的方式,content部分核心代码如下:
const tabList = [{
name: 'midstation',
label: '监听组件',
content:()=>{
import('@/module-install/confSetting/midstation.vue').then(res=>{
com.value=res.default
})
}
}]
这代码参考即可,部分代码未显示,因为这种代码方式被放弃了。它虽然比各种dom操作优雅点,但仍然繁琐,如果要让这种思路可用,需要再封装一个更复杂的函数,专门来处理这种情况。
动态组件(异步组件)并不是一个小众场景,很多ui框架也都实现了,我觉得vue3一定也会考虑这种场景,所以就又翻了翻官网api,果然发现了vue3提供的defineAsyncComponent。
这个api可以处理动态组件,而不需要开发者自己去处理promise对象,它的功能,不就是我们上面想要封装的那个函数吗?这次不用自己做了,改良后代码如下:
const tabList = [{
name: 'midstation',
label: '监听组件',
content: defineAsyncComponent(() =>{
return import('@/module-install/confSetting/midstation.vue')
}
)
},
{
name: 'restart',
label: '重启组件',
content: defineAsyncComponent(() =>{
return import('@/module-install/confSetting/restart.vue')
}
)
},
]
到这,content字段就已经赋值我们动态组件返回的结果了。下面我们把它渲染出来:
<template>
<Tabs value="name1">
<TabPane :label="tabItem.label" :name="tabItem.name" v-for="tabItem in tabList">
<component :is="tabItem.content"></component>
</TabPane>
</Tabs>
</template>
最终结果如图:
完美加载两个组件,并且以后这个tab页面再有修改,只需要修改tabList一个变量即可。
如果文章对同学有帮助,请点赞收藏关注博主,感谢支持。
在人类社会资源池中能抢占多少,看的不只是某一因素,而是你在人堆里的整体排名,排名影响因素包括但不止于家庭、学历、性格、颜值、编程能力……看到这篇文章的同学,大概率前面四个因素已经无法重塑,你能提升人堆里排名的机遇在哪,摆在眼前了!
——鲁迅说:中二少年说的