fix(compiler-core): force dynamic slots when slot referencing scope vars (#9427)

close #9380
This commit is contained in:
edison 2025-09-02 17:24:56 +08:00 committed by GitHub
parent 15fc75f403
commit 99d54b28b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 41 additions and 11 deletions

View File

@ -478,7 +478,10 @@ describe('compiler: transform component slots', () => {
}) })
test('should only force dynamic slots when actually using scope vars w/ prefixIdentifiers: true', () => { test('should only force dynamic slots when actually using scope vars w/ prefixIdentifiers: true', () => {
function assertDynamicSlots(template: string, shouldForce: boolean) { function assertDynamicSlots(
template: string,
expectedPatchFlag?: PatchFlags,
) {
const { root } = parseWithSlots(template, { prefixIdentifiers: true }) const { root } = parseWithSlots(template, { prefixIdentifiers: true })
let flag: any let flag: any
if (root.children[0].type === NodeTypes.FOR) { if (root.children[0].type === NodeTypes.FOR) {
@ -491,8 +494,8 @@ describe('compiler: transform component slots', () => {
.children[0] as ComponentNode .children[0] as ComponentNode
flag = (innerComp.codegenNode as VNodeCall).patchFlag flag = (innerComp.codegenNode as VNodeCall).patchFlag
} }
if (shouldForce) { if (expectedPatchFlag) {
expect(flag).toBe(PatchFlags.DYNAMIC_SLOTS) expect(flag).toBe(expectedPatchFlag)
} else { } else {
expect(flag).toBeUndefined() expect(flag).toBeUndefined()
} }
@ -502,14 +505,13 @@ describe('compiler: transform component slots', () => {
`<div v-for="i in list"> `<div v-for="i in list">
<Comp v-slot="bar">foo</Comp> <Comp v-slot="bar">foo</Comp>
</div>`, </div>`,
false,
) )
assertDynamicSlots( assertDynamicSlots(
`<div v-for="i in list"> `<div v-for="i in list">
<Comp v-slot="bar">{{ i }}</Comp> <Comp v-slot="bar">{{ i }}</Comp>
</div>`, </div>`,
true, PatchFlags.DYNAMIC_SLOTS,
) )
// reference the component's own slot variable should not force dynamic slots // reference the component's own slot variable should not force dynamic slots
@ -517,14 +519,13 @@ describe('compiler: transform component slots', () => {
`<Comp v-slot="foo"> `<Comp v-slot="foo">
<Comp v-slot="bar">{{ bar }}</Comp> <Comp v-slot="bar">{{ bar }}</Comp>
</Comp>`, </Comp>`,
false,
) )
assertDynamicSlots( assertDynamicSlots(
`<Comp v-slot="foo"> `<Comp v-slot="foo">
<Comp v-slot="bar">{{ foo }}</Comp> <Comp v-slot="bar">{{ foo }}</Comp>
</Comp>`, </Comp>`,
true, PatchFlags.DYNAMIC_SLOTS,
) )
// #2564 // #2564
@ -532,14 +533,35 @@ describe('compiler: transform component slots', () => {
`<div v-for="i in list"> `<div v-for="i in list">
<Comp v-slot="bar"><button @click="fn(i)" /></Comp> <Comp v-slot="bar"><button @click="fn(i)" /></Comp>
</div>`, </div>`,
true, PatchFlags.DYNAMIC_SLOTS,
) )
assertDynamicSlots( assertDynamicSlots(
`<div v-for="i in list"> `<div v-for="i in list">
<Comp v-slot="bar"><button @click="fn()" /></Comp> <Comp v-slot="bar"><button @click="fn()" /></Comp>
</div>`, </div>`,
false, )
// #9380
assertDynamicSlots(
`<div v-for="i in list">
<Comp :i="i">foo</Comp>
</div>`,
PatchFlags.PROPS,
)
assertDynamicSlots(
`<div v-for="i in list">
<Comp v-slot="{ value = i }"><button @click="fn()" /></Comp>
</div>`,
PatchFlags.DYNAMIC_SLOTS,
)
assertDynamicSlots(
`<div v-for="i in list">
<Comp v-slot:[i]><button @click="fn()" /></Comp>
</div>`,
PatchFlags.DYNAMIC_SLOTS,
) )
}) })

View File

@ -131,9 +131,17 @@ export function buildSlots(
// since it likely uses a scope variable. // since it likely uses a scope variable.
let hasDynamicSlots = context.scopes.vSlot > 0 || context.scopes.vFor > 0 let hasDynamicSlots = context.scopes.vSlot > 0 || context.scopes.vFor > 0
// with `prefixIdentifiers: true`, this can be further optimized to make // with `prefixIdentifiers: true`, this can be further optimized to make
// it dynamic only when the slot actually uses the scope variables. // it dynamic when
// 1. the slot arg or exp uses the scope variables.
// 2. the slot children use the scope variables.
if (!__BROWSER__ && !context.ssr && context.prefixIdentifiers) { if (!__BROWSER__ && !context.ssr && context.prefixIdentifiers) {
hasDynamicSlots = hasScopeRef(node, context.identifiers) hasDynamicSlots =
node.props.some(
prop =>
isVSlot(prop) &&
(hasScopeRef(prop.arg, context.identifiers) ||
hasScopeRef(prop.exp, context.identifiers)),
) || children.some(child => hasScopeRef(child, context.identifiers))
} }
// 1. Check for slot with slotProps on component itself. // 1. Check for slot with slotProps on component itself.