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,
slotsProperties.push(
createObjectProperty(
arg || createSimpleExpression('default', true),
- buildSlotFn(exp, children, loc)
+ buildSlotFn(exp, undefined, children, loc)
)
)
}
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(
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 ||
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
}
})
test('v-for slot', () => {
- expect(
- compile(`<foo>
- <template v-for="key in names" v-slot:[key]="{ msg }">{{ msg + key + bar }}</template>
- </foo>`).code
- ).toMatchInlineSnapshot(`
+ const { code } = compile(`<foo>
+ <template v-for="(key, index) in names" v-slot:[key]="{ msg }">{{ msg + key + index + bar }}</template>
+ </foo>`)
+ 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\\")
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 */)
]
}
})
// 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)
})
}
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`],
function createVNodeSlotBranch(
props: ExpressionNode | undefined,
+ vForExp: ExpressionNode | undefined,
children: TemplateChildNode[],
parentContext: TransformContext
): ReturnStatement {
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,
arg: undefined,
modifiers: [],
loc: locStub
+ },
+ {
+ type: NodeTypes.DIRECTIVE,
+ name: 'for',
+ exp: vForExp,
+ arg: undefined,
+ modifiers: [],
+ loc: locStub
}
],
children,
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
}
}
}