Skip to content

Commit 29425df

Browse files
authored
fix(compiler-core): fix :key shorthand on v-for (#10942)
close #10882 close #10939
1 parent f94568b commit 29425df

File tree

3 files changed

+58
-11
lines changed

3 files changed

+58
-11
lines changed

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

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
import { ErrorCodes } from '../../src/errors'
1919
import { type CompilerOptions, generate } from '../../src'
2020
import { FRAGMENT, RENDER_LIST, RENDER_SLOT } from '../../src/runtimeHelpers'
21-
import { PatchFlags } from '@vue/shared'
21+
import { PatchFlagNames, PatchFlags } from '@vue/shared'
2222
import { createObjectMatcher, genFlagText } from '../testUtils'
2323

2424
export function parseWithForTransform(
@@ -1043,5 +1043,33 @@ describe('compiler: v-for', () => {
10431043
})
10441044
expect(generate(root).code).toMatchSnapshot()
10451045
})
1046+
1047+
test('template v-for key w/ :key shorthand on div', () => {
1048+
const {
1049+
node: { codegenNode },
1050+
} = parseWithForTransform('<div v-for="key in keys" :key>test</div>')
1051+
expect(codegenNode.patchFlag).toBe(
1052+
`${PatchFlags.KEYED_FRAGMENT} /* ${PatchFlagNames[PatchFlags.KEYED_FRAGMENT]} */`,
1053+
)
1054+
})
1055+
1056+
test('template v-for key w/ :key shorthand on template injected to the child', () => {
1057+
const {
1058+
node: { codegenNode },
1059+
} = parseWithForTransform(
1060+
'<template v-for="key in keys" :key><div>test</div></template>',
1061+
)
1062+
expect(assertSharedCodegen(codegenNode, true)).toMatchObject({
1063+
source: { content: `keys` },
1064+
params: [{ content: `key` }],
1065+
innerVNodeCall: {
1066+
type: NodeTypes.VNODE_CALL,
1067+
tag: `"div"`,
1068+
props: createObjectMatcher({
1069+
key: '[key]',
1070+
}),
1071+
},
1072+
})
1073+
})
10461074
})
10471075
})

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

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import type { DirectiveTransform } from '../transform'
1+
import type { DirectiveTransform, TransformContext } from '../transform'
22
import {
3+
type DirectiveNode,
34
type ExpressionNode,
45
NodeTypes,
56
type SimpleExpressionNode,
@@ -56,11 +57,8 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => {
5657
}
5758
}
5859

59-
const propName = camelize((arg as SimpleExpressionNode).content)
60-
exp = dir.exp = createSimpleExpression(propName, false, arg.loc)
61-
if (!__BROWSER__) {
62-
exp = dir.exp = processExpression(exp, context)
63-
}
60+
transformBindShorthand(dir, context)
61+
exp = dir.exp!
6462
}
6563

6664
if (arg.type !== NodeTypes.SIMPLE_EXPRESSION) {
@@ -98,6 +96,19 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => {
9896
}
9997
}
10098

99+
export const transformBindShorthand = (
100+
dir: DirectiveNode,
101+
context: TransformContext,
102+
) => {
103+
const arg = dir.arg!
104+
105+
const propName = camelize((arg as SimpleExpressionNode).content)
106+
dir.exp = createSimpleExpression(propName, false, arg.loc)
107+
if (!__BROWSER__) {
108+
dir.exp = processExpression(dir.exp, context)
109+
}
110+
}
111+
101112
const injectPrefix = (arg: ExpressionNode, prefix: string) => {
102113
if (arg.type === NodeTypes.SIMPLE_EXPRESSION) {
103114
if (arg.isStatic) {

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
import { processExpression } from './transformExpression'
4848
import { validateBrowserExpression } from '../validateExpression'
4949
import { PatchFlagNames, PatchFlags } from '@vue/shared'
50+
import { transformBindShorthand } from './vBind'
5051

5152
export const transformFor = createStructuralDirectiveTransform(
5253
'for',
@@ -60,13 +61,20 @@ export const transformFor = createStructuralDirectiveTransform(
6061
]) as ForRenderListExpression
6162
const isTemplate = isTemplateNode(node)
6263
const memo = findDir(node, 'memo')
63-
const keyProp = findProp(node, `key`)
64+
const keyProp = findProp(node, `key`, false, true)
65+
if (keyProp && keyProp.type === NodeTypes.DIRECTIVE && !keyProp.exp) {
66+
// resolve :key shorthand #10882
67+
transformBindShorthand(keyProp, context)
68+
}
6469
const keyExp =
6570
keyProp &&
6671
(keyProp.type === NodeTypes.ATTRIBUTE
67-
? createSimpleExpression(keyProp.value!.content, true)
68-
: keyProp.exp!)
69-
const keyProperty = keyProp ? createObjectProperty(`key`, keyExp!) : null
72+
? keyProp.value
73+
? createSimpleExpression(keyProp.value.content, true)
74+
: undefined
75+
: keyProp.exp)
76+
const keyProperty =
77+
keyProp && keyExp ? createObjectProperty(`key`, keyExp) : null
7078

7179
if (!__BROWSER__ && isTemplate) {
7280
// #2085 / #5288 process :key and v-memo expressions need to be

0 commit comments

Comments
 (0)