Skip to content

Commit

Permalink
fix(runtime-core): more edge case fix for manually rendered compiled …
Browse files Browse the repository at this point in the history
…slot

close #11336
  • Loading branch information
yyx990803 committed Jul 12, 2024
1 parent 314ce82 commit 685e3f3
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 10 deletions.
65 changes: 65 additions & 0 deletions packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1040,4 +1040,69 @@ describe('renderer: optimized mode', () => {
expect(app.config.errorHandler).not.toHaveBeenCalled()
}
})

// #11336
test('should bail manually rendered compiler slots for both mount and update (2)', async () => {
// only reproducible in prod
__DEV__ = false
const n = ref(0)
function Outer(_: any, { slots }: any) {
n.value // track
return slots.default()
}
const Mid = {
render(ctx: any) {
return (
openBlock(),
createElementBlock('div', null, [renderSlot(ctx.$slots, 'default')])
)
},
}
const show = ref(false)
const App = {
render() {
return (
openBlock(),
createBlock(Outer, null, {
default: withCtx(() => [
createVNode(Mid, null, {
default: withCtx(() => [
createElementVNode('div', null, [
show.value
? (openBlock(),
createElementBlock('div', { key: 0 }, '1'))
: createCommentVNode('v-if', true),
createElementVNode('div', null, '2'),
createElementVNode('div', null, '3'),
]),
createElementVNode('div', null, '4'),
]),
_: 1 /* STABLE */,
}),
]),
_: 1 /* STABLE */,
})
)
},
}

const app = createApp(App)
app.config.errorHandler = vi.fn()

try {
app.mount(root)

// force Outer update, which will assign new slots to Mid
// we want to make sure the compiled slot flag doesn't accidentally
// get assigned again
n.value++
await nextTick()

show.value = true
await nextTick()
} finally {
__DEV__ = true
expect(app.config.errorHandler).not.toHaveBeenCalled()
}
})
})
29 changes: 19 additions & 10 deletions packages/runtime-core/src/componentSlots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
ShapeFlags,
SlotFlags,
def,
extend,
isArray,
isFunction,
} from '@vue/shared'
Expand Down Expand Up @@ -161,6 +160,22 @@ const normalizeVNodeSlots = (
instance.slots.default = () => normalized
}

const assignSlots = (
slots: InternalSlots,
children: Slots,
optimized: boolean,
) => {
for (const key in children) {
// #2893
// when rendering the optimized slots by manually written render function,
// do not copy the `slots._` compiler flag so that `renderSlot` creates
// slot Fragment with BAIL patchFlag to force full updates
if (optimized || key !== '_') {
slots[key] = children[key]
}
}
}

export const initSlots = (
instance: ComponentInternalInstance,
children: VNodeNormalizedChildren,
Expand All @@ -170,16 +185,10 @@ export const initSlots = (
if (instance.vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
const type = (children as RawSlots)._
if (type) {
extend(slots, children as InternalSlots)
assignSlots(slots, children as Slots, optimized)
// make compiler marker non-enumerable
if (optimized) {
def(slots, '_', type, true)
} else {
// #2893
// when rendering the optimized slots by manually written render function,
// we need to delete the `slots._` flag if necessary to make subsequent
// updates reliable, i.e. let the `renderSlot` create the bailed Fragment
delete slots._
}
} else {
normalizeObjectSlots(children as RawSlots, slots, instance)
Expand All @@ -204,7 +213,7 @@ export const updateSlots = (
if (__DEV__ && isHmrUpdating) {
// 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)
assignSlots(slots, children as Slots, optimized)
trigger(instance, TriggerOpTypes.SET, '$slots')
} else if (optimized && type === SlotFlags.STABLE) {
// compiled AND stable.
Expand All @@ -213,7 +222,7 @@ export const updateSlots = (
} else {
// compiled but dynamic (v-if/v-for on slots) - update slots, but skip
// normalization.
extend(slots, children as Slots)
assignSlots(slots, children as Slots, optimized)
}
} else {
needDeletionCheck = !(children as RawSlots).$stable
Expand Down

0 comments on commit 685e3f3

Please sign in to comment.