From 64c7b2f9cedae676ec26a7a8da4c109bc88b48f1 Mon Sep 17 00:00:00 2001 From: HcySunYang Date: Wed, 15 Jul 2020 21:21:40 +0800 Subject: [PATCH] fix(compiler-core): generate incremental keys for v-if/else-if/else chains (#1589) fix #1587 --- .../transforms/__snapshots__/vIf.spec.ts.snap | 40 +++++++++++ .../__tests__/transforms/vIf.spec.ts | 71 ++++++++++++++++++- packages/compiler-core/src/transforms/vIf.ts | 17 ++++- 3 files changed, 123 insertions(+), 5 deletions(-) diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vIf.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vIf.spec.ts.snap index 73184e0a432..7a4b9041370 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/vIf.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vIf.spec.ts.snap @@ -14,6 +14,46 @@ return function render(_ctx, _cache) { }" `; +exports[`compiler: v-if codegen increasing key: v-if + v-else-if + v-else 1`] = ` +"const _Vue = Vue + +return function render(_ctx, _cache) { + with (_ctx) { + const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode, Fragment: _Fragment } = _Vue + + return (_openBlock(), _createBlock(_Fragment, null, [ + ok + ? (_openBlock(), _createBlock(\\"div\\", { key: 0 })) + : (_openBlock(), _createBlock(\\"p\\", { key: 1 })), + another + ? (_openBlock(), _createBlock(\\"div\\", { key: 2 })) + : orNot + ? (_openBlock(), _createBlock(\\"p\\", { key: 3 })) + : (_openBlock(), _createBlock(\\"p\\", { key: 4 })) + ], 64 /* STABLE_FRAGMENT */)) + } +}" +`; + +exports[`compiler: v-if codegen multiple v-if that are sibling nodes should have different keys 1`] = ` +"const _Vue = Vue + +return function render(_ctx, _cache) { + with (_ctx) { + const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode, Fragment: _Fragment } = _Vue + + return (_openBlock(), _createBlock(_Fragment, null, [ + ok + ? (_openBlock(), _createBlock(\\"div\\", { key: 0 })) + : _createCommentVNode(\\"v-if\\", true), + orNot + ? (_openBlock(), _createBlock(\\"p\\", { key: 1 })) + : _createCommentVNode(\\"v-if\\", true) + ], 64 /* STABLE_FRAGMENT */)) + } +}" +`; + exports[`compiler: v-if codegen template v-if 1`] = ` "const _Vue = Vue diff --git a/packages/compiler-core/__tests__/transforms/vIf.spec.ts b/packages/compiler-core/__tests__/transforms/vIf.spec.ts index 927e2d1d291..44433d84d8c 100644 --- a/packages/compiler-core/__tests__/transforms/vIf.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vIf.spec.ts @@ -29,7 +29,8 @@ import { createObjectMatcher } from '../testUtils' function parseWithIfTransform( template: string, options: CompilerOptions = {}, - returnIndex: number = 0 + returnIndex: number = 0, + childrenLen: number = 1 ) { const ast = parse(template, options) transform(ast, { @@ -37,8 +38,10 @@ function parseWithIfTransform( ...options }) if (!options.onError) { - expect(ast.children.length).toBe(1) - expect(ast.children[0].type).toBe(NodeTypes.IF) + expect(ast.children.length).toBe(childrenLen) + for (let i = 0; i < childrenLen; i++) { + expect(ast.children[i].type).toBe(NodeTypes.IF) + } } return { root: ast, @@ -459,6 +462,68 @@ describe('compiler: v-if', () => { expect(generate(root).code).toMatchSnapshot() }) + test('multiple v-if that are sibling nodes should have different keys', () => { + const { root } = parseWithIfTransform( + `

`, + {}, + 0 /* returnIndex, just give the default value */, + 2 /* childrenLen */ + ) + + const ifNode = root.children[0] as IfNode & { + codegenNode: IfConditionalExpression + } + expect(ifNode.codegenNode.consequent).toMatchObject({ + tag: `"div"`, + props: createObjectMatcher({ key: `[0]` }) + }) + const ifNode2 = root.children[1] as IfNode & { + codegenNode: IfConditionalExpression + } + expect(ifNode2.codegenNode.consequent).toMatchObject({ + tag: `"p"`, + props: createObjectMatcher({ key: `[1]` }) + }) + expect(generate(root).code).toMatchSnapshot() + }) + + test('increasing key: v-if + v-else-if + v-else', () => { + const { root } = parseWithIfTransform( + `

`, + {}, + 0 /* returnIndex, just give the default value */, + 2 /* childrenLen */ + ) + const ifNode = root.children[0] as IfNode & { + codegenNode: IfConditionalExpression + } + expect(ifNode.codegenNode.consequent).toMatchObject({ + tag: `"div"`, + props: createObjectMatcher({ key: `[0]` }) + }) + expect(ifNode.codegenNode.alternate).toMatchObject({ + tag: `"p"`, + props: createObjectMatcher({ key: `[1]` }) + }) + const ifNode2 = root.children[1] as IfNode & { + codegenNode: IfConditionalExpression + } + expect(ifNode2.codegenNode.consequent).toMatchObject({ + tag: `"div"`, + props: createObjectMatcher({ key: `[2]` }) + }) + const branch = ifNode2.codegenNode.alternate as IfConditionalExpression + expect(branch.consequent).toMatchObject({ + tag: `"p"`, + props: createObjectMatcher({ key: `[3]` }) + }) + expect(branch.alternate).toMatchObject({ + tag: `"p"`, + props: createObjectMatcher({ key: `[4]` }) + }) + expect(generate(root).code).toMatchSnapshot() + }) + test('key injection (only v-bind)', () => { const { node: { codegenNode } diff --git a/packages/compiler-core/src/transforms/vIf.ts b/packages/compiler-core/src/transforms/vIf.ts index fc6ca5e1f04..11165d41768 100644 --- a/packages/compiler-core/src/transforms/vIf.ts +++ b/packages/compiler-core/src/transforms/vIf.ts @@ -37,13 +37,26 @@ export const transformIf = createStructuralDirectiveTransform( /^(if|else|else-if)$/, (node, dir, context) => { return processIf(node, dir, context, (ifNode, branch, isRoot) => { + // #1587: We need to dynamically increment the key based on the current + // node's sibling nodes, since chained v-if/else branches are + // rendered at the same depth + const siblings = context.parent!.children + let i = siblings.indexOf(ifNode) + let key = 0 + while (i-- >= 0) { + const sibling = siblings[i] + if (sibling && sibling.type === NodeTypes.IF) { + key += sibling.branches.length + } + } + // Exit callback. Complete the codegenNode when all children have been // transformed. return () => { if (isRoot) { ifNode.codegenNode = createCodegenNodeForBranch( branch, - 0, + key, context ) as IfConditionalExpression } else { @@ -57,7 +70,7 @@ export const transformIf = createStructuralDirectiveTransform( } parentCondition.alternate = createCodegenNodeForBranch( branch, - ifNode.branches.length - 1, + key + ifNode.branches.length - 1, context ) }