Skip to content

Commit

Permalink
fix(watch): fix queueing multiple post watchers
Browse files Browse the repository at this point in the history
fix #12664
  • Loading branch information
yyx990803 committed Jul 16, 2022
1 parent 46ec648 commit 25ffdb6
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 2 deletions.
11 changes: 10 additions & 1 deletion src/core/observer/scheduler.ts
Expand Up @@ -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.
*/
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/core/observer/watcher.ts
Expand Up @@ -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
Expand Down Expand Up @@ -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 = []
Expand Down
2 changes: 1 addition & 1 deletion src/v3/apiWatch.ts
Expand Up @@ -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
Expand Down
33 changes: 33 additions & 0 deletions test/unit/features/v3/apiWatch.spec.ts
Expand Up @@ -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)
})
})

0 comments on commit 25ffdb6

Please sign in to comment.