我正在参加「掘金·启航计划」
遇到的问题场景:
- 初始需求是一个列表组件L,一个布局组件C,路由配置如下
{
path: "/home",
component: C,
children: [
{
path: 'list',
component: L
}
]
}
dom的表现形式为<C> <L /> </C>
- 修改需求,增加两种新的布局样式组件
A和B,需要在不同情况下展示不一样的列表布局样式,但<C> <L /> </C>这个保持不变,只需要在最外面再包裹一层,如下所示
<A> <C> <L /> </C> </A>
或者
<B> <C> <L /> </C> </B>
如何尽量的复用布局组件与列表业务组件的组合关系?
可用的解决方案
- 第一种解决思路
再创建两个路由组件A1和B1,此时总共有6个组件文件
A1:
<A> <C></C> </A>
B1:
<B> <C></C> </B>
然后配置路由
{
path: "/home",
component: process.env.platform ? A1 : B1,
children: [
{
path: 'list',
component: L
}
]
}
缺点:如果有多种不同的列表组件和布局组件的组合,将会多创建出了很多组件文件,冗余度高
-
第二种解决思路
为了避免创建过多的文件,可考虑直接在一个组件上引入多个组件解决
// Main.vue Vue.extend({ render (h) { const targetLayout = process.env.platform ? A : B return h(targetLayout, [C]) } }缺点:需要提前
import所有的布局组件,且每次增加布局组件都需要修改当前文件 -
第三种解决思路
使用函数式组件,通过组合的设计模式,可以灵活的组装不同的布局组合,并对布局组件的嵌套进行了解耦
// layoutRender.js export default function(containerLayout, homeLayout) { return { name: "LayoutRender", functional: true, render: function (h, context) { const scopedSlots = { default(){ return h(homeLayout) } } return h(containerLayout, { routerView: true, // 这个属性必须设置 scopedSlots }) } } }此时的路由配置如下:
{ path: "/home", component: LayoutRender(A, C), children: [ { path: 'list', component: L } ] }
进阶阅读-介绍router-view组件的部分细节(不感兴趣可略过)
RouterView组件是一个函数式组件,主要是vue-router库用来渲染子路由组件的
RouterView组件在判断渲染哪一个路由组件时,首先会不断的迭代父组件的vnode直至到达根节点,寻找该vnode的data属性有没有包含routerView属性,如果有则把depth加1,最终计算的值将赋给routerViewDepth,当使用嵌套路由时,匹配到的路由组件会有多个,是个数组类型,通过routerViewDepth则可以获取数组里面对应的元素组件作为路由组件并进行渲染。
部分源码如下
var depth = 0;
while (parent && parent._routerRoot !== parent) {
var vnodeData = parent.$vnode ? parent.$vnode.data : {};
if (vnodeData.routerView) {
depth++;
}
parent = parent.$parent;
}
data.routerViewDepth = depth;
var matched = route.matched[depth];
// 获取需要渲染的子组件
var component = matched && matched.components[name];