فهرست منبع

fix(watch): fix queueing multiple post watchers

fix #12664
Evan You 3 سال پیش
والد
کامیت
25ffdb62d2
4فایلهای تغییر یافته به همراه46 افزوده شده و 2 حذف شده
  1. 10 1
      src/core/observer/scheduler.ts
  2. 2 0
      src/core/observer/watcher.ts
  3. 1 1
      src/v3/apiWatch.ts
  4. 33 0
      test/unit/features/v3/apiWatch.spec.ts

+ 10 - 1
src/core/observer/scheduler.ts

@@ -59,6 +59,15 @@ if (inBrowser && !isIE) {
   }
 }
 
+const sortCompareFn = (a: Watcher, b: Watcher): number => {
+  if (a.post) {
+    if (!b.post) return 1
+  } else if (b.post) {
+    return -1
+  }
+  return a.id - b.id
+}
+
 /**
  * Flush both queues and run the watchers.
  */
@@ -75,7 +84,7 @@ function flushSchedulerQueue() {
   //    user watchers are created before the render watcher)
   // 3. If a component is destroyed during a parent component's watcher run,
   //    its watchers can be skipped.
-  queue.sort((a, b) => a.id - b.id)
+  queue.sort(sortCompareFn)
 
   // do not cache length because more watchers might be pushed
   // as we run existing watchers

+ 2 - 0
src/core/observer/watcher.ts

@@ -58,6 +58,7 @@ export default class Watcher implements DepTarget {
   noRecurse?: boolean
   getter: Function
   value: any
+  post: boolean
 
   // dev only
   onTrack?: ((event: DebuggerEvent) => void) | undefined
@@ -93,6 +94,7 @@ export default class Watcher implements DepTarget {
     this.cb = cb
     this.id = ++uid // uid for batching
     this.active = true
+    this.post = false
     this.dirty = this.lazy // for lazy watchers
     this.deps = []
     this.newDeps = []

+ 1 - 1
src/v3/apiWatch.ts

@@ -313,7 +313,7 @@ function doWatch(
   if (flush === 'sync') {
     watcher.update = watcher.run
   } else if (flush === 'post') {
-    watcher.id = Infinity
+    watcher.post = true
     watcher.update = () => queueWatcher(watcher)
   } else {
     // pre

+ 33 - 0
test/unit/features/v3/apiWatch.spec.ts

@@ -1167,4 +1167,37 @@ describe('api: watch', () => {
     set(r.value, 'foo', 1)
     expect(spy).not.toHaveBeenCalled()
   })
+
+  // #12664
+  it('queueing multiple flush: post watchers', async () => {
+    const parentSpy = vi.fn()
+    const childSpy = vi.fn()
+
+    const Child = {
+      setup() {
+        const el = ref()
+        watch(el, childSpy, { flush: 'post' })
+        return { el }
+      },
+      template: `<div><span ref="el">hello child</span></div>`
+    }
+    const App = {
+      components: { Child },
+      setup() {
+        const el = ref()
+        watch(el, parentSpy, { flush: 'post' })
+        return { el }
+      },
+      template: `<div><Child /><span ref="el">hello app1</span></div>`
+    }
+
+    const container = document.createElement('div')
+    const root = document.createElement('div')
+    container.appendChild(root)
+    new Vue(App).$mount(root)
+
+    await nextTick()
+    expect(parentSpy).toHaveBeenCalledTimes(1)
+    expect(childSpy).toHaveBeenCalledTimes(1)
+  })
 })