时间:2022-11-07 09:37:28 | 栏目:vue | 点击:次
动态组件我相信大部分在开发的过程中都会用到,当我们需要在不同的组件之间进行状态切换时,动态组件可以很好的满足我们的需求,其中的核心是component标签和is属性的使用。
// vue <div id="app"> <button @click="changeTabs('child1')">child1</button> <button @click="changeTabs('child2')">child2</button> <button @click="changeTabs('child3')">child3</button> <component :is="chooseTabs"> </component> </div>
// js var child1 = { template: '<div>content1</div>', } var child2 = { template: '<div>content2</div>' } var child3 = { template: '<div>content3</div>' } var vm = new Vue({ el: '#app', components: { child1, child2, child3 }, methods: { changeTabs(tab) { this.chooseTabs = tab; } } })
<component>的解读和前面几篇内容一致,会从AST解析阶段说起,过程也不会专注每一个细节,而是把和以往处理方式不同的地方特别说明。针对动态组件解析的差异,集中在processComponent上,由于标签上is属性的存在,它会在最终的ast树上打上component属性的标志。
// 针对动态组件的解析 function processComponent (el) { var binding; // 拿到is属性所对应的值 if ((binding = getBindingAttr(el, 'is'))) { // ast树上多了component的属性 el.component = binding; } if (getAndRemoveAttr(el, 'inline-template') != null) { el.inlineTemplate = true; } }
有了ast树,接下来是根据ast树生成可执行的render函数,由于有component属性,render函数的产生过程会走genComponent分支。
// render函数生成函数 var code = generate(ast, options); // generate函数的实现 function generate (ast,options) { var state = new CodegenState(options); var code = ast ? genElement(ast, state) : '_c("div")'; return { render: ("with(this){return " + code + "}"), staticRenderFns: state.staticRenderFns } } function genElement(el, state) { ··· var code; // 动态组件分支 if (el.component) { code = genComponent(el.component, el, state); } }
针对动态组件的处理逻辑其实很简单,当没有内联模板标志时(后面会讲),拿到后续的子节点进行拼接,和普通组件唯一的区别在于,_c的第一个参数不再是一个指定的字符串,而是一个代表组件的变量。
// 针对动态组件的处理 function genComponent ( componentName, el, state ) { // 拥有inlineTemplate属性时,children为null var children = el.inlineTemplate ? null : genChildren(el, state, true); return ("_c(" + componentName + "," + (genData$2(el, state)) + (children ? ("," + children) : '') + ")") }
普通组件的render函数
"with(this){return _c('div',{attrs:{"id":"app"}},[_c('child1',[_v(_s(test))])],1)}"
动态组件的render函数
"with(this){return _c('div',{attrs:{"id":"app"}},[_c(chooseTabs,{tag:"component"})],1)}"
简单的总结,动态组件和普通组件的区别在于:
还可以使用如下工厂函数形式的动态组件:
const AsyncComponent = () => ({ // 需要加载的组件 (应该是一个 `Promise` 对象) component: import('./MyComponent.vue'), // 异步组件加载时使用的组件 loading: LoadingComponent, // 加载失败时使用的组件 error: ErrorComponent, // 展示加载时组件的延时时间。默认值是 200 (毫秒) delay: 200, // 如果提供了超时时间且组件加载也超时了, // 则使用加载失败时使用的组件。默认值是:`Infinity` timeout: 3000 }); components: { AsyncComponent, },