# 处理 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 }