expect(cb).toHaveBeenCalledTimes(1)
})
+ // #1763
+ it('flush: pre watcher watching props should fire before child update', async () => {
+ const a = ref(0)
+ const b = ref(0)
+ const calls: string[] = []
+
+ const Comp = {
+ props: ['a', 'b'],
+ setup(props: any) {
+ watch(
+ () => props.a + props.b,
+ () => {
+ calls.push('watcher')
+ },
+ { flush: 'pre' }
+ )
+ return () => {
+ calls.push('render')
+ }
+ }
+ }
+
+ const App = {
+ render() {
+ return h(Comp, { a: a.value, b: b.value })
+ }
+ }
+
+ render(h(App), nodeOps.createElement('div'))
+ expect(calls).toEqual(['render'])
+
+ // both props are updated
+ // should trigger pre-flush watcher first and only once
+ // then trigger child render
+ a.value++
+ b.value++
+ await nextTick()
+ expect(calls).toEqual(['render', 'watcher', 'render'])
+ })
+
it('deep', async () => {
const state = reactive({
nested: {
queueJob,
queuePostFlushCb,
flushPostFlushCbs,
- invalidateJob
+ invalidateJob,
+ runPreflushJobs
} from './scheduler'
import { effect, stop, ReactiveEffectOptions, isRef } from '@vue/reactivity'
import { updateProps } from './componentProps'
instance.next = null
updateProps(instance, nextVNode.props, prevProps, optimized)
updateSlots(instance, nextVNode.children)
+ runPreflushJobs()
}
const patchChildren: PatchChildrenFn = (
let flushIndex = 0
let pendingPostFlushCbs: Function[] | null = null
let pendingPostFlushIndex = 0
+let hasPendingPreFlushJobs = false
const RECURSION_LIMIT = 100
type CountMap = Map<SchedulerJob | Function, number>
!queue.includes(job, job.cb ? flushIndex + 1 : flushIndex)
) {
queue.push(job)
+ if ((job.id as number) < 0) hasPendingPreFlushJobs = true
queueFlush()
}
}
}
}
+export function runPreflushJobs() {
+ if (hasPendingPreFlushJobs) {
+ hasPendingPreFlushJobs = false
+ for (let job, i = queue.length - 1; i > flushIndex; i--) {
+ job = queue[i]
+ if (job && (job.id as number) < 0) {
+ job()
+ queue[i] = null
+ }
+ }
+ }
+}
+
export function queuePostFlushCb(cb: Function | Function[]) {
if (!isArray(cb)) {
if (