Vue高级用法实例教程之动态组件
动态组件我相信大部分在开发的过程中都会用到,当我们需要在不同的组件之间进行状态切换时,动态组件可以很好的满足我们的需求,其中的核心是component标签和is属性的使用。
基础描述
1
2
3
4
5
6
7
8
// 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
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 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;
}
}
})
AST解析
<component>的解读和前面几篇内容一致,会从AST解析阶段说起,过程也不会专注每一个细节,而是把和以往处理方式不同的地方特别说明。针对动态组件解析的差异,集中在processComponent上,由于标签上is属性的存在,它会在最终的ast树上打上component属性的标志。
1
2
3
4
5
6
7
8
9
10
11
12
// 针对动态组件的解析
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
;
}
}
render函数
有了ast树,接下来是根据ast树生成可执行的render函数,由于有component属性,render函数的产生过程会走genComponent分支。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 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的第一个参数不再是一个指定的字符串,而是一个代表组件的变量。
1
2
3
4
5
6
7
8
9
10
11
12
// 针对动态组件的处理
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函数
1
"with(this){return _c('div',{attrs:{"
id
":"
app
"}},[_c('child1',[_v(_s(test))])],1)}"
动态组件的render函数
1
"with(this){return _c('div',{attrs:{"
id
":"
app
"}},[_c(chooseTabs,{tag:"
component
"})],1)}"
简单的总结,动态组件和普通组件的区别在于:
ast阶段新增了component属性,这是动态组件的标志
产生render函数阶段由于component属性的存在,会执行genComponent分支,genComponent会针对动态组件的执行函数进行特殊的处理,和普通组件不同的是,_c的第一个参数不再是不变的字符串,而是指定的组件名变量。
render到vnode阶段和普通组件的流程相同,只是字符串换成了变量,并有{ tag: 'component' }的data属性。例子中chooseTabs此时取的是child1。
工厂函数形式的动态组件
还可以使用如下工厂函数形式的动态组件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import(
'./MyComponent.vue'
),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
});
components: {
AsyncComponent,
},