]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(teleport): handle deferred teleport updates before and after mount (#13350)
authoredison <daiwei521@126.com>
Tue, 20 May 2025 00:27:54 +0000 (08:27 +0800)
committerGitHub <noreply@github.com>
Tue, 20 May 2025 00:27:54 +0000 (08:27 +0800)
close #13349

packages/runtime-core/__tests__/components/Teleport.spec.ts
packages/runtime-core/src/components/Teleport.ts

index 4c35b1f2d79754c59c5edc48addf2023193ca311..69a1c4cb25fbfd8bd18881556d985aed98e65e5b 100644 (file)
@@ -16,6 +16,7 @@ import {
   render,
   serialize,
   serializeInner,
+  useModel,
   withDirectives,
 } from '@vue/runtime-test'
 import {
@@ -144,6 +145,62 @@ describe('renderer: teleport', () => {
         `"<!--teleport start--><!--teleport end--><div>Footer</div><div id="targetId"><div>bar</div></div>"`,
       )
     })
+
+    // #13349
+    test('handle deferred teleport updates before and after mount', async () => {
+      const root = document.createElement('div')
+      document.body.appendChild(root)
+
+      const show = ref(false)
+      const data2 = ref('2')
+      const data3 = ref('3')
+
+      const Comp = {
+        props: {
+          modelValue: {},
+          modelModifiers: {},
+        },
+        emits: ['update:modelValue'],
+        setup(props: any) {
+          const data2 = useModel(props, 'modelValue')
+          data2.value = '2+'
+          return () => h('span')
+        },
+      }
+
+      createDOMApp({
+        setup() {
+          setTimeout(() => (show.value = true), 5)
+          setTimeout(() => (data3.value = '3+'), 10)
+        },
+        render() {
+          return h(Fragment, null, [
+            h('span', { id: 'targetId001' }),
+            show.value
+              ? h(Fragment, null, [
+                  h(Teleport, { to: '#targetId001', defer: true }, [
+                    createTextVNode(String(data3.value)),
+                  ]),
+                  h(Comp, {
+                    modelValue: data2.value,
+                    'onUpdate:modelValue': (event: any) =>
+                      (data2.value = event),
+                  }),
+                ])
+              : createCommentVNode('v-if'),
+          ])
+        },
+      }).mount(root)
+
+      expect(root.innerHTML).toMatchInlineSnapshot(
+        `"<span id="targetId001"></span><!--v-if-->"`,
+      )
+
+      await new Promise(r => setTimeout(r, 10))
+      expect(root.innerHTML).toMatchInlineSnapshot(
+        `"<span id="targetId001">3+</span><!--teleport start--><!--teleport end--><span></span>"`,
+      )
+    })
   })
 
   function runSharedTests(deferMode: boolean) {
index fc2ee4c086f64eb199cfd1bbf15d5ecdb975b04b..c37356a7869bfa054eea5fa15e917171fb0266b2 100644 (file)
@@ -164,15 +164,16 @@ export const TeleportImpl = {
       }
 
       if (isTeleportDeferred(n2.props)) {
+        n2.el!.__isMounted = false
         queuePostRenderEffect(() => {
           mountToTarget()
-          n2.el!.__isMounted = true
+          delete n2.el!.__isMounted
         }, parentSuspense)
       } else {
         mountToTarget()
       }
     } else {
-      if (isTeleportDeferred(n2.props) && !n1.el!.__isMounted) {
+      if (isTeleportDeferred(n2.props) && n1.el!.__isMounted === false) {
         queuePostRenderEffect(() => {
           TeleportImpl.process(
             n1,
@@ -186,7 +187,6 @@ export const TeleportImpl = {
             optimized,
             internals,
           )
-          delete n1.el!.__isMounted
         }, parentSuspense)
         return
       }