Vue2.x中,如果有一个组件传入了slot,那么每次父组件更新的时候,必定会强制使子组件update,造成性能的浪费。这是由于2.x中,组件的插槽会被当成组件的一个普通children,因此在2.x里面的处理就是只要一个component中传入了slot,那么如果父组件更新,必定会update子组件。
Vue3优化了Slot的生成,使得非动态slot中属性的更新只会触发子组件的更新。动态slot指的是在slot上面使用v-if,v-for,动态slot名字等会导致slot产生运行时动态变化但是又无法被子组件track的操作。(一般来说还是很少在slot上做这种操作的)
Vue3实现这个优化的逻辑主要是这样的:
- 首先还是静态编译时候做的工作,给一个Component打上一个PatchFlag标记---是否是DynamicSlot,这一块的逻辑在compiler-core/src/transforms/transformElement.ts中。
- 遇到有传入slot的组件,它的Children不是普通的vnode数组,而是一个slot function的映射表,这些slot function用于在组件中懒生成slot中的vnodes,如下是一个有传入slot的组件生成的render function。
_createVNode(_component_sub_com, null, {
// _withCtx使得可以访问父组件的context
default: _withCtx(() => [
_createTextVNode(_toDisplayString(_ctx.count), 1 /* TEXT */)
]),
_: 1
})
- 在子组件的render函数里面,调用相应的slot生成函数,因此这个slot函数里面的属性都会被当前的组件实例所track。
- 以上过程就实现了插槽被正确的组件实例所追踪,最后,关于第一步所打的标记,如果传入的slot是动态slot,那么会在第二步的createVNode函数中传入DynamicSlot的PatchFlag,在虚拟dom的patch过程中,遇到一个组件有DynamicSlot,就和Vue 2.x 一样,随着父组件更新强制更新这个组件。