]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix: inherit `el` for static nodes inside keyed `template` fragment (#2089)
authorunderfin <likui6666666@gmail.com>
Mon, 14 Sep 2020 20:02:29 +0000 (04:02 +0800)
committerGitHub <noreply@github.com>
Mon, 14 Sep 2020 20:02:29 +0000 (16:02 -0400)
fix #2080

packages/runtime-core/__tests__/rendererFragment.spec.ts
packages/runtime-core/src/renderer.ts

index 9e1f87aec8e3dc3f8a309ef7d77b30a7d46af52c..93140f13f4efc697a3a8cdeee88137676b2af030 100644 (file)
@@ -10,9 +10,13 @@ import {
   dumpOps,
   NodeOpTypes,
   serializeInner,
-  createTextVNode
+  createTextVNode,
+  createBlock,
+  openBlock,
+  createCommentVNode
 } from '@vue/runtime-test'
 import { PatchFlags } from '@vue/shared'
+import { renderList } from '../src/helpers/renderList'
 
 describe('renderer: fragment', () => {
   it('should allow returning multiple component root nodes', () => {
@@ -269,4 +273,46 @@ describe('renderer: fragment', () => {
     render(null, root)
     expect(serializeInner(root)).toBe(``)
   })
+
+  // #2080
+  test('`template` keyed fragment w/ comment + hoisted node', () => {
+    const root = nodeOps.createElement('div')
+    const hoisted = h('span')
+
+    const renderFn = (items: string[]) => {
+      return (
+        openBlock(true),
+        createBlock(
+          Fragment,
+          null,
+          renderList(items, item => {
+            return (
+              openBlock(),
+              createBlock(
+                Fragment,
+                { key: item },
+                [
+                  createCommentVNode('comment'),
+                  hoisted,
+                  createVNode('div', null, item, PatchFlags.TEXT)
+                ],
+                PatchFlags.STABLE_FRAGMENT
+              )
+            )
+          }),
+          PatchFlags.KEYED_FRAGMENT
+        )
+      )
+    }
+
+    render(renderFn(['one', 'two']), root)
+    expect(serializeInner(root)).toBe(
+      `<!--comment--><span></span><div>one</div><!--comment--><span></span><div>two</div>`
+    )
+
+    render(renderFn(['two', 'one']), root)
+    expect(serializeInner(root)).toBe(
+      `<!--comment--><span></span><div>two</div><!--comment--><span></span><div>one</div>`
+    )
+  })
 })
index 670cd41f5c0cf7f0146d468a0dd2a7b9add8d48b..5246fcb1baa478c93a43b235fa44c13025a09163 100644 (file)
@@ -1153,8 +1153,10 @@ function baseCreateRenderer(
           parentSuspense,
           isSVG
         )
-        if (__DEV__ && parentComponent && parentComponent.type.__hmrId) {
-          traverseStaticChildren(n1, n2)
+        // #2080 if the stable fragment has a key, it's a <template v-for> that may
+        //  get moved around. Make sure all root level vnodes inherit el.
+        if (n2.key != null) {
+          traverseStaticChildren(n1, n2, true /* shallow */)
         }
       } else {
         // keyed / unkeyed, or manual fragments.
@@ -2166,9 +2168,12 @@ function baseCreateRenderer(
    * inside a block also inherit the DOM element from the previous tree so that
    * HMR updates (which are full updates) can retrieve the element for patching.
    *
-   * Dev only.
+   * #2080
+   * Inside keyed `template` fragment static children, if a fragment is moved,
+   * the children will always moved so that need inherit el form previous nodes
+   * to ensure correct moved position.
    */
-  const traverseStaticChildren = (n1: VNode, n2: VNode) => {
+  const traverseStaticChildren = (n1: VNode, n2: VNode, shallow = false) => {
     const ch1 = n1.children
     const ch2 = n2.children
     if (isArray(ch1) && isArray(ch2)) {
@@ -2181,7 +2186,10 @@ function baseCreateRenderer(
           if (c2.patchFlag <= 0 || c2.patchFlag === PatchFlags.HYDRATE_EVENTS) {
             c2.el = c1.el
           }
-          traverseStaticChildren(c1, c2)
+          if (!shallow) traverseStaticChildren(c1, c2)
+        }
+        if (__DEV__ && c2.type === Comment) {
+          c2.el = c1.el
         }
       }
     }