]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-core): use correct container for moving `Teleport` content (#1703)
authorunderfin <2218301630@qq.com>
Tue, 28 Jul 2020 15:23:39 +0000 (23:23 +0800)
committerGitHub <noreply@github.com>
Tue, 28 Jul 2020 15:23:39 +0000 (11:23 -0400)
packages/runtime-core/__tests__/components/Teleport.spec.ts
packages/runtime-core/src/components/Teleport.ts
packages/runtime-core/src/renderer.ts

index 99bea073072da0ccc52bd94a6c623acf64511388..eaa6e227ee37c1f8d4d43b524218e785623dba6a 100644 (file)
@@ -6,9 +6,11 @@ import {
   Teleport,
   Text,
   ref,
-  nextTick
+  nextTick,
+  markRaw
 } from '@vue/runtime-test'
 import { createVNode, Fragment } from '../../src/vnode'
+import { compile } from 'vue'
 
 describe('renderer: teleport', () => {
   test('should work', () => {
@@ -299,4 +301,49 @@ describe('renderer: teleport', () => {
     )
     expect(serializeInner(target)).toBe('')
   })
+
+  test('should work with block tree', async () => {
+    const target = nodeOps.createElement('div')
+    const root = nodeOps.createElement('div')
+    const disabled = ref(false)
+
+    const App = {
+      setup() {
+        return {
+          target: markRaw(target),
+          disabled
+        }
+      },
+      render: compile(`
+      <teleport :to="target" :disabled="disabled">
+        <div>teleported</div><span>{{ disabled }}</span>
+      </teleport>
+      <div>root</div>
+      `)
+    }
+    render(h(App), root)
+    expect(serializeInner(root)).toMatchInlineSnapshot(
+      `"<!--teleport start--><!--teleport end--><div>root</div>"`
+    )
+    expect(serializeInner(target)).toMatchInlineSnapshot(
+      `"<div>teleported</div><span>false</span>"`
+    )
+
+    disabled.value = true
+    await nextTick()
+    expect(serializeInner(root)).toMatchInlineSnapshot(
+      `"<!--teleport start--><div>teleported</div><span>true</span><!--teleport end--><div>root</div>"`
+    )
+    expect(serializeInner(target)).toBe(``)
+
+    // toggle back
+    disabled.value = false
+    await nextTick()
+    expect(serializeInner(root)).toMatchInlineSnapshot(
+      `"<!--teleport start--><!--teleport end--><div>root</div>"`
+    )
+    expect(serializeInner(target)).toMatchInlineSnapshot(
+      `"<div>teleported</div><span>false</span>"`
+    )
+  })
 })
index a4fdd4b505fd5f157eeda92f4d04d540f1dfd4ba..8c588df128b3a99aa501773da79e5e6ed913364d 100644 (file)
@@ -139,6 +139,13 @@ export const TeleportImpl = {
           parentSuspense,
           isSVG
         )
+        if (n2.patchFlag > 0 && n2.shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
+          const oldChildren = n1.children as VNode[]
+          const children = n2.children as VNode[]
+          for (let i = 0; i < children.length; i++) {
+            children[i].el = oldChildren[i].el
+          }
+        }
       } else if (!optimized) {
         patchChildren(
           n1,
index 09f2ab242b4cb2493faf7d2fb2c5bf8aebd05358..df26f87ac7174ecb95e5157bdfbee126d19235b9 100644 (file)
@@ -960,7 +960,8 @@ function baseCreateRenderer(
         // which also requires the correct parent container
         !isSameVNodeType(oldVNode, newVNode) ||
         // - In the case of a component, it could contain anything.
-        oldVNode.shapeFlag & ShapeFlags.COMPONENT
+        oldVNode.shapeFlag & ShapeFlags.COMPONENT ||
+        oldVNode.shapeFlag & ShapeFlags.TELEPORT
           ? hostParentNode(oldVNode.el!)!
           : // In other cases, the parent container is not actually used so we
             // just pass the block element here to avoid a DOM parentNode call.