普通插槽
父组件编译阶段:processSlotContent
./src/compiler/parser/index.js
对于普通插槽会给ast结点添加slot属性,其值为slotTarget。
如
子组件编译阶段:processSlotContent
./src/compiler/parser/index.js
给AST结点添加slotName属性,其值为name属性对应的值。
如 那么 AST上就添加{slotName:"default"}
如,那么AST上就没有slotName属性,但是在genSlot的时候会给一个default
slot是抽象包裹标签,所有不用加上key属性
子组件编译阶段:genSlot
./src/compiler/condegen/index.js
genSlot最后会生成_t(...略),这个_t函数最后在运行时会生成VNode。
这里的children指的是标签内的内容,其会作为插槽的默认内容,在父组件没有替换对应的插槽的时候显示。
运行时:_t()
./src/core/instance/render-helpers/render-slot
普通域插直接返回vnodes
但是这里出现了$slots属性。这个属性是何时产生的?
$slots属性:初始化组件时,inintRender内定义
这里出现了_parentVnode.context及_renderChildren
_parentVNode:createComponentInstanceForVnode时添加
在patch组件过程中,初始化组件触发init钩子,从而递归创建组件。
init
createComponentInstanceForVnode
在实例化子组件的时候,传入了含有_parentVnode(占位符VNode)的options。在子组件实例化的时候调用就能取到_parentVNode。
_renderChildren:组件初始化,initInternalComponent时添加
在创建占位符VNode的时候,会传入子节点(非子组件)vnode数组。
在子组件初始化的时候取到_parentVNode.componentOptions.children的值,即,在父组件内子组件标签内的所有子节点
回到vm.$slots:resolveSlots,给子组件挂上slots属性
在解决了_renderChildren与renderContext是什么之后就可以分析resolveSlots函数了
这里需要注意的是child.context含义:child为当前父组件的子组件标签内的所有子节点VNode。
因此child.context与renderContext的值都指向父组件,于是就生成一个“具名“插槽数组。这里的具名包括default。
而另一个判断的分支是渲染出组件中没有slot属性的节点。如
没有slots属性,就当成默认插槽。回到 renderSlot,通过子组件vnode的$slots属性(内含父组件渲染出的内容)替换子组件对应名称的插槽
回到最开始,调用render函数的时候,遇到slot会使用renderSlot函数渲染。
做的事情很简单,对于普通插槽返回$slots内对应的VNode。
this.$slots[name] || fallback的含义就是有限获取父组件内的插槽内容,如果没有的话就取子组件的默认值。fallback就是genSlot解析出的的子节点。
小结
普通插槽的渲染过程最重要的就是父组件中占位符节点的子节点是在父组件中渲染的(vue中一般都是如此),其child.context其实是父组件,那么就可以拿到父组件中的数据了。
另外,rederSlot函数的fallback是在genSlot函数解析子组件标签内容后产生的,其fallback就是子组件中标签内部的内容,如果父组件中没有传入名称对应的插槽,那么这些fallback就作为默认内容显示,否则就被替换掉了。