# 处理 slots

slots 有很多讲究,最主要的是在 vue3 里面,children 的表示方式(createElement的第三个参数)很多,简单列举一下:

  • [child]
  • child
  • () => child
  • { default: child }
  • { default: [child] }
  • { default: () => child }

当然最终形态是{ default: () => child },vue runtime 在最终的处理结果也基本及时这样的,除非你的 child 是字符串数字之类的非节点类型。

export const initSlots = (
  instance: ComponentInternalInstance,
  children: VNodeNormalizedChildren
) => {
  if (instance.vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
    const type = (children as RawSlots)._;
    if (type) {
      instance.slots = children as InternalSlots;
      // make compiler marker non-enumerable
      def(children as InternalSlots, "_", type);
    } else {
      normalizeObjectSlots(children as RawSlots, (instance.slots = {}));
    }
  } else {
    instance.slots = {};
    if (children) {
      normalizeVNodeSlots(instance, children);
    }
  }
  def(instance.slots, InternalObjectKey, 1);
};

initSlots做的就是根据vnode记载的 children 类型做处理。在创建vnode的时候,会执行normalizeChildren,这里面会根据实际情况累分配shapeFlag。具体在 vnode 章节里面有详细讲解。那么常见的主要就是ShapeFlags.SLOTS_CHILDREN也就是满足if条件的,但其实上下两种情况是差不多的,我们直接看normalizeObjectSlots

const normalizeObjectSlots = (rawSlots: RawSlots, slots: InternalSlots) => {
  const ctx = rawSlots._ctx;
  for (const key in rawSlots) {
    if (isInternalKey(key)) continue;
    const value = rawSlots[key];
    if (isFunction(value)) {
      slots[key] = normalizeSlot(key, value, ctx);
    } else if (value != null) {
      const normalized = normalizeSlotValue(value);
      slots[key] = () => normalized;
    }
  }
};

const normalizeSlot = (
  key: string,
  rawSlot: Function,
  ctx: ComponentInternalInstance | null | undefined
): Slot =>
  withCtx((props: any) => {
    return normalizeSlotValue(rawSlot(props));
  }, ctx);

const normalizeSlotValue = (value: unknown): VNode[] =>
  isArray(value)
    ? value.map(normalizeVNode)
    : [normalizeVNode(value as VNodeChild)];

可以看到主要的区分就是是否是方法,如果是方法,就调用方法,最终都是传给normalizeSlotValue。我们再看一下normalizeVNodeSlots

const normalizeVNodeSlots = (
  instance: ComponentInternalInstance,
  children: VNodeNormalizedChildren
) => {
  const normalized = normalizeSlotValue(children);
  instance.slots.default = () => normalized;
};

我们可以看到最终slots还是变成了{ default: () => xxx }