Skip to content

Commit be946ea

Browse files
committed
fix(compiler-core): fix prefixing for <template v-for> key expressions
fix #2085
1 parent a32870a commit be946ea

File tree

2 files changed

+77
-8
lines changed

2 files changed

+77
-8
lines changed

packages/compiler-core/__tests__/transforms/vFor.spec.ts

+55
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,61 @@ describe('compiler: v-for', () => {
582582
]
583583
})
584584
})
585+
586+
test('element v-for key expression prefixing', () => {
587+
const {
588+
node: { codegenNode }
589+
} = parseWithForTransform(
590+
'<div v-for="item in items" :key="itemKey(item)">test</div>',
591+
{ prefixIdentifiers: true }
592+
)
593+
const innerBlock = codegenNode.children.arguments[1].returns
594+
expect(innerBlock).toMatchObject({
595+
type: NodeTypes.VNODE_CALL,
596+
tag: `"div"`,
597+
props: createObjectMatcher({
598+
key: {
599+
type: NodeTypes.COMPOUND_EXPRESSION,
600+
children: [
601+
// should prefix outer scope references
602+
{ content: `_ctx.itemKey` },
603+
`(`,
604+
// should NOT prefix in scope variables
605+
{ content: `item` },
606+
`)`
607+
]
608+
}
609+
})
610+
})
611+
})
612+
613+
// #2085
614+
test('template v-for key expression prefixing', () => {
615+
const {
616+
node: { codegenNode }
617+
} = parseWithForTransform(
618+
'<template v-for="item in items" :key="itemKey(item)">test</template>',
619+
{ prefixIdentifiers: true }
620+
)
621+
const innerBlock = codegenNode.children.arguments[1].returns
622+
expect(innerBlock).toMatchObject({
623+
type: NodeTypes.VNODE_CALL,
624+
tag: FRAGMENT,
625+
props: createObjectMatcher({
626+
key: {
627+
type: NodeTypes.COMPOUND_EXPRESSION,
628+
children: [
629+
// should prefix outer scope references
630+
{ content: `_ctx.itemKey` },
631+
`(`,
632+
// should NOT prefix in scope variables
633+
{ content: `item` },
634+
`)`
635+
]
636+
}
637+
})
638+
})
639+
})
585640
})
586641

587642
describe('codegen', () => {

packages/compiler-core/src/transforms/vFor.ts

+22-8
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,27 @@ export const transformFor = createStructuralDirectiveTransform(
5454
forNode.source
5555
]) as ForRenderListExpression
5656
const keyProp = findProp(node, `key`)
57+
const keyProperty = keyProp
58+
? createObjectProperty(
59+
`key`,
60+
keyProp.type === NodeTypes.ATTRIBUTE
61+
? createSimpleExpression(keyProp.value!.content, true)
62+
: keyProp.exp!
63+
)
64+
: null
65+
66+
if (!__BROWSER__ && context.prefixIdentifiers && keyProperty) {
67+
// #2085 process :key expression needs to be processed in order for it
68+
// to behave consistently for <template v-for> and <div v-for>.
69+
// In the case of `<template v-for>`, the node is discarded and never
70+
// traversed so its key expression won't be processed by the normal
71+
// transforms.
72+
keyProperty.value = processExpression(
73+
keyProperty.value as SimpleExpressionNode,
74+
context
75+
)
76+
}
77+
5778
const isStableFragment =
5879
forNode.source.type === NodeTypes.SIMPLE_EXPRESSION &&
5980
forNode.source.isConstant
@@ -108,14 +129,7 @@ export const transformFor = createStructuralDirectiveTransform(
108129
isSlotOutlet(node.children[0])
109130
? (node.children[0] as SlotOutletNode) // api-extractor somehow fails to infer this
110131
: null
111-
const keyProperty = keyProp
112-
? createObjectProperty(
113-
`key`,
114-
keyProp.type === NodeTypes.ATTRIBUTE
115-
? createSimpleExpression(keyProp.value!.content, true)
116-
: keyProp.exp!
117-
)
118-
: null
132+
119133
if (slotOutlet) {
120134
// <slot v-for="..."> or <template v-for="..."><slot/></template>
121135
childBlock = slotOutlet.codegenNode as RenderSlotCall

0 commit comments

Comments
 (0)