Skip to content

Commit 64c7b2f

Browse files
authored
fix(compiler-core): generate incremental keys for v-if/else-if/else chains (#1589)
fix #1587
1 parent 46158b4 commit 64c7b2f

File tree

3 files changed

+123
-5
lines changed

3 files changed

+123
-5
lines changed

packages/compiler-core/__tests__/transforms/__snapshots__/vIf.spec.ts.snap

+40
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,46 @@ return function render(_ctx, _cache) {
1414
}"
1515
`;
1616

17+
exports[`compiler: v-if codegen increasing key: v-if + v-else-if + v-else 1`] = `
18+
"const _Vue = Vue
19+
20+
return function render(_ctx, _cache) {
21+
with (_ctx) {
22+
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode, Fragment: _Fragment } = _Vue
23+
24+
return (_openBlock(), _createBlock(_Fragment, null, [
25+
ok
26+
? (_openBlock(), _createBlock(\\"div\\", { key: 0 }))
27+
: (_openBlock(), _createBlock(\\"p\\", { key: 1 })),
28+
another
29+
? (_openBlock(), _createBlock(\\"div\\", { key: 2 }))
30+
: orNot
31+
? (_openBlock(), _createBlock(\\"p\\", { key: 3 }))
32+
: (_openBlock(), _createBlock(\\"p\\", { key: 4 }))
33+
], 64 /* STABLE_FRAGMENT */))
34+
}
35+
}"
36+
`;
37+
38+
exports[`compiler: v-if codegen multiple v-if that are sibling nodes should have different keys 1`] = `
39+
"const _Vue = Vue
40+
41+
return function render(_ctx, _cache) {
42+
with (_ctx) {
43+
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode, Fragment: _Fragment } = _Vue
44+
45+
return (_openBlock(), _createBlock(_Fragment, null, [
46+
ok
47+
? (_openBlock(), _createBlock(\\"div\\", { key: 0 }))
48+
: _createCommentVNode(\\"v-if\\", true),
49+
orNot
50+
? (_openBlock(), _createBlock(\\"p\\", { key: 1 }))
51+
: _createCommentVNode(\\"v-if\\", true)
52+
], 64 /* STABLE_FRAGMENT */))
53+
}
54+
}"
55+
`;
56+
1757
exports[`compiler: v-if codegen template v-if 1`] = `
1858
"const _Vue = Vue
1959

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

+68-3
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,19 @@ import { createObjectMatcher } from '../testUtils'
2929
function parseWithIfTransform(
3030
template: string,
3131
options: CompilerOptions = {},
32-
returnIndex: number = 0
32+
returnIndex: number = 0,
33+
childrenLen: number = 1
3334
) {
3435
const ast = parse(template, options)
3536
transform(ast, {
3637
nodeTransforms: [transformIf, transformSlotOutlet, transformElement],
3738
...options
3839
})
3940
if (!options.onError) {
40-
expect(ast.children.length).toBe(1)
41-
expect(ast.children[0].type).toBe(NodeTypes.IF)
41+
expect(ast.children.length).toBe(childrenLen)
42+
for (let i = 0; i < childrenLen; i++) {
43+
expect(ast.children[i].type).toBe(NodeTypes.IF)
44+
}
4245
}
4346
return {
4447
root: ast,
@@ -459,6 +462,68 @@ describe('compiler: v-if', () => {
459462
expect(generate(root).code).toMatchSnapshot()
460463
})
461464

465+
test('multiple v-if that are sibling nodes should have different keys', () => {
466+
const { root } = parseWithIfTransform(
467+
`<div v-if="ok"/><p v-if="orNot"/>`,
468+
{},
469+
0 /* returnIndex, just give the default value */,
470+
2 /* childrenLen */
471+
)
472+
473+
const ifNode = root.children[0] as IfNode & {
474+
codegenNode: IfConditionalExpression
475+
}
476+
expect(ifNode.codegenNode.consequent).toMatchObject({
477+
tag: `"div"`,
478+
props: createObjectMatcher({ key: `[0]` })
479+
})
480+
const ifNode2 = root.children[1] as IfNode & {
481+
codegenNode: IfConditionalExpression
482+
}
483+
expect(ifNode2.codegenNode.consequent).toMatchObject({
484+
tag: `"p"`,
485+
props: createObjectMatcher({ key: `[1]` })
486+
})
487+
expect(generate(root).code).toMatchSnapshot()
488+
})
489+
490+
test('increasing key: v-if + v-else-if + v-else', () => {
491+
const { root } = parseWithIfTransform(
492+
`<div v-if="ok"/><p v-else/><div v-if="another"/><p v-else-if="orNot"/><p v-else/>`,
493+
{},
494+
0 /* returnIndex, just give the default value */,
495+
2 /* childrenLen */
496+
)
497+
const ifNode = root.children[0] as IfNode & {
498+
codegenNode: IfConditionalExpression
499+
}
500+
expect(ifNode.codegenNode.consequent).toMatchObject({
501+
tag: `"div"`,
502+
props: createObjectMatcher({ key: `[0]` })
503+
})
504+
expect(ifNode.codegenNode.alternate).toMatchObject({
505+
tag: `"p"`,
506+
props: createObjectMatcher({ key: `[1]` })
507+
})
508+
const ifNode2 = root.children[1] as IfNode & {
509+
codegenNode: IfConditionalExpression
510+
}
511+
expect(ifNode2.codegenNode.consequent).toMatchObject({
512+
tag: `"div"`,
513+
props: createObjectMatcher({ key: `[2]` })
514+
})
515+
const branch = ifNode2.codegenNode.alternate as IfConditionalExpression
516+
expect(branch.consequent).toMatchObject({
517+
tag: `"p"`,
518+
props: createObjectMatcher({ key: `[3]` })
519+
})
520+
expect(branch.alternate).toMatchObject({
521+
tag: `"p"`,
522+
props: createObjectMatcher({ key: `[4]` })
523+
})
524+
expect(generate(root).code).toMatchSnapshot()
525+
})
526+
462527
test('key injection (only v-bind)', () => {
463528
const {
464529
node: { codegenNode }

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

+15-2
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,26 @@ export const transformIf = createStructuralDirectiveTransform(
3737
/^(if|else|else-if)$/,
3838
(node, dir, context) => {
3939
return processIf(node, dir, context, (ifNode, branch, isRoot) => {
40+
// #1587: We need to dynamically increment the key based on the current
41+
// node's sibling nodes, since chained v-if/else branches are
42+
// rendered at the same depth
43+
const siblings = context.parent!.children
44+
let i = siblings.indexOf(ifNode)
45+
let key = 0
46+
while (i-- >= 0) {
47+
const sibling = siblings[i]
48+
if (sibling && sibling.type === NodeTypes.IF) {
49+
key += sibling.branches.length
50+
}
51+
}
52+
4053
// Exit callback. Complete the codegenNode when all children have been
4154
// transformed.
4255
return () => {
4356
if (isRoot) {
4457
ifNode.codegenNode = createCodegenNodeForBranch(
4558
branch,
46-
0,
59+
key,
4760
context
4861
) as IfConditionalExpression
4962
} else {
@@ -57,7 +70,7 @@ export const transformIf = createStructuralDirectiveTransform(
5770
}
5871
parentCondition.alternate = createCodegenNodeForBranch(
5972
branch,
60-
ifNode.branches.length - 1,
73+
key + ifNode.branches.length - 1,
6174
context
6275
)
6376
}

0 commit comments

Comments
 (0)