fix(watch): fix queueing multiple post watchers

fix #12664
This commit is contained in:
Evan You 2022-07-16 20:46:39 +08:00
parent 46ec648694
commit 25ffdb62d2
4 changed files with 46 additions and 2 deletions

View File

@ -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

View File

@ -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 = []

View File

@ -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

View File

@ -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)
})
})