diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap index 2cd13bab0..e93d74fe2 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap @@ -1,5 +1,31 @@ // 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`] = ` "const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = Vue diff --git a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts index e0f44a064..aeef8f0e5 100644 --- a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts @@ -371,6 +371,39 @@ describe('compiler: transform component slots', () => { expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot() }) + test('Properly handle v-for and ref on template', () => { + const { root, slots } = parseWithSlots( + ` + + `, + { 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', () => { const { root, slots } = parseWithSlots( ` diff --git a/packages/compiler-core/src/transforms/vSlot.ts b/packages/compiler-core/src/transforms/vSlot.ts index 43296dcc9..f24b99d89 100644 --- a/packages/compiler-core/src/transforms/vSlot.ts +++ b/packages/compiler-core/src/transforms/vSlot.ts @@ -9,6 +9,7 @@ import { NodeTypes, type ObjectExpression, type Property, + type RenderSlotCall, type SlotsExpression, type SourceLocation, type TemplateChildNode, @@ -25,7 +26,9 @@ import { ErrorCodes, createCompilerError } from '../errors' import { assert, findDir, + findProp, hasScopeRef, + injectProp, isStaticExp, isTemplateNode, isVSlot, @@ -258,6 +261,8 @@ export function buildSlots( const parseResult = vFor.forParseResult if (parseResult) { finalizeForParseResult(parseResult, context) + // #8395 + injectRefFor(slotElement.children, context) // Render the dynamic slots as an array and add it to the createSlot() // args. The runtime knows how to handle it appropriately. dynamicSlots.push( @@ -419,3 +424,19 @@ function isNonWhitespaceContent(node: TemplateChildNode): boolean { ? !!node.content.trim() : 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) + } + } +}