Vue 动态组件渲染问题分析
fire
读在最前面:
1、本文适用于有一定基础的vue开发者,需要了解基本的vue渲染流程
2、本文知识点涉及vue构造器以及选项策略合并、<component> 渲染逻辑
问题描述:
child继承自app,主程序通过true 和false来控制显示 child 还是 app,在动态<component /> 中渲染出来的始终是app,代码如下
vue.config.productiontip = false;
vue.config.devtools = false;
// ----------------options---------------------
const optionsa = {
render: (h) => h('span', '我是options - 父'),
};
const optionsb = {
render: (h) => h('span', '我是options - 子'),
};
const app = vue.extend({
template: `<div>
当前组件: {{name}}
<br/>
<component :is="node" />
</div>`,
data() {
return {
name: 'app',
node: optionsa,
}
}
});
const child = app.extend({
name: 'child',
data() {
return {
name: 'child',
node: optionsb,
}
}
});
const vm = new vue({
el: '#app',
data() {
return {
issuper: true,
};
},
components: { app, child },
render(h) {
const that = this;
return h('div', {}, [
h('button', {
on: {
click: () => {
this.issuper = true;
}
},
}, '父类'),
h('button', {
on: {
click: () => {
this.issuper = false;
}
},
}, '子类'),
h(this.issuper ? 'app' : 'child')
]);
},
});
如下图(点击父/子类切换,始终显示的是 父文本):
关键执行顺序分析:
1、app通过继承vue生成构造,child通过继承app生成构造
2、默认issuper:true,渲染出app(<component :is="node" /> 编译为render: _c(node),这个时候会在app的node中生成.ctor)
3、切换issuper:false,渲染出child(这里渲染的时候,生成的实例是app,这里是不符合预期的,按理应该是child)
3.1、生成child实例的时候进行了data合并,这个时候data中node变量合并了app的node中的.ctor($options合并策略),参照下图
3.2、在_createelement的时候 node 当为component options / constructor 时,会验证是否 node 是否为object,如果是会转换为构造器 使用vue.extend
3.2、在child中动态调用 new ctor() (这个ctor是app的),生成实例
最后附上大致流程图:
备注:
1、vue.extend会生成vuecomponent构造器,内部包含一个ctor,组件生成的时候就是调用这个new ctor() 进行实例生成
2、选项中data的生成是延迟到实例生成的时候
3、createcomponent在分支<component>渲染时,传入ctor为对象的时候,会转换为构造器,这也是我们这个使用 const optionsa = {render: (h) => h('span', '我是options - 父'), }; 这种方式的问题根源所在
4、知道了问题所在,解决方式就比较多了,比如直接传入构造器,比如绕开data值合并策略,使用method方式。
by:海豚湾-丰