Prechádzať zdrojové kódy

fix(teleport): handle deferred teleport update before mounted (#12168)

close #12161
edison 1 rok pred
rodič
commit
8bff142f99

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

@@ -87,6 +87,49 @@ describe('renderer: teleport', () => {
           `</div>`,
       )
     })
+
+    test('update before mounted with defer', async () => {
+      const root = document.createElement('div')
+      document.body.appendChild(root)
+
+      const show = ref(false)
+      const foo = ref('foo')
+      const Header = {
+        props: { foo: String },
+        setup(props: any) {
+          return () => h('div', props.foo)
+        },
+      }
+      const Footer = {
+        setup() {
+          foo.value = 'bar'
+          return () => h('div', 'Footer')
+        },
+      }
+      createDOMApp({
+        render() {
+          return show.value
+            ? [
+                h(
+                  Teleport,
+                  { to: '#targetId', defer: true },
+                  h(Header, { foo: foo.value }),
+                ),
+                h(Footer),
+                h('div', { id: 'targetId' }),
+              ]
+            : [h('div')]
+        },
+      }).mount(root)
+
+      expect(root.innerHTML).toMatchInlineSnapshot(`"<div></div>"`)
+
+      show.value = true
+      await nextTick()
+      expect(root.innerHTML).toMatchInlineSnapshot(
+        `"<!--teleport start--><!--teleport end--><div>Footer</div><div id="targetId"><div>bar</div></div>"`,
+      )
+    })
   })
 
   function runSharedTests(deferMode: boolean) {

+ 22 - 1
packages/runtime-core/src/components/Teleport.ts

@@ -164,11 +164,32 @@ export const TeleportImpl = {
       }
 
       if (isTeleportDeferred(n2.props)) {
-        queuePostRenderEffect(mountToTarget, parentSuspense)
+        queuePostRenderEffect(() => {
+          mountToTarget()
+          n2.el!.__isMounted = true
+        }, parentSuspense)
       } else {
         mountToTarget()
       }
     } else {
+      if (isTeleportDeferred(n2.props) && !n1.el!.__isMounted) {
+        queuePostRenderEffect(() => {
+          TeleportImpl.process(
+            n1,
+            n2,
+            container,
+            anchor,
+            parentComponent,
+            parentSuspense,
+            namespace,
+            slotScopeIds,
+            optimized,
+            internals,
+          )
+          delete n1.el!.__isMounted
+        }, parentSuspense)
+        return
+      }
       // update content
       n2.el = n1.el
       n2.targetStart = n1.targetStart