]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor: ensure watcher callbacks are deferred
authorEvan You <yyx990803@gmail.com>
Thu, 20 Sep 2018 22:57:13 +0000 (18:57 -0400)
committerEvan You <yyx990803@gmail.com>
Thu, 20 Sep 2018 22:57:13 +0000 (18:57 -0400)
packages/core/src/component.ts
packages/core/src/componentWatch.ts
packages/core/src/createRenderer.ts
packages/renderer-dom/src/index.ts
packages/scheduler/src/index.ts

index 54cb0874f0e09040c8b910e31d4a0324946cc09c..865c992e8d1a1416b5e4ff977d9c72938ef52349 100644 (file)
@@ -44,7 +44,9 @@ export interface MountedComponent<D = Data, P = Data> extends Component {
   destroyed?(): void
 
   _updateHandle: Autorun
+  _queueJob: ((fn: () => void) => void)
   $forceUpdate: () => void
+  $nextTick: (fn: () => void) => Promise<any>
 
   _self: MountedComponent<D, P> // on proxies only
 }
@@ -68,6 +70,7 @@ export class Component {
   public $options: any
   public $proxy: any = null
   public $forceUpdate: (() => void) | null = null
+  public $nextTick: ((fn: () => void) => Promise<any>) | null = null
 
   public _rawData: Data | null = null
   public _computedGetters: Record<string, ComputedGetter> | null = null
@@ -76,6 +79,7 @@ export class Component {
   public _destroyed: boolean = false
   public _events: { [event: string]: Function[] | null } | null = null
   public _updateHandle: Autorun | null = null
+  public _queueJob: ((fn: () => void) => void) | null = null
   public _revokeProxy: () => void
   public _isVue: boolean = true
 
index a8387021dac718a1519ca1806bf7148d652e4cd3..c18ff9ad0b1ac9bd80a23ea229e9866802c07b63 100644 (file)
@@ -1,6 +1,6 @@
 import { MountedComponent } from './component'
 import { ComponentWatchOptions } from './componentOptions'
-import { autorun, stop, Autorun } from '@vue/observer'
+import { autorun, stop } from '@vue/observer'
 
 export function initializeWatch(
   instance: MountedComponent,
@@ -14,6 +14,7 @@ export function initializeWatch(
 }
 
 // TODO deep watch
+// TODO sync watch
 export function setupWatcher(
   instance: MountedComponent,
   keyOrFn: string | Function,
@@ -21,22 +22,32 @@ export function setupWatcher(
 ): () => void {
   const handles = instance._watchHandles || (instance._watchHandles = new Set())
   const proxy = instance.$proxy
+
   const rawGetter =
     typeof keyOrFn === 'string'
       ? () => proxy[keyOrFn]
       : () => keyOrFn.call(proxy)
+
   let oldValue: any
+
+  const applyCb = () => {
+    const newValue = runner()
+    if (newValue !== oldValue) {
+      cb(newValue, oldValue)
+      oldValue = newValue
+    }
+  }
+
   const runner = autorun(rawGetter, {
-    scheduler: (runner: Autorun) => {
-      const newValue = runner()
-      if (newValue !== oldValue) {
-        cb(newValue, oldValue)
-        oldValue = newValue
-      }
+    scheduler: () => {
+      // defer watch callback using the scheduler injected defer.
+      instance._queueJob(applyCb)
     }
   })
+
   oldValue = runner()
   handles.add(runner)
+
   return () => {
     stop(runner)
     handles.delete(runner)
index 5ad364015d884253601b20179063e8e9c999c24d..ca4962ca3f442fe68574d68a08adb7b2928f14c1 100644 (file)
@@ -56,7 +56,10 @@ interface PatchDataFunction {
 }
 
 interface RendererOptions {
-  queueJob: (fn: () => void, postFlushJob?: () => void) => void
+  scheduler: {
+    nextTick: (fn: () => void) => Promise<any>
+    queueJob: (fn: () => void, postFlushJob?: () => void) => void
+  }
   nodeOps: NodeOps
   patchData: PatchDataFunction
   teardownVNode?: (vnode: VNode) => void
@@ -68,7 +71,7 @@ interface RendererOptions {
 // renderer alongside an actual renderer.
 export function createRenderer(options: RendererOptions) {
   const {
-    queueJob,
+    scheduler: { queueJob, nextTick },
     nodeOps: {
       createElement: platformCreateElement,
       createText: platformCreateText,
@@ -1186,6 +1189,10 @@ export function createRenderer(options: RendererOptions) {
       (__COMPAT__ && (parentVNode.children as MountedComponent)) ||
       createComponentInstance(parentVNode, Component, parentComponent)
 
+    // renderer-injected scheduler methods
+    instance.$nextTick = nextTick
+    instance._queueJob = queueJob
+
     const queueUpdate = (instance.$forceUpdate = () => {
       queueJob(instance._updateHandle, flushHooks)
     })
index 423f9a1ea50b89f06f6be6629d106ddef89583e3..e1057c3f6a863db7bdaab9e680e64ff24ac1f090 100644 (file)
@@ -1,12 +1,15 @@
 import { createRenderer, VNode } from '@vue/core'
-import { queueJob } from '@vue/scheduler'
+import { queueJob, nextTick } from '@vue/scheduler'
 
 import { nodeOps } from './nodeOps'
 import { patchData } from './patchData'
 import { teardownVNode } from './teardownVNode'
 
 const { render: _render } = createRenderer({
-  queueJob,
+  scheduler: {
+    queueJob,
+    nextTick
+  },
   nodeOps,
   patchData,
   teardownVNode
index 8a30cb3aa6a8550785d1ca246907ea84c9fd8cdf..e99840be786f62aa1b74cbe97d455b898ea8fbf4 100644 (file)
@@ -17,18 +17,14 @@ export function queueJob(job: () => void, postFlushCb?: () => void) {
     }
   }
   if (postFlushCb) {
-    queuePostFlushCb(postFlushCb)
+    postFlushCbs.push(postFlushCb)
   }
   if (!flushing) {
     nextTick(flushJobs)
   }
 }
 
-export function queuePostFlushCb(cb: () => void) {
-  postFlushCbs.push(cb)
-}
-
-export function flushJobs() {
+function flushJobs() {
   flushing = true
   let job
   while ((job = queue.shift())) {