fix(runtime-core/scheduler): avoid duplicate updates of child component

This commit is contained in:
Evan You 2020-02-10 13:09:15 -05:00
parent 778f3a5e88
commit 8a87074df0
3 changed files with 45 additions and 3 deletions

View File

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

View File

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

View File

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