fix(slots): properly force update on forwarded slots

fix #1594
This commit is contained in:
Evan You
2020-07-15 20:12:49 -04:00
parent 44e6da1402
commit aab99abd28
7 changed files with 99 additions and 22 deletions

View File

@@ -12,7 +12,8 @@ import {
EMPTY_OBJ,
ShapeFlags,
extend,
def
def,
SlotFlags
} from '@vue/shared'
import { warn } from './warning'
import { isKeepAlive } from './components/KeepAlive'
@@ -27,24 +28,25 @@ export type InternalSlots = {
export type Slots = Readonly<InternalSlots>
export const enum CompiledSlotTypes {
STATIC = 1,
DYNAMIC = 2
}
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.
/**
* for tracking slot owner instance. This is attached during
* normalizeChildren when the component vnode is created.
* @internal
*/
_ctx?: ComponentInternalInstance | null
// internal, indicates compiler generated slots
// we use a reserved property instead of a vnode patchFlag because the slots
// object may be directly passed down to a child component in a manual
// render function, and the optimization hint need to be on the slot object
// itself to be preserved.
_?: CompiledSlotTypes
/**
* indicates compiler generated slots
* we use a reserved property instead of a vnode patchFlag because the slots
* object may be directly passed down to a child component in a manual
* render function, and the optimization hint need to be on the slot object
* itself to be preserved.
* @internal
*/
_?: SlotFlags
}
const isInternalKey = (key: string) => key[0] === '_' || key === '$stable'
@@ -141,8 +143,8 @@ export const updateSlots = (
// Parent was HMR updated so slot content may have changed.
// force update slots and mark instance for hmr as well
extend(slots, children as Slots)
} else if (type === CompiledSlotTypes.STATIC) {
// compiled AND static.
} else if (type === SlotFlags.STABLE) {
// compiled AND stable.
// no need to update, and skip stale slots removal.
needDeletionCheck = false
} else {

View File

@@ -1,5 +1,5 @@
import { Data } from '../component'
import { Slots, RawSlots, CompiledSlotTypes } from '../componentSlots'
import { Slots, RawSlots } from '../componentSlots'
import {
VNodeArrayChildren,
openBlock,
@@ -7,7 +7,7 @@ import {
Fragment,
VNode
} from '../vnode'
import { PatchFlags } from '@vue/shared'
import { PatchFlags, SlotFlags } from '@vue/shared'
import { warn } from '../warning'
/**
@@ -39,7 +39,7 @@ export function renderSlot(
Fragment,
{ key: props.key },
slot ? slot(props) : fallback ? fallback() : [],
(slots as RawSlots)._ === CompiledSlotTypes.STATIC
(slots as RawSlots)._ === SlotFlags.STABLE
? PatchFlags.STABLE_FRAGMENT
: PatchFlags.BAIL
)

View File

@@ -8,7 +8,8 @@ import {
normalizeClass,
normalizeStyle,
PatchFlags,
ShapeFlags
ShapeFlags,
SlotFlags
} from '@vue/shared'
import {
ComponentInternalInstance,
@@ -542,10 +543,22 @@ export function normalizeChildren(vnode: VNode, children: unknown) {
return
} else {
type = ShapeFlags.SLOTS_CHILDREN
if (!(children as RawSlots)._ && !(InternalObjectKey in children!)) {
const slotFlag = (children as RawSlots)._
if (!slotFlag && !(InternalObjectKey in children!)) {
// if slots are not normalized, attach context instance
// (compiled / normalized slots already have context)
;(children as RawSlots)._ctx = currentRenderingInstance
} else if (slotFlag === SlotFlags.FORWARDED && currentRenderingInstance) {
// a child component receives forwarded slots from the parent.
// its slot type is determined by its parent's slot type.
if (
currentRenderingInstance.vnode.patchFlag & PatchFlags.DYNAMIC_SLOTS
) {
;(children as RawSlots)._ = SlotFlags.DYNAMIC
vnode.patchFlag |= PatchFlags.DYNAMIC_SLOTS
} else {
;(children as RawSlots)._ = SlotFlags.STABLE
}
}
}
} else if (isFunction(children)) {