]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(watch): fix flush: pre watchers triggered synchronously in setup
authorEvan You <yyx990803@gmail.com>
Fri, 15 Apr 2022 10:08:44 +0000 (18:08 +0800)
committerEvan You <yyx990803@gmail.com>
Fri, 15 Apr 2022 10:08:44 +0000 (18:08 +0800)
fix #5721

packages/runtime-core/__tests__/apiWatch.spec.ts
packages/runtime-core/src/apiWatch.ts
packages/runtime-core/src/scheduler.ts

index a5517ab1d263350d65837a6714d88f3528049dd2..572ac11d163c009c3402af2d9111c1498df86b95 100644 (file)
@@ -18,7 +18,8 @@ import {
   h,
   createApp,
   watchPostEffect,
-  watchSyncEffect
+  watchSyncEffect,
+  onMounted
 } from '@vue/runtime-test'
 import {
   ITERATE_KEY,
@@ -581,6 +582,33 @@ describe('api: watch', () => {
     expect(calls).toEqual(['render', 'watcher 1', 'watcher 2', 'render'])
   })
 
+  // #5721
+  it('flush: pre triggered in component setup should be buffered and called before mounted', () => {
+    const count = ref(0)
+    const calls: string[] = []
+    const App = {
+      render() {},
+      setup() {
+        watch(
+          count,
+          () => {
+            calls.push('watch ' + count.value)
+          },
+          { flush: 'pre' }
+        )
+        onMounted(() => {
+          calls.push('mounted')
+        })
+        // mutate multiple times
+        count.value++
+        count.value++
+        count.value++
+      }
+    }
+    render(h(App), nodeOps.createElement('div'))
+    expect(calls).toMatchObject(['watch 3', 'mounted'])
+  })
+
   // #1852
   it('flush: post watcher should fire after template refs updated', async () => {
     const toggle = ref(false)
index 881a04a6645677674bfdf1d07dc5947fc269d8c7..84937eeae638fdea997bee9ce1f9a76b8a8e0384 100644 (file)
@@ -345,15 +345,7 @@ function doWatch(
     scheduler = () => queuePostRenderEffect(job, instance && instance.suspense)
   } else {
     // default: 'pre'
-    scheduler = () => {
-      if (!instance || instance.isMounted) {
-        queuePreFlushCb(job)
-      } else {
-        // with 'pre' option, the first call must happen before
-        // the component is mounted so it is called synchronously.
-        job()
-      }
-    }
+    scheduler = () => queuePreFlushCb(job)
   }
 
   const effect = new ReactiveEffect(getter, scheduler)
index fbf18deb1e5b7e2bc66c42dc952ea9a22f888ba0..2056d80e84709282c1bc1a1ced8b3fc177ff8918 100644 (file)
@@ -182,6 +182,8 @@ export function flushPreFlushCbs(
 }
 
 export function flushPostFlushCbs(seen?: CountMap) {
+  // flush any pre cbs queued during the flush (e.g. pre watchers)
+  flushPreFlushCbs()
   if (pendingPostFlushCbs.length) {
     const deduped = [...new Set(pendingPostFlushCbs)]
     pendingPostFlushCbs.length = 0