Просмотр исходного кода

fix: inherit `el` for static nodes inside keyed `template` fragment (#2089)

fix #2080
underfin 5 лет назад
Родитель
Сommit
a32870a8f6

+ 47 - 1
packages/runtime-core/__tests__/rendererFragment.spec.ts

@@ -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>`
+    )
+  })
 })

+ 13 - 5
packages/runtime-core/src/renderer.ts

@@ -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
         }
       }
     }