]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-core/scheduler): avoid duplicate updates of child component
authorEvan You <yyx990803@gmail.com>
Mon, 10 Feb 2020 18:09:15 +0000 (13:09 -0500)
committerEvan You <yyx990803@gmail.com>
Mon, 10 Feb 2020 18:09:15 +0000 (13:09 -0500)
packages/runtime-core/__tests__/scheduler.spec.ts
packages/runtime-core/src/renderer.ts
packages/runtime-core/src/scheduler.ts

index 03a7abc1629fc4d648c3d5360b924972cea8a807..65535d679a0db3c2a424a73ac68bb86372c31db9 100644 (file)
@@ -1,4 +1,9 @@
-import { queueJob, nextTick, queuePostFlushCb } from '../src/scheduler'
+import {
+  queueJob,
+  nextTick,
+  queuePostFlushCb,
+  invalidateJob
+} from '../src/scheduler'
 
 describe('scheduler', () => {
   it('nextTick', async () => {
@@ -230,4 +235,23 @@ describe('scheduler', () => {
       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'])
+  })
 })
index cee334a8fccc099a4959504bafbb540749280ee9..e2f87c8f277d8b3241a9e7e1cbcba13271328ac4 100644 (file)
@@ -30,7 +30,12 @@ import {
   isFunction,
   PatchFlags
 } from '@vue/shared'
-import { queueJob, queuePostFlushCb, flushPostFlushCbs } from './scheduler'
+import {
+  queueJob,
+  queuePostFlushCb,
+  flushPostFlushCbs,
+  invalidateJob
+} from './scheduler'
 import {
   effect,
   stop,
@@ -895,6 +900,9 @@ export function createRenderer<
         } else {
           // normal update
           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()
         }
index 78d3d59cd1dcb2e2aca3d87a91e4ad6985f02c24..a13a01280e0cf96927683f4b0bcc74c6e673f2bf 100644 (file)
@@ -1,7 +1,7 @@
 import { ErrorCodes, callWithErrorHandling } from './errorHandling'
 import { isArray } from '@vue/shared'
 
-const queue: Function[] = []
+const queue: (Function | null)[] = []
 const postFlushCbs: Function[] = []
 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[]) {
   if (!isArray(cb)) {
     postFlushCbs.push(cb)
@@ -64,6 +71,9 @@ function flushJobs(seen?: CountMap) {
     seen = seen || new Map()
   }
   while ((job = queue.shift())) {
+    if (job === null) {
+      continue
+    }
     if (__DEV__) {
       checkRecursiveUpdates(seen!, job)
     }