vue插槽原理之前一直理解的不好,因为之前一直理解错了,首先插槽和作用域插槽是完全不同的东西,要区别对待
对于插槽称之为slot
let AppLayout = {
template: '<div class="container">' +
'<header><slot name="header"></slot></header>' +
'<main><slot>默认内容</slot></main>' +
'<footer><slot name="footer"></slot></footer>' +
'</div>'
}
let vm = new Vue({
el: '#app',
template: '<div>' +
'<app-layout>' +
'<h1 slot="header">{{title}}</h1>' +
'<p>{{msg}}</p>' +
'<p slot="footer">{{desc}}</p>' +
'</app-layout>' +
'</div>',
data() {
return {
title: '我是标题',
msg: '我是内容',
desc: '其它信息'
}
},
components: {
AppLayout
}
})
之前对vnode的创建顺序理解有误,以为父组件创建vnode的时候,创建到app-layout这里的时候,子组件会进行初始化操作,其实是错误的,父组件执行render函数创建vnode的时候,render函数是这样的
with(this){
return _c('div',
[_c('app-layout',
[_c('h1',{attrs:{"slot":"header"},slot:"header"},
[_v(_s(title))]),
_c('p',[_v(_s(msg))]),
_c('p',{attrs:{"slot":"footer"},slot:"footer"},
[_v(_s(desc))]
)
])
],
1)}
有slot属性的节点vnode已经在app-layout组件的vnode创建之前创建了,先创建h1节点对应的vnode,再创建p节点对应的vnode,然后是app-layout对应的vnode,最后才是div对应的vnode,
也就是说在子组件app-layou初始化之前,先创建了子组件子节点对应的slot真实vnode,再创建了子组件的vnode,创建了子组件vnode不代表子组件已经初始化了,仅仅是类似于元素节点的vnode
父组件执行render函数生成vnode之后,会通过patch方法将vnode转化为真是dom,在这个过程里,会将vnode创建为真实dom,然后插入到父vnode对应的真实dom里,创建组件的真实dom的时候,会执行子组件的初始化操作,就是parse,render,patch,即解析模版生成抽象语法树,遍历抽象语法树生成render函数,通过render函数和组件中的数据生成vnode,最后通过patch方法将vnode转化成真实dom
这个时候子组件的render函数是这样的
with(this) {
return _c('div',{
staticClass:"container"
},[
_c('header',[_t("header")],2),
_c('main',[_t("default",[_v("默认内容")])],2),
_c('footer',[_t("footer")],2)
]
)
}
_t是renderSlot方法,就是将slot标签转化成vnode的方法,slot标签相当于一个虚拟的节点,不是真实存在的,相当于一个占位符,slot标签的name属性和父组件里节点的slot属性是一一对应关系,也就是说可以拿slot标签的name属性当作key,去父组件里查找相同的slot属性,然后拿到对应的vnode,然后在renderSlot返回即可,父组件里查找slot属性的范围是子组件的子节点,
export function renderSlot (
name: string,
fallback: ?Array<VNode>,
props: ?Object,
bindObject: ?Object
): ?Array<VNode> {
......
let nodes = this.$slots[name]
return nodes;
}
this.$slots是在子组件初始化的时候,将子组件在父组件里生成的子节点vnode按照slot属性的值进行的处理,this.$slots是一个对象,对象的key是vnode对应的slot属性值,就是先存储起来,等到子组件执行render函数生成自己的vnode的时候,通过slot标签的name属性,在this.$slots里查找到对应的vnode然后返回即可
|