fix(runtime-core/scheduler): avoid duplicate updates of child component
This commit is contained in:
parent
778f3a5e88
commit
8a87074df0
@ -1,4 +1,9 @@
|
|||||||
import { queueJob, nextTick, queuePostFlushCb } from '../src/scheduler'
|
import {
|
||||||
|
queueJob,
|
||||||
|
nextTick,
|
||||||
|
queuePostFlushCb,
|
||||||
|
invalidateJob
|
||||||
|
} from '../src/scheduler'
|
||||||
|
|
||||||
describe('scheduler', () => {
|
describe('scheduler', () => {
|
||||||
it('nextTick', async () => {
|
it('nextTick', async () => {
|
||||||
@ -230,4 +235,23 @@ describe('scheduler', () => {
|
|||||||
expect(calls).toEqual(['job1', 'job2', 'cb1', 'cb2'])
|
expect(calls).toEqual(['job1', 'job2', 'cb1', 'cb2'])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('invalidateJob', async () => {
|
||||||
|
const calls: string[] = []
|
||||||
|
const job1 = () => {
|
||||||
|
calls.push('job1')
|
||||||
|
invalidateJob(job2)
|
||||||
|
job2()
|
||||||
|
}
|
||||||
|
const job2 = () => {
|
||||||
|
calls.push('job2')
|
||||||
|
}
|
||||||
|
// queue both jobs
|
||||||
|
queueJob(job1)
|
||||||
|
queueJob(job2)
|
||||||
|
expect(calls).toEqual([])
|
||||||
|
await nextTick()
|
||||||
|
// job2 should be called only once
|
||||||
|
expect(calls).toEqual(['job1', 'job2'])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -30,7 +30,12 @@ import {
|
|||||||
isFunction,
|
isFunction,
|
||||||
PatchFlags
|
PatchFlags
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import { queueJob, queuePostFlushCb, flushPostFlushCbs } from './scheduler'
|
import {
|
||||||
|
queueJob,
|
||||||
|
queuePostFlushCb,
|
||||||
|
flushPostFlushCbs,
|
||||||
|
invalidateJob
|
||||||
|
} from './scheduler'
|
||||||
import {
|
import {
|
||||||
effect,
|
effect,
|
||||||
stop,
|
stop,
|
||||||
@ -895,6 +900,9 @@ export function createRenderer<
|
|||||||
} else {
|
} else {
|
||||||
// normal update
|
// normal update
|
||||||
instance.next = n2
|
instance.next = n2
|
||||||
|
// in case the child component is also queued, remove it to avoid
|
||||||
|
// double updating the same child component in the same flush.
|
||||||
|
invalidateJob(instance.update)
|
||||||
// instance.update is the reactive effect runner.
|
// instance.update is the reactive effect runner.
|
||||||
instance.update()
|
instance.update()
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
|
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
|
||||||
import { isArray } from '@vue/shared'
|
import { isArray } from '@vue/shared'
|
||||||
|
|
||||||
const queue: Function[] = []
|
const queue: (Function | null)[] = []
|
||||||
const postFlushCbs: Function[] = []
|
const postFlushCbs: Function[] = []
|
||||||
const p = Promise.resolve()
|
const p = Promise.resolve()
|
||||||
|
|
||||||
@ -22,6 +22,13 @@ export function queueJob(job: () => void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function invalidateJob(job: () => void) {
|
||||||
|
const i = queue.indexOf(job)
|
||||||
|
if (i > -1) {
|
||||||
|
queue[i] = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function queuePostFlushCb(cb: Function | Function[]) {
|
export function queuePostFlushCb(cb: Function | Function[]) {
|
||||||
if (!isArray(cb)) {
|
if (!isArray(cb)) {
|
||||||
postFlushCbs.push(cb)
|
postFlushCbs.push(cb)
|
||||||
@ -64,6 +71,9 @@ function flushJobs(seen?: CountMap) {
|
|||||||
seen = seen || new Map()
|
seen = seen || new Map()
|
||||||
}
|
}
|
||||||
while ((job = queue.shift())) {
|
while ((job = queue.shift())) {
|
||||||
|
if (job === null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
checkRecursiveUpdates(seen!, job)
|
checkRecursiveUpdates(seen!, job)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user