diff --git a/packages/compiler-core/src/transforms/vSlot.ts b/packages/compiler-core/src/transforms/vSlot.ts
index c4416dd45f7..ffa90ea1171 100644
--- a/packages/compiler-core/src/transforms/vSlot.ts
+++ b/packages/compiler-core/src/transforms/vSlot.ts
@@ -100,11 +100,12 @@ export const trackVForSlotScopes: NodeTransform = (node, context) => {
export type SlotFnBuilder = (
slotProps: ExpressionNode | undefined,
+ vForExp: ExpressionNode | undefined,
slotChildren: TemplateChildNode[],
loc: SourceLocation
) => FunctionExpression
-const buildClientSlotFn: SlotFnBuilder = (props, children, loc) =>
+const buildClientSlotFn: SlotFnBuilder = (props, _vForExp, children, loc) =>
createFunctionExpression(
props,
children,
@@ -149,7 +150,7 @@ export function buildSlots(
slotsProperties.push(
createObjectProperty(
arg || createSimpleExpression('default', true),
- buildSlotFn(exp, children, loc)
+ buildSlotFn(exp, undefined, children, loc)
)
)
}
@@ -201,11 +202,17 @@ export function buildSlots(
hasDynamicSlots = true
}
- const slotFunction = buildSlotFn(slotProps, slotChildren, slotLoc)
+ const vFor = findDir(slotElement, 'for')
+ const slotFunction = buildSlotFn(
+ slotProps,
+ vFor?.exp,
+ slotChildren,
+ slotLoc
+ )
+
// check if this slot is conditional (v-if/v-for)
let vIf: DirectiveNode | undefined
let vElse: DirectiveNode | undefined
- let vFor: DirectiveNode | undefined
if ((vIf = findDir(slotElement, 'if'))) {
hasDynamicSlots = true
dynamicSlots.push(
@@ -257,7 +264,7 @@ export function buildSlots(
createCompilerError(ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, vElse.loc)
)
}
- } else if ((vFor = findDir(slotElement, 'for'))) {
+ } else if (vFor) {
hasDynamicSlots = true
const parseResult =
vFor.parseResult ||
@@ -306,7 +313,7 @@ export function buildSlots(
props: ExpressionNode | undefined,
children: TemplateChildNode[]
) => {
- const fn = buildSlotFn(props, children, loc)
+ const fn = buildSlotFn(props, undefined, children, loc)
if (__COMPAT__ && context.compatConfig) {
fn.isNonScopedSlot = true
}
diff --git a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts
index 9391c01e37e..a8ea08a5349 100644
--- a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts
+++ b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts
@@ -181,11 +181,14 @@ describe('ssr: components', () => {
})
test('v-for slot', () => {
- expect(
- compile(`
- {{ msg + key + bar }}
- `).code
- ).toMatchInlineSnapshot(`
+ const { code } = compile(`
+ {{ msg + key + index + bar }}
+ `)
+ expect(code).not.toMatch(`_ctx.msg`)
+ expect(code).not.toMatch(`_ctx.key`)
+ expect(code).not.toMatch(`_ctx.index`)
+ expect(code).toMatch(`_ctx.bar`)
+ expect(code).toMatchInlineSnapshot(`
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, renderList: _renderList, createSlots: _createSlots } = require(\\"vue\\")
const { ssrRenderComponent: _ssrRenderComponent, ssrInterpolate: _ssrInterpolate } = require(\\"vue/server-renderer\\")
@@ -193,15 +196,15 @@ describe('ssr: components', () => {
const _component_foo = _resolveComponent(\\"foo\\")
_push(_ssrRenderComponent(_component_foo, _attrs, _createSlots({ _: 2 /* DYNAMIC */ }, [
- _renderList(_ctx.names, (key) => {
+ _renderList(_ctx.names, (key, index) => {
return {
name: key,
fn: _withCtx(({ msg }, _push, _parent, _scopeId) => {
if (_push) {
- _push(\`\${_ssrInterpolate(msg + key + _ctx.bar)}\`)
+ _push(\`\${_ssrInterpolate(msg + key + index + _ctx.bar)}\`)
} else {
return [
- _createTextVNode(_toDisplayString(msg + _ctx.key + _ctx.bar), 1 /* TEXT */)
+ _createTextVNode(_toDisplayString(msg + key + index + _ctx.bar), 1 /* TEXT */)
]
}
})
diff --git a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts
index 93cae7db3c2..7a12cb29009 100644
--- a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts
+++ b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts
@@ -125,8 +125,10 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
// fallback in case the child is render-fn based). Store them in an array
// for later use.
if (clonedNode.children.length) {
- buildSlots(clonedNode, context, (props, children) => {
- vnodeBranches.push(createVNodeSlotBranch(props, children, context))
+ buildSlots(clonedNode, context, (props, vFor, children) => {
+ vnodeBranches.push(
+ createVNodeSlotBranch(props, vFor, children, context)
+ )
return createFunctionExpression(undefined)
})
}
@@ -150,7 +152,7 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
const wipEntries: WIPSlotEntry[] = []
wipMap.set(node, wipEntries)
- const buildSSRSlotFn: SlotFnBuilder = (props, children, loc) => {
+ const buildSSRSlotFn: SlotFnBuilder = (props, _vForExp, children, loc) => {
const param0 = (props && stringifyExpression(props)) || `_`
const fn = createFunctionExpression(
[param0, `_push`, `_parent`, `_scopeId`],
@@ -277,6 +279,7 @@ const vnodeDirectiveTransforms = {
function createVNodeSlotBranch(
props: ExpressionNode | undefined,
+ vForExp: ExpressionNode | undefined,
children: TemplateChildNode[],
parentContext: TransformContext
): ReturnStatement {
@@ -303,8 +306,8 @@ function createVNodeSlotBranch(
tag: 'template',
tagType: ElementTypes.TEMPLATE,
isSelfClosing: false,
- // important: provide v-slot="props" on the wrapper for proper
- // scope analysis
+ // important: provide v-slot="props" and v-for="exp" on the wrapper for
+ // proper scope analysis
props: [
{
type: NodeTypes.DIRECTIVE,
@@ -313,6 +316,14 @@ function createVNodeSlotBranch(
arg: undefined,
modifiers: [],
loc: locStub
+ },
+ {
+ type: NodeTypes.DIRECTIVE,
+ name: 'for',
+ exp: vForExp,
+ arg: undefined,
+ modifiers: [],
+ loc: locStub
}
],
children,
diff --git a/packages/compiler-ssr/src/transforms/ssrTransformSuspense.ts b/packages/compiler-ssr/src/transforms/ssrTransformSuspense.ts
index 207e9348eef..e7efbe1fb73 100644
--- a/packages/compiler-ssr/src/transforms/ssrTransformSuspense.ts
+++ b/packages/compiler-ssr/src/transforms/ssrTransformSuspense.ts
@@ -36,20 +36,24 @@ export function ssrTransformSuspense(
wipSlots: []
}
wipMap.set(node, wipEntry)
- wipEntry.slotsExp = buildSlots(node, context, (_props, children, loc) => {
- const fn = createFunctionExpression(
- [],
- undefined, // no return, assign body later
- true, // newline
- false, // suspense slots are not treated as normal slots
- loc
- )
- wipEntry.wipSlots.push({
- fn,
- children
- })
- return fn
- }).slots
+ wipEntry.slotsExp = buildSlots(
+ node,
+ context,
+ (_props, _vForExp, children, loc) => {
+ const fn = createFunctionExpression(
+ [],
+ undefined, // no return, assign body later
+ true, // newline
+ false, // suspense slots are not treated as normal slots
+ loc
+ )
+ wipEntry.wipSlots.push({
+ fn,
+ children
+ })
+ return fn
+ }
+ ).slots
}
}
}