refactor: ensure watcher callbacks are deferred
This commit is contained in:
parent
9b50a5abb9
commit
bf38fea313
@ -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
|
||||
|
||||
|
@ -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 runner = autorun(rawGetter, {
|
||||
scheduler: (runner: Autorun) => {
|
||||
|
||||
const applyCb = () => {
|
||||
const newValue = runner()
|
||||
if (newValue !== oldValue) {
|
||||
cb(newValue, oldValue)
|
||||
oldValue = newValue
|
||||
}
|
||||
}
|
||||
|
||||
const runner = autorun(rawGetter, {
|
||||
scheduler: () => {
|
||||
// defer watch callback using the scheduler injected defer.
|
||||
instance._queueJob(applyCb)
|
||||
}
|
||||
})
|
||||
|
||||
oldValue = runner()
|
||||
handles.add(runner)
|
||||
|
||||
return () => {
|
||||
stop(runner)
|
||||
handles.delete(runner)
|
||||
|
@ -56,7 +56,10 @@ interface PatchDataFunction {
|
||||
}
|
||||
|
||||
interface RendererOptions {
|
||||
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)
|
||||
})
|
||||
|
@ -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({
|
||||
scheduler: {
|
||||
queueJob,
|
||||
nextTick
|
||||
},
|
||||
nodeOps,
|
||||
patchData,
|
||||
teardownVNode
|
||||
|
@ -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())) {
|
||||
|
Loading…
Reference in New Issue
Block a user