Skip to content

Commit

Permalink
fix(runtime-core): bail manually rendered compiler slot fragments in …
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
yyx990803 committed Jul 11, 2024
1 parent 23cd614 commit 3d34f40
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 13 deletions.
17 changes: 15 additions & 2 deletions packages/runtime-core/__tests__/componentSlots.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
ref,
render,
} from '@vue/runtime-test'
import { normalizeVNode } from '../src/vnode'
import { createBlock, normalizeVNode } from '../src/vnode'
import { createSlots } from '../src/helpers/createSlots'

describe('component: slots', () => {
Expand All @@ -25,8 +25,21 @@ describe('component: slots', () => {
}

test('initSlots: instance.slots should be set correctly', () => {
let instance: any
const Comp = {
render() {
instance = getCurrentInstance()
return h('div')
},
}
const slots = { foo: () => {}, _: 1 }
render(createBlock(Comp, null, slots), nodeOps.createElement('div'))
expect(instance.slots).toMatchObject(slots)
})

test('initSlots: instance.slots should remove compiler marker if parent is using manual render function', () => {
const { slots } = renderWithSlots({ _: 1 })
expect(slots).toMatchObject({ _: 1 })
expect(slots).toMatchObject({})
})

test('initSlots: should normalize object slots (when value is null, string, array)', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ describe('renderer: optimized mode', () => {
const App = {
setup() {
return () => {
return createVNode(Comp, null, {
return createBlock(Comp, null, {
default: withCtx(() => [
createVNode('p', null, foo.value, PatchFlags.TEXT),
]),
Expand Down Expand Up @@ -560,6 +560,7 @@ describe('renderer: optimized mode', () => {
const state = ref(0)

const CompA = {
name: 'A',
setup(props: any, { slots }: SetupContext) {
return () => {
return (
Expand All @@ -571,6 +572,7 @@ describe('renderer: optimized mode', () => {
}

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

const app = createApp({
name: 'App',
setup() {
return () => {
return (
Expand Down
3 changes: 2 additions & 1 deletion packages/runtime-core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -736,13 +736,14 @@ export let isInSSRComponentSetup = false
export function setupComponent(
instance: ComponentInternalInstance,
isSSR = false,
optimized = false,
) {
isSSR && setInSSRSetupState(isSSR)

const { props, children } = instance.vnode
const isStateful = isStatefulComponent(instance)
initProps(instance, props, isStateful, isSSR)
initSlots(instance, children)
initSlots(instance, children, optimized)

const setupResult = isStateful
? setupStatefulComponent(instance, isSSR)
Expand Down
18 changes: 10 additions & 8 deletions packages/runtime-core/src/componentSlots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,23 @@ const normalizeVNodeSlots = (
export const initSlots = (
instance: ComponentInternalInstance,
children: VNodeNormalizedChildren,
optimized: boolean,
) => {
const slots = (instance.slots = createInternalObject())
if (instance.vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
const type = (children as RawSlots)._
if (type) {
extend(slots, children as InternalSlots)
// make compiler marker non-enumerable
def(slots, '_', type, true)
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 Down Expand Up @@ -205,13 +214,6 @@ export const updateSlots = (
// compiled but dynamic (v-if/v-for on slots) - update slots, but skip
// normalization.
extend(slots, children as Slots)
// #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
if (!optimized && type === SlotFlags.STABLE) {
delete slots._
}
}
} else {
needDeletionCheck = !(children as RawSlots).$stable
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-core/src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1229,7 +1229,7 @@ function baseCreateRenderer(
if (__DEV__) {
startMeasure(instance, `init`)
}
setupComponent(instance)
setupComponent(instance, false, optimized)
if (__DEV__) {
endMeasure(instance, `init`)
}
Expand Down

0 comments on commit 3d34f40

Please sign in to comment.