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

fix(runtime-core): avoid retaining el on cached text vnodes during static traversal (#14419)

revert #14134 
refix #14127
edison 2 месяцев назад
Родитель
Сommit
4ace79ac4c

+ 43 - 0
packages/runtime-core/__tests__/components/Teleport.spec.ts

@@ -292,6 +292,49 @@ describe('renderer: teleport', () => {
       expect(serializeInner(targetB)).toBe(`<div>teleported</div>`)
     })
 
+    test('move cached text nodes', async () => {
+      document.body.innerHTML = ''
+      const root = document.createElement('div')
+      document.body.appendChild(root)
+
+      const to = ref('#teleport01')
+      const disabled = ref(true)
+
+      const App = defineComponent({
+        setup() {
+          return { to, disabled, deferMode }
+        },
+        template: `  
+          <div id="teleport01">
+            <Teleport :to="to" :defer="deferMode" :disabled="disabled">
+              static text
+            </Teleport>
+          </div>
+          <div id="teleport02"></div>
+        `,
+      })
+
+      domRender(h(App), root)
+      await nextTick()
+
+      const target1 = root.querySelector('#teleport01') as HTMLElement
+      const target2 = root.querySelector('#teleport02') as HTMLElement
+      expect(target1.innerHTML).toBe(
+        '<!--teleport start--> static text <!--teleport end-->',
+      )
+      expect(target2.innerHTML).toBe('')
+
+      to.value = '#teleport02'
+      disabled.value = false
+
+      await nextTick()
+      expect(target1.innerHTML).toBe('<!--teleport start--><!--teleport end-->')
+      expect(target2.innerHTML).toContain('static text')
+
+      domRender(null, root)
+      root.remove()
+    })
+
     test('should update children', async () => {
       const target = nodeOps.createElement('div')
       const root = nodeOps.createElement('div')

+ 4 - 29
packages/runtime-core/src/renderer.ts

@@ -500,27 +500,7 @@ function baseCreateRenderer(
     } else {
       const el = (n2.el = n1.el!)
       if (n2.children !== n1.children) {
-        // We don't inherit el for cached text nodes in `traverseStaticChildren`
-        // to avoid retaining detached DOM nodes. However, the text node may be
-        // changed during HMR. In this case we need to replace the old text node
-        // with the new one.
-        if (
-          __DEV__ &&
-          isHmrUpdating &&
-          n2.patchFlag === PatchFlags.CACHED &&
-          '__elIndex' in n1
-        ) {
-          const childNodes = __TEST__
-            ? container.children
-            : container.childNodes
-          const newChild = hostCreateText(n2.children as string)
-          const oldChild =
-            childNodes[((n2 as any).__elIndex = (n1 as any).__elIndex)]
-          hostInsert(newChild, container, oldChild)
-          hostRemove(oldChild)
-        } else {
-          hostSetText(el, n2.children as string)
-        }
+        hostSetText(el, n2.children as string)
       }
     }
   }
@@ -2518,15 +2498,10 @@ export function traverseStaticChildren(
       // #6852 also inherit for text nodes
       if (c2.type === Text) {
         // avoid cached text nodes retaining detached dom nodes
-        if (c2.patchFlag !== PatchFlags.CACHED) {
-          c2.el = c1.el
-        } else {
-          // cache the child index for HMR updates
-          ;(c2 as any).__elIndex =
-            i +
-            // take fragment start anchor into account
-            (n1.type === Fragment ? 1 : 0)
+        if (c2.patchFlag === PatchFlags.CACHED) {
+          c2 = ch2[i] = cloneIfMounted(c2)
         }
+        c2.el = c1.el
       }
       // #2324 also inherit for comment nodes, but not placeholders (e.g. v-if which
       // would have received .el during block patch)