Vue源码之插槽slot

905 阅读4分钟

新版普通插槽

父组件编译阶段之parse

template

ast

流程

总结

1.在闭合标签后执行closeElement,然后执行processElement执行processSlotContent(element)。

2.首先执行getAndRemoveAttrByRegex遍历el.attrsList找到插槽指令的那条对象返回。

3.如果存在插槽指令的对象,通过getSlotName函数获取到插槽的名字和dynamic。设置当前插槽节点的slotTarget为插槽名,设置slotTargetDynamic为dynamic,设置slotScope如果存在value就设置value否则设置为empty

4.processSlotContent执行完后接着执行processSlotOutlet,函数判断节点tag是否等于slot,显然是不成立的。接着就返回当前插槽节点的ast。

5.processElement执行完后接着会判断当前插槽节点是否存在slotScope,如果存在,获取插槽的名字,如果没有名字就设置为默认值default,接着就是重构demo的ast节点,为demo ast树添加scopedSlots对象,scopedSlots 对象是插槽定义名为key和插槽节点ast树为value。为demo ast树添加children属性由每个插槽ast树组成的数组。设置插槽节点ast树的parent 为demo ast树。

6.最后还会遍历demo ast树的children删除作用域插槽的值,最终children为空数组。

父组件编译阶段之generate

code

流程

总结

1.执行到generate阶段,执行genElement,执行genData,因demo ast节点存在scopedSlots接着执行genScopedSlots函数。

2.genScopedSlots,首先遍历scopedSlots获取key对应的value调用genScopedSlot,genScopedSlot最终返回一个对象插槽名和插槽执行的函数。最后拼接插槽的render表达式。

父组件render & patch

流程

总结

1.父组件render时,遇到组件标签创建组件占位符vnode,七中占位符vnode中的data属性就是父组件generate阶段生成的data对象,里面就有scopedSlots。render执行完后执行patch阶段。

2.patch时创建子组件执行子组件的init钩子函数,调用createComponentInstanceForVnode传入占位符vnode,设置组件实例options的_parentVnode属性为占位符vnode。然后组件初始化执行initInternalComponent。

3.initInternalComponent首先设置组件的options的_parentVnode属性为占位符vnode。

子组件编译阶段之parse

template

ast

流程

总结

1.在闭合标签后执行closeElement,然后执行processElement执行processSlotContent(element),processSlotContent条件不满足啥也不干。

2.接着执行processSlotOutlet,函数判断节点tag是否等于slot,显然成立。设置节点.slotName的值为name属性定义的值如果没有设置为undefined。

子组件编译阶段之generate

code

流程

总结

1.执行到generate阶段,执行genElement,存在tag为slot执行genSlot函数。

2.genSlot函数首先获取节点的slotName也就是插槽名,不存在设置为default,接着如果存在默认值就对默认值进行处理,最后返回_t拼接的render表达式。

子组件render

流程

总结

image.png
image.png

新版作用域插槽

作用域插槽与普通插槽最大的不同就是子组件generate阶段,会对标签的attrs属性和bind指令进行处理,生成的渲染函数会有不同。然后在render阶段接收到传入参数执行插槽函数。

template

流程

新版本插槽总结

⽗组件在编译和渲染阶段并不会直接⽣成 vnodes,⽽是在⽗节点 vnode 的 data 中保留⼀个 scopedSlots 对象,存储着不同名称的插槽以及它们对应的渲染函数,只有在编译和渲染⼦组件阶段才会执⾏这个渲染函数⽣成vnodes,由于是在⼦组件环境执⾏的,所以对应的数据作⽤域是⼦组件实例。

旧版本的普通插槽(前身)

template

父组件ast

  1. 解析ast时会把插槽内容解析成ast插入到demo节点的children中。

父组件render函数

父组件render & patch

image.png
image.png

子组件ast

子组件code

子组件render

这里就可以看到子组件render时冲组件实例的$slots属性中根据插槽名拿到对应的VNode进行渲染。

旧版本插槽总结

旧版本的普通插槽是在⽗组件编译和渲染阶段⽣成 vnodes ,所以数据的作⽤域是⽗组件实例,⼦组件渲染的时候直接拿到这些渲染好的vnodes。对于作用域插槽跟新版的一样。