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', () => {
function assertDynamicSlots(template: string, shouldForce: boolean) {
function assertDynamicSlots(
template: string,
expectedPatchFlag?: PatchFlags,
) {
const { root } = parseWithSlots(template, { prefixIdentifiers: true })
let flag: any
if (root.children[0].type === NodeTypes.FOR) {
@ -491,8 +494,8 @@ describe('compiler: transform component slots', () => {
.children[0] as ComponentNode
flag = (innerComp.codegenNode as VNodeCall).patchFlag
}
if (shouldForce) {
expect(flag).toBe(PatchFlags.DYNAMIC_SLOTS)
if (expectedPatchFlag) {
expect(flag).toBe(expectedPatchFlag)
} else {
expect(flag).toBeUndefined()
}
@ -502,14 +505,13 @@ describe('compiler: transform component slots', () => {
`<div v-for="i in list">
<Comp v-slot="bar">foo</Comp>
</div>`,
false,
)
assertDynamicSlots(
`<div v-for="i in list">
<Comp v-slot="bar">{{ i }}</Comp>
</div>`,
true,
PatchFlags.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="bar">{{ bar }}</Comp>
</Comp>`,
false,
)
assertDynamicSlots(
`<Comp v-slot="foo">
<Comp v-slot="bar">{{ foo }}</Comp>
</Comp>`,
true,
PatchFlags.DYNAMIC_SLOTS,
)
// #2564
@ -532,14 +533,35 @@ describe('compiler: transform component slots', () => {
`<div v-for="i in list">
<Comp v-slot="bar"><button @click="fn(i)" /></Comp>
</div>`,
true,
PatchFlags.DYNAMIC_SLOTS,
)
assertDynamicSlots(
`<div v-for="i in list">
<Comp v-slot="bar"><button @click="fn()" /></Comp>
</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.
let hasDynamicSlots = context.scopes.vSlot > 0 || context.scopes.vFor > 0
// 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) {
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.