Skip to content

Commit 3d34f40

Browse files
committed
fix(runtime-core): bail manually rendered compiler slot fragments in all cases
Previously this bail was only applied on updates but not on initial mount, and leads to different patch code paths between mount and update in edge cases. close #10870
1 parent 23cd614 commit 3d34f40

File tree

5 files changed

+32
-13
lines changed

5 files changed

+32
-13
lines changed

packages/runtime-core/__tests__/componentSlots.spec.ts

+15-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
ref,
88
render,
99
} from '@vue/runtime-test'
10-
import { normalizeVNode } from '../src/vnode'
10+
import { createBlock, normalizeVNode } from '../src/vnode'
1111
import { createSlots } from '../src/helpers/createSlots'
1212

1313
describe('component: slots', () => {
@@ -25,8 +25,21 @@ describe('component: slots', () => {
2525
}
2626

2727
test('initSlots: instance.slots should be set correctly', () => {
28+
let instance: any
29+
const Comp = {
30+
render() {
31+
instance = getCurrentInstance()
32+
return h('div')
33+
},
34+
}
35+
const slots = { foo: () => {}, _: 1 }
36+
render(createBlock(Comp, null, slots), nodeOps.createElement('div'))
37+
expect(instance.slots).toMatchObject(slots)
38+
})
39+
40+
test('initSlots: instance.slots should remove compiler marker if parent is using manual render function', () => {
2841
const { slots } = renderWithSlots({ _: 1 })
29-
expect(slots).toMatchObject({ _: 1 })
42+
expect(slots).toMatchObject({})
3043
})
3144

3245
test('initSlots: should normalize object slots (when value is null, string, array)', () => {

packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ describe('renderer: optimized mode', () => {
434434
const App = {
435435
setup() {
436436
return () => {
437-
return createVNode(Comp, null, {
437+
return createBlock(Comp, null, {
438438
default: withCtx(() => [
439439
createVNode('p', null, foo.value, PatchFlags.TEXT),
440440
]),
@@ -560,6 +560,7 @@ describe('renderer: optimized mode', () => {
560560
const state = ref(0)
561561

562562
const CompA = {
563+
name: 'A',
563564
setup(props: any, { slots }: SetupContext) {
564565
return () => {
565566
return (
@@ -571,6 +572,7 @@ describe('renderer: optimized mode', () => {
571572
}
572573

573574
const Wrapper = {
575+
name: 'Wrapper',
574576
setup(props: any, { slots }: SetupContext) {
575577
// use the manually written render function to rendering the optimized slots,
576578
// which should make subsequent updates exit the optimized mode correctly
@@ -581,6 +583,7 @@ describe('renderer: optimized mode', () => {
581583
}
582584

583585
const app = createApp({
586+
name: 'App',
584587
setup() {
585588
return () => {
586589
return (

packages/runtime-core/src/component.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -736,13 +736,14 @@ export let isInSSRComponentSetup = false
736736
export function setupComponent(
737737
instance: ComponentInternalInstance,
738738
isSSR = false,
739+
optimized = false,
739740
) {
740741
isSSR && setInSSRSetupState(isSSR)
741742

742743
const { props, children } = instance.vnode
743744
const isStateful = isStatefulComponent(instance)
744745
initProps(instance, props, isStateful, isSSR)
745-
initSlots(instance, children)
746+
initSlots(instance, children, optimized)
746747

747748
const setupResult = isStateful
748749
? setupStatefulComponent(instance, isSSR)

packages/runtime-core/src/componentSlots.ts

+10-8
Original file line numberDiff line numberDiff line change
@@ -164,14 +164,23 @@ const normalizeVNodeSlots = (
164164
export const initSlots = (
165165
instance: ComponentInternalInstance,
166166
children: VNodeNormalizedChildren,
167+
optimized: boolean,
167168
) => {
168169
const slots = (instance.slots = createInternalObject())
169170
if (instance.vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
170171
const type = (children as RawSlots)._
171172
if (type) {
172173
extend(slots, children as InternalSlots)
173174
// make compiler marker non-enumerable
174-
def(slots, '_', type, true)
175+
if (optimized) {
176+
def(slots, '_', type, true)
177+
} else {
178+
// #2893
179+
// when rendering the optimized slots by manually written render function,
180+
// we need to delete the `slots._` flag if necessary to make subsequent
181+
// updates reliable, i.e. let the `renderSlot` create the bailed Fragment
182+
delete slots._
183+
}
175184
} else {
176185
normalizeObjectSlots(children as RawSlots, slots, instance)
177186
}
@@ -205,13 +214,6 @@ export const updateSlots = (
205214
// compiled but dynamic (v-if/v-for on slots) - update slots, but skip
206215
// normalization.
207216
extend(slots, children as Slots)
208-
// #2893
209-
// when rendering the optimized slots by manually written render function,
210-
// we need to delete the `slots._` flag if necessary to make subsequent updates reliable,
211-
// i.e. let the `renderSlot` create the bailed Fragment
212-
if (!optimized && type === SlotFlags.STABLE) {
213-
delete slots._
214-
}
215217
}
216218
} else {
217219
needDeletionCheck = !(children as RawSlots).$stable

packages/runtime-core/src/renderer.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1229,7 +1229,7 @@ function baseCreateRenderer(
12291229
if (__DEV__) {
12301230
startMeasure(instance, `init`)
12311231
}
1232-
setupComponent(instance)
1232+
setupComponent(instance, false, optimized)
12331233
if (__DEV__) {
12341234
endMeasure(instance, `init`)
12351235
}

0 commit comments

Comments
 (0)