Browse Source

fix: fix post watcher fire timing on nested app mounts

close #10005
Evan You 2 years ago
parent
commit
3c3561e720

+ 32 - 0
packages/runtime-core/__tests__/apiCreateApp.spec.ts

@@ -5,12 +5,15 @@ import {
   getCurrentInstance,
   getCurrentInstance,
   h,
   h,
   inject,
   inject,
+  nextTick,
   nodeOps,
   nodeOps,
+  onMounted,
   provide,
   provide,
   ref,
   ref,
   resolveComponent,
   resolveComponent,
   resolveDirective,
   resolveDirective,
   serializeInner,
   serializeInner,
+  watch,
   withDirectives,
   withDirectives,
 } from '@vue/runtime-test'
 } from '@vue/runtime-test'
 
 
@@ -551,6 +554,35 @@ describe('api: createApp', () => {
     ).not.toHaveBeenWarned()
     ).not.toHaveBeenWarned()
   })
   })
 
 
+  // #10005
+  test('flush order edge case on nested createApp', async () => {
+    const order: string[] = []
+    const App = defineComponent({
+      setup(props) {
+        const message = ref('m1')
+        watch(
+          message,
+          () => {
+            order.push('post watcher')
+          },
+          { flush: 'post' },
+        )
+        onMounted(() => {
+          message.value = 'm2'
+          createApp(() => '').mount(nodeOps.createElement('div'))
+        })
+        return () => {
+          order.push('render')
+          return h('div', [message.value])
+        }
+      },
+    })
+
+    createApp(App).mount(nodeOps.createElement('div'))
+    await nextTick()
+    expect(order).toMatchObject(['render', 'render', 'post watcher'])
+  })
+
   // config.compilerOptions is tested in packages/vue since it is only
   // config.compilerOptions is tested in packages/vue since it is only
   // supported in the full build.
   // supported in the full build.
 })
 })

+ 7 - 2
packages/runtime-core/src/renderer.ts

@@ -2348,6 +2348,7 @@ function baseCreateRenderer(
     return hostNextSibling((vnode.anchor || vnode.el)!)
     return hostNextSibling((vnode.anchor || vnode.el)!)
   }
   }
 
 
+  let isFlushing = false
   const render: RootRenderFunction = (vnode, container, namespace) => {
   const render: RootRenderFunction = (vnode, container, namespace) => {
     if (vnode == null) {
     if (vnode == null) {
       if (container._vnode) {
       if (container._vnode) {
@@ -2364,8 +2365,12 @@ function baseCreateRenderer(
         namespace,
         namespace,
       )
       )
     }
     }
-    flushPreFlushCbs()
-    flushPostFlushCbs()
+    if (!isFlushing) {
+      isFlushing = true
+      flushPreFlushCbs()
+      flushPostFlushCbs()
+      isFlushing = false
+    }
     container._vnode = vnode
     container._vnode = vnode
   }
   }