This commit is contained in:
白雾三语 2025-06-30 14:01:15 +08:00 committed by GitHub
commit 8cbff687e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 80 additions and 0 deletions

View File

@ -1,5 +1,31 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`compiler: transform component slots > Properly handle v-for and ref on template 1`] = `
"const _Vue = Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode, resolveComponent: _resolveComponent, withCtx: _withCtx, renderList: _renderList, createSlots: _createSlots, openBlock: _openBlock, createBlock: _createBlock } = _Vue
const _component_Comp = _resolveComponent("Comp")
return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _: 2 /* DYNAMIC */ }, [
_renderList(_ctx.list, (name) => {
return {
name: name,
fn: _withCtx(() => [
_createElementVNode("p", {
ref_for: true,
ref: "slotItems"
}, _toDisplayString(name), 513 /* TEXT, NEED_PATCH */)
])
}
})
]), 1024 /* DYNAMIC_SLOTS */))
}
}"
`;
exports[`compiler: transform component slots > dynamically named slots 1`] = ` exports[`compiler: transform component slots > dynamically named slots 1`] = `
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = Vue "const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = Vue

View File

@ -371,6 +371,39 @@ describe('compiler: transform component slots', () => {
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot() expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
}) })
test('Properly handle v-for and ref on template', () => {
const { root, slots } = parseWithSlots(
`<Comp>
<template v-for="name in list" #[name]><p ref="slotItems">{{name}}</p></template>
</Comp>`,
{ prefixIdentifiers: true },
)
const spy = {
type: NodeTypes.JS_PROPERTY,
key: {
constType: 3,
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'ref_for',
isStatic: true,
},
value: {
constType: 0,
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'true',
isStatic: false,
},
}
const properties = (slots as any).arguments[1].elements[0].arguments[1]
.returns.properties[1].value.returns[0].codegenNode.props.properties[0]
expect(properties).toMatchObject(spy)
expect((root as any).children[0].codegenNode.patchFlag).toMatch(
PatchFlags.DYNAMIC_SLOTS + '',
)
expect(generate(root).code).toMatchSnapshot()
})
test('nested slots scoping', () => { test('nested slots scoping', () => {
const { root, slots } = parseWithSlots( const { root, slots } = parseWithSlots(
`<Comp> `<Comp>

View File

@ -9,6 +9,7 @@ import {
NodeTypes, NodeTypes,
type ObjectExpression, type ObjectExpression,
type Property, type Property,
type RenderSlotCall,
type SlotsExpression, type SlotsExpression,
type SourceLocation, type SourceLocation,
type TemplateChildNode, type TemplateChildNode,
@ -25,7 +26,9 @@ import { ErrorCodes, createCompilerError } from '../errors'
import { import {
assert, assert,
findDir, findDir,
findProp,
hasScopeRef, hasScopeRef,
injectProp,
isStaticExp, isStaticExp,
isTemplateNode, isTemplateNode,
isVSlot, isVSlot,
@ -258,6 +261,8 @@ export function buildSlots(
const parseResult = vFor.forParseResult const parseResult = vFor.forParseResult
if (parseResult) { if (parseResult) {
finalizeForParseResult(parseResult, context) finalizeForParseResult(parseResult, context)
// #8395
injectRefFor(slotElement.children, context)
// Render the dynamic slots as an array and add it to the createSlot() // Render the dynamic slots as an array and add it to the createSlot()
// args. The runtime knows how to handle it appropriately. // args. The runtime knows how to handle it appropriately.
dynamicSlots.push( dynamicSlots.push(
@ -419,3 +424,19 @@ function isNonWhitespaceContent(node: TemplateChildNode): boolean {
? !!node.content.trim() ? !!node.content.trim()
: isNonWhitespaceContent(node.content) : isNonWhitespaceContent(node.content)
} }
function injectRefFor(
children: TemplateChildNode[],
context: TransformContext,
) {
for (let i = 0; i < children.length; i++) {
const child = children[i]
if (child.type === NodeTypes.ELEMENT && findProp(child, 'ref')) {
const Property = createObjectProperty(
createSimpleExpression('ref_for', true),
createSimpleExpression('true'),
)
injectProp(child.codegenNode as RenderSlotCall, Property, context)
}
}
}