]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-core): generate incremental keys for v-if/else-if/else chains (#1589)
authorHcySunYang <HcySunYang@outlook.com>
Wed, 15 Jul 2020 13:21:40 +0000 (21:21 +0800)
committerGitHub <noreply@github.com>
Wed, 15 Jul 2020 13:21:40 +0000 (09:21 -0400)
fix #1587

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

index 73184e0a432a9531295238a808e23fc17454f466..7a4b904137070866bd272cfb3e91fc3c26d019c8 100644 (file)
@@ -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
 
index 927e2d1d291456a9dea01b1f5f8e55978578788f..44433d84d8cb5c871b0dca74c9fb1b296b5e83e0 100644 (file)
@@ -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(
+        `<div v-if="ok"/><p v-if="orNot"/>`,
+        {},
+        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(
+        `<div v-if="ok"/><p v-else/><div v-if="another"/><p v-else-if="orNot"/><p v-else/>`,
+        {},
+        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 }
index fc6ca5e1f040ea8641831253516e190630b8c797..11165d417684863152f3fbe0e14adc814e102025 100644 (file)
@@ -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
           )
         }