Skip to content

Commit d4c17fb

Browse files
committed
fix(watch): pre-flush watcher watching props should trigger before component update
fix #1763
1 parent b10bc28 commit d4c17fb

File tree

3 files changed

+58
-1
lines changed

3 files changed

+58
-1
lines changed

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

+40
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,46 @@ describe('api: watch', () => {
432432
expect(cb).toHaveBeenCalledTimes(1)
433433
})
434434

435+
// #1763
436+
it('flush: pre watcher watching props should fire before child update', async () => {
437+
const a = ref(0)
438+
const b = ref(0)
439+
const calls: string[] = []
440+
441+
const Comp = {
442+
props: ['a', 'b'],
443+
setup(props: any) {
444+
watch(
445+
() => props.a + props.b,
446+
() => {
447+
calls.push('watcher')
448+
},
449+
{ flush: 'pre' }
450+
)
451+
return () => {
452+
calls.push('render')
453+
}
454+
}
455+
}
456+
457+
const App = {
458+
render() {
459+
return h(Comp, { a: a.value, b: b.value })
460+
}
461+
}
462+
463+
render(h(App), nodeOps.createElement('div'))
464+
expect(calls).toEqual(['render'])
465+
466+
// both props are updated
467+
// should trigger pre-flush watcher first and only once
468+
// then trigger child render
469+
a.value++
470+
b.value++
471+
await nextTick()
472+
expect(calls).toEqual(['render', 'watcher', 'render'])
473+
})
474+
435475
it('deep', async () => {
436476
const state = reactive({
437477
nested: {

packages/runtime-core/src/renderer.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ import {
4040
queueJob,
4141
queuePostFlushCb,
4242
flushPostFlushCbs,
43-
invalidateJob
43+
invalidateJob,
44+
runPreflushJobs
4445
} from './scheduler'
4546
import { effect, stop, ReactiveEffectOptions, isRef } from '@vue/reactivity'
4647
import { updateProps } from './componentProps'
@@ -1429,6 +1430,7 @@ function baseCreateRenderer(
14291430
instance.next = null
14301431
updateProps(instance, nextVNode.props, prevProps, optimized)
14311432
updateSlots(instance, nextVNode.children)
1433+
runPreflushJobs()
14321434
}
14331435

14341436
const patchChildren: PatchChildrenFn = (

packages/runtime-core/src/scheduler.ts

+15
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ let isFlushPending = false
2626
let flushIndex = 0
2727
let pendingPostFlushCbs: Function[] | null = null
2828
let pendingPostFlushIndex = 0
29+
let hasPendingPreFlushJobs = false
2930

3031
const RECURSION_LIMIT = 100
3132
type CountMap = Map<SchedulerJob | Function, number>
@@ -47,6 +48,7 @@ export function queueJob(job: SchedulerJob) {
4748
!queue.includes(job, job.cb ? flushIndex + 1 : flushIndex)
4849
) {
4950
queue.push(job)
51+
if ((job.id as number) < 0) hasPendingPreFlushJobs = true
5052
queueFlush()
5153
}
5254
}
@@ -58,6 +60,19 @@ export function invalidateJob(job: SchedulerJob) {
5860
}
5961
}
6062

63+
export function runPreflushJobs() {
64+
if (hasPendingPreFlushJobs) {
65+
hasPendingPreFlushJobs = false
66+
for (let job, i = queue.length - 1; i > flushIndex; i--) {
67+
job = queue[i]
68+
if (job && (job.id as number) < 0) {
69+
job()
70+
queue[i] = null
71+
}
72+
}
73+
}
74+
}
75+
6176
export function queuePostFlushCb(cb: Function | Function[]) {
6277
if (!isArray(cb)) {
6378
if (

0 commit comments

Comments
 (0)