vue3-yuanma/packages/runtime-core/src/componentSlots.ts

160 lines
4.5 KiB
TypeScript
Raw Normal View History

2019-09-07 00:58:31 +08:00
import { ComponentInternalInstance, currentInstance } from './component'
import {
VNode,
VNodeNormalizedChildren,
normalizeVNode,
VNodeChild,
InternalObjectKey
} from './vnode'
import {
isArray,
isFunction,
EMPTY_OBJ,
ShapeFlags,
PatchFlags,
extend,
def
} from '@vue/shared'
import { warn } from './warning'
import { isKeepAlive } from './components/KeepAlive'
import { withCtx } from './helpers/withRenderContext'
2019-05-31 18:07:43 +08:00
2019-11-01 23:32:53 +08:00
export type Slot = (...args: any[]) => VNode[]
2019-10-09 00:43:13 +08:00
export type InternalSlots = {
[name: string]: Slot | undefined
2019-10-09 00:43:13 +08:00
}
export type Slots = Readonly<InternalSlots>
2019-05-31 18:07:43 +08:00
export type RawSlots = {
[name: string]: unknown
// manual render fn hint to skip forced children updates
$stable?: boolean
// internal, for tracking slot owner instance. This is attached during
// normalizeChildren when the component vnode is created.
_ctx?: ComponentInternalInstance | null
// internal, indicates compiler generated slots
_?: 1
2019-05-31 18:07:43 +08:00
}
const isInternalKey = (key: string) => key[0] === '_' || key === '$stable'
2019-05-31 18:07:43 +08:00
const normalizeSlotValue = (value: unknown): VNode[] =>
isArray(value)
? value.map(normalizeVNode)
: [normalizeVNode(value as VNodeChild)]
const normalizeSlot = (
key: string,
rawSlot: Function,
ctx: ComponentInternalInstance | null | undefined
): Slot =>
withCtx((props: any) => {
2020-03-19 06:14:51 +08:00
if (__DEV__ && currentInstance) {
warn(
`Slot "${key}" invoked outside of the render function: ` +
`this will not track dependencies used in the slot. ` +
`Invoke the slot function inside the render function instead.`
)
}
return normalizeSlotValue(rawSlot(props))
}, ctx)
2019-05-31 18:07:43 +08:00
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) {
if (__DEV__) {
warn(
`Non-function value encountered for slot "${key}". ` +
`Prefer function slots for better performance.`
)
}
const normalized = normalizeSlotValue(value)
slots[key] = () => normalized
}
}
}
const normalizeVNodeSlots = (
2019-09-07 00:58:31 +08:00
instance: ComponentInternalInstance,
children: VNodeNormalizedChildren
) => {
if (__DEV__ && !isKeepAlive(instance.vnode)) {
warn(
`Non-function value encountered for default slot. ` +
`Prefer function slots for better performance.`
)
}
const normalized = normalizeSlotValue(children)
instance.slots.default = () => normalized
}
export const initSlots = (
instance: ComponentInternalInstance,
children: VNodeNormalizedChildren
) => {
2019-08-22 23:12:37 +08:00
if (instance.vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
if ((children as RawSlots)._ === 1) {
instance.slots = children as InternalSlots
2019-05-31 18:07:43 +08:00
} else {
normalizeObjectSlots(children as RawSlots, (instance.slots = {}))
}
} else {
instance.slots = {}
if (children) {
normalizeVNodeSlots(instance, children)
}
}
def(instance.slots, InternalObjectKey, 1)
}
export const updateSlots = (
instance: ComponentInternalInstance,
children: VNodeNormalizedChildren
) => {
const { vnode, slots } = instance
let needDeletionCheck = true
let deletionComparisonTarget = EMPTY_OBJ
if (vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
if ((children as RawSlots)._ === 1) {
// compiled slots.
if (
// bail on dynamic slots (v-if, v-for, reference of scope variables)
!(vnode.patchFlag & PatchFlags.DYNAMIC_SLOTS) &&
// bail on HRM updates
!(__DEV__ && instance.parent && instance.parent.renderUpdated)
) {
// compiled AND static.
// no need to update, and skip stale slots removal.
needDeletionCheck = false
} else {
// compiled but dynamic - update slots, but skip normalization.
extend(slots, children as Slots)
2019-05-31 18:07:43 +08:00
}
} else {
needDeletionCheck = !(children as RawSlots).$stable
normalizeObjectSlots(children as RawSlots, slots)
2019-05-31 18:07:43 +08:00
}
deletionComparisonTarget = children as RawSlots
2020-03-19 06:14:51 +08:00
} else if (children) {
// non slot object children (direct value) passed to a component
normalizeVNodeSlots(instance, children)
deletionComparisonTarget = { default: 1 }
}
// delete stale slots
if (needDeletionCheck) {
for (const key in slots) {
if (!isInternalKey(key) && !(key in deletionComparisonTarget)) {
delete slots[key]
}
2019-05-31 18:07:43 +08:00
}
}
}