Vue2实现无限级递归组件渲染
- 需求
- 实现方案
- 效果
需求
之前工作要做一个可视化配置组态页面的系统,前端需要拖拽组件进行布局设计,当然最开始做的时候实现的比较简单,单纯的一张画布去渲染全部的组件。但后期项目实施去设计时需要同时操作多个组件的的显示切换,就想着做一些常用的布局组件如Tab布局组件(可以切换多个tab页进行布局),分支布局组件(根据配置的条件显示不同的内容),可以把那些子组件放到这个布局组件中渲染,当然布局组件也可以进行嵌套。
网上搜索Vue的递归组件渲染示例,通常都是官方示例类似的树组件渲染,其递归渲染的也是其组件本身。但本人的需求是递归渲染一类组件。比如:一个画布下有文本组件,Tab布局组件,Tab布局组件中又包含了分支布局组件,分支组件中又嵌套了Tab布局组件,文本组件等。
实现方案
要实现上面的效果,用vue的模板就不行了,因为要递归渲染很多不同的组件,后期可能还会增加,这里我们使用render函数来实现。将画布中的所有组件都保存到一个数组中,每个组件都有一个rawId标识,并且如果组件在布局组件内,则为组件添加一个属性layoutRawId表明关联的布局组件(像tab布局容器,里面会有多个tab页面要设计,我的方案是每个tab页内容都放到不同的一个插槽中,组件被拖进去时赋予一个slotId关联插槽,这样渲染时将对应组件放到正确的插槽中渲染)
具体代码如下:
<script>
import Vue from "vue"
//通过组件信息中的layoutRawId 将组件进行分组整理,子组件放置到compos属性中
import {groupingComponentByLayout} from './Utils'
//记录所有组件信息,组件有一个唯一的code作为key,值为 import的vue组件
import {componentMap} from "@views/componentConfig";
export default {
name:"RecursionRenderComponent",
data(){
return{
}
},
methods:{
},
render(createElement, context) {
//此方法主要渲染非布局类的组件,即该类组件不会有嵌套
const renderSlotFunc = (com,layoutRawId,currentCanvasRawId,paramDataManager,callFunc,webId)=> {
// 注册要渲染的组件
Vue.component(com.code, PageSideConfig.componentMap[com.code])
const vnodeData = {
props: {
layoutRawId: com.rawId,
},
ref: 'ref' + com.rawId
}
if (com.slotId!=null && com.slotId!==''){
//放置到对应插槽渲染(父组件有多个slot时)
vnodeData.slot = com.slotId
}
return createElement(com.code,vnodeData)
}
//渲染布局组件
const renderLayoutFunc = (com,layoutRawId,componentInfos)=>{
Vue.component(com.code,componentMap[com.code])
return createElement(com.code,{
props:{
layoutRawId:com.rawId,
componentInfos:componentInfos,
},
slot:com.slotId,
},componentInfos.length>0 ? renderLoop(com.rawId,componentInfos,currentCanvasRawId):[])
}
//递归渲染组件
const renderLoop = (layoutRawId,componentInfos) =>{
const list = componentInfos.map((com,index)=>{
let vnode = null
if(com.compos==null){
//无子组件,普通渲染
vnode = renderSlotFunc(com,layoutRawId,currentCanvasRawId,paramDataManager,callFunc,webId)
}else{
//布局渲染
vnode = renderLayoutFunc(com,layoutRawId,com.compos,currentCanvasRawId,paramDataManager,callFunc,webId)
}
return vnode
}).filter(node=>node!=null)
return list
}
const groupCompos = groupingComponentByLayout(this.componentInfos,undefined)
return createElement('div',renderLoop(this.layoutRawId,groupCompos))
},
props:{
//所有组件配置信息
componentInfos:{
type:Array,
default:()=>[]
},
//布局rawId
layoutRawId:{
type:String
},
},
watch:{
},
computed:{
},
components:{
}
}
</script>
效果
实现效果:布局组件和普通组件多层级递归渲染