Vue中Slot的实现机制
2021-9-19
| 2023-2-19
0  |  0 分钟
password
Created
Feb 19, 2023 03:35 PM
type
Post
status
Published
date
Sep 19, 2021
slug
summary
介绍Vue中插槽Slot的使用及实现方式
tags
Vue
Vue Slot
Vue源码
category
源码
icon
插槽的核心是: 在父组件中预定义好子组件插槽部分的模板, 然后再子组件渲染的过程中, 将插槽中的这部分内容填充到这个子组件定义的插槽中.
因此, 在父组件渲染的阶段, 子组件插槽部分的DOM是不能渲染的, 需要以某种方式保留下来, 等子组件渲染的时候, 在一并渲染, 并携带上子组件的对应作用域.

插槽初始化

 function setupComponent(instance, isSSR = false) {    const { props, children, shapeFlag } = instance.vnode;    // ...    initSlots(instance, children)    // ...  }

initSlots

主要就是将插槽对象保存到了instance.slots中, 以便后续拿取
 const initSlots = (instance, children) => {    // 32: SLOTS_CHILDREN    if (instance.vnode.shapeFlag & 32) {      const type = children._      if (type) {        instance.slots = children        def(children, '_', type)     } else {        normalizeObjectSlots(children, (instance.slots = {}))     }   } else {      instance.slots = {}      if (children) {        normalizeVNodeSlots(instance, children)     }   }    def(instance.slots, InternalObjectKey, 1)  }

renderSlot

SFC编译之后的代码中, slot使用的是renderSlot函数来进行处理
  1. 根据name来获取对应的插槽函数slot(从前面的slots, 也就是instance.slots中取)
  1. 通过createBlock创建vnode节点
 function renderSlot(slots, name, props = {}, fallback) {    // 获取对应的slot    let slot = slots[name]    // 1: STABLE 64: STABLE_FRAGMENT -2: BAIL    return (openBlock(), createBlock(Fragment, {key: props.key}, slot ? slot(props) : fallback ? fallback() : [], slots._ === 1 ? 64 : -2))  }

withCtx

再具体到slots函数中, 有这样的结构
 {    default: _withCtx(() => [      // 1: TEXT      _createVNode("p", null, _toDisplayString(_ctx.main), 1)   ])  }  function withCtx(fn, ctx = currentRenderingInstance) {    if (!ctx) return fn    return function renderFnWithContext() {      // 先保存当前渲染组件实例      const owner = currentRenderingInstance      // 然后将ctx设置为当前渲染组件实例      setCurrentRenderingInstance(ctx)      // 执行      const res = fn.apply(null, arguments)      // 完毕之后再将渲染组件实例设置回去      setCurrentRenderingInstance(owner)      return res   }  }
如上代码, 将ctx进行了切换执行处理, 然后再设置回来
这样的好处是, 既能够保证在子组件中渲染具体插槽内容, 又能保证它的数据作用域也是父组件

总结

插槽实际上就是一种延时渲染, 把父组件中编写的插槽内容保存到一个对象上
并且吧具体渲染DOM的代码用函数的方式封装, 然后再子组件渲染的时候, 根据插槽名在对象中找到对应的函数, 然后执行这些函数做真正的渲染
源码
  • Vue
  • Vue Slot
  • Vue源码
  • Vue中Props的实现机制Vue中的侦听器(watch)
    目录