refactor: properly cleanup invalidated jobs
This commit is contained in:
parent
d39eb6cdbc
commit
a95532495a
@ -25,6 +25,7 @@ import {
|
|||||||
} from './errorHandling'
|
} from './errorHandling'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
import { setCurrentInstance, unsetCurrentInstance } from './experimental/hooks'
|
import { setCurrentInstance, unsetCurrentInstance } from './experimental/hooks'
|
||||||
|
import { stop } from '@vue/observer'
|
||||||
|
|
||||||
let currentVNode: VNode | null = null
|
let currentVNode: VNode | null = null
|
||||||
let currentContextVNode: VNode | null = null
|
let currentContextVNode: VNode | null = null
|
||||||
@ -150,9 +151,6 @@ export function renderFunctionalRoot(vnode: VNode): VNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function teardownComponentInstance(instance: ComponentInstance) {
|
export function teardownComponentInstance(instance: ComponentInstance) {
|
||||||
if (instance._unmounted) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const parentComponent = instance.$parent && instance.$parent._self
|
const parentComponent = instance.$parent && instance.$parent._self
|
||||||
if (parentComponent && !parentComponent._unmounted) {
|
if (parentComponent && !parentComponent._unmounted) {
|
||||||
parentComponent.$children.splice(
|
parentComponent.$children.splice(
|
||||||
@ -160,6 +158,7 @@ export function teardownComponentInstance(instance: ComponentInstance) {
|
|||||||
1
|
1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
stop(instance._updateHandle)
|
||||||
teardownComputed(instance)
|
teardownComputed(instance)
|
||||||
teardownWatch(instance)
|
teardownWatch(instance)
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,13 @@ import {
|
|||||||
immutable,
|
immutable,
|
||||||
AutorunOptions
|
AutorunOptions
|
||||||
} from '@vue/observer'
|
} from '@vue/observer'
|
||||||
import { queueJob, handleSchedulerError, nextTick } from '@vue/scheduler'
|
import {
|
||||||
|
queueJob,
|
||||||
|
handleSchedulerError,
|
||||||
|
nextTick,
|
||||||
|
queuePostCommitCb,
|
||||||
|
flushPostCommitCbs
|
||||||
|
} from '@vue/scheduler'
|
||||||
import { VNodeFlags, ChildrenFlags } from './flags'
|
import { VNodeFlags, ChildrenFlags } from './flags'
|
||||||
import { EMPTY_OBJ, reservedPropRE, isString } from '@vue/shared'
|
import { EMPTY_OBJ, reservedPropRE, isString } from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
@ -110,22 +116,6 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lifecycle Hooks -----------------------------------------------------------
|
|
||||||
|
|
||||||
const lifecycleHooks: Function[] = []
|
|
||||||
const vnodeUpdatedHooks: Function[] = []
|
|
||||||
|
|
||||||
function queuePostCommitHook(fn: Function) {
|
|
||||||
lifecycleHooks.push(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
function flushHooks() {
|
|
||||||
let fn
|
|
||||||
while ((fn = lifecycleHooks.pop())) {
|
|
||||||
fn()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// mounting ------------------------------------------------------------------
|
// mounting ------------------------------------------------------------------
|
||||||
|
|
||||||
function mount(
|
function mount(
|
||||||
@ -197,12 +187,12 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
insertOrAppend(container, el, endNode)
|
insertOrAppend(container, el, endNode)
|
||||||
}
|
}
|
||||||
if (ref) {
|
if (ref) {
|
||||||
queuePostCommitHook(() => {
|
queuePostCommitCb(() => {
|
||||||
ref(el)
|
ref(el)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (data != null && data.vnodeMounted) {
|
if (data != null && data.vnodeMounted) {
|
||||||
queuePostCommitHook(() => {
|
queuePostCommitCb(() => {
|
||||||
data.vnodeMounted(vnode)
|
data.vnodeMounted(vnode)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -238,8 +228,17 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
mountComponentInstance(vnode, container, isSVG, endNode)
|
mountComponentInstance(vnode, container, isSVG, endNode)
|
||||||
} else {
|
} else {
|
||||||
queueJob(() => {
|
queueJob(() => {
|
||||||
mountComponentInstance(vnode, container, isSVG, endNode)
|
const instance = mountComponentInstance(
|
||||||
}, flushHooks)
|
vnode,
|
||||||
|
container,
|
||||||
|
isSVG,
|
||||||
|
endNode
|
||||||
|
)
|
||||||
|
// cleanup if mount is invalidated before committed
|
||||||
|
return () => {
|
||||||
|
teardownComponentInstance(instance)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,7 +278,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
const subTree = (handle.prevTree = vnode.children = renderFunctionalRoot(
|
const subTree = (handle.prevTree = vnode.children = renderFunctionalRoot(
|
||||||
vnode
|
vnode
|
||||||
))
|
))
|
||||||
queuePostCommitHook(() => {
|
queuePostCommitCb(() => {
|
||||||
vnode.el = subTree.el as RenderNode
|
vnode.el = subTree.el as RenderNode
|
||||||
})
|
})
|
||||||
mount(subTree, container, vnode as MountedVNode, isSVG, endNode)
|
mount(subTree, container, vnode as MountedVNode, isSVG, endNode)
|
||||||
@ -300,7 +299,13 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
if (__COMPAT__) {
|
if (__COMPAT__) {
|
||||||
doMount()
|
doMount()
|
||||||
} else {
|
} else {
|
||||||
queueJob(doMount)
|
queueJob(() => {
|
||||||
|
doMount()
|
||||||
|
// cleanup if mount is invalidated before committed
|
||||||
|
return () => {
|
||||||
|
stop(handle.runner)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,7 +318,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
const nextTree = (handle.prevTree = current.children = renderFunctionalRoot(
|
const nextTree = (handle.prevTree = current.children = renderFunctionalRoot(
|
||||||
current
|
current
|
||||||
))
|
))
|
||||||
queuePostCommitHook(() => {
|
queuePostCommitCb(() => {
|
||||||
current.el = nextTree.el
|
current.el = nextTree.el
|
||||||
})
|
})
|
||||||
patch(
|
patch(
|
||||||
@ -349,7 +354,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
const { children, childFlags } = vnode
|
const { children, childFlags } = vnode
|
||||||
switch (childFlags) {
|
switch (childFlags) {
|
||||||
case ChildrenFlags.SINGLE_VNODE:
|
case ChildrenFlags.SINGLE_VNODE:
|
||||||
queuePostCommitHook(() => {
|
queuePostCommitCb(() => {
|
||||||
vnode.el = (children as MountedVNode).el
|
vnode.el = (children as MountedVNode).el
|
||||||
})
|
})
|
||||||
mount(children as VNode, container, contextVNode, isSVG, endNode)
|
mount(children as VNode, container, contextVNode, isSVG, endNode)
|
||||||
@ -360,7 +365,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
vnode.el = placeholder.el
|
vnode.el = placeholder.el
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
queuePostCommitHook(() => {
|
queuePostCommitCb(() => {
|
||||||
vnode.el = (children as MountedVNode[])[0].el
|
vnode.el = (children as MountedVNode[])[0].el
|
||||||
})
|
})
|
||||||
mountArrayChildren(
|
mountArrayChildren(
|
||||||
@ -397,7 +402,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (ref) {
|
if (ref) {
|
||||||
queuePostCommitHook(() => {
|
queuePostCommitCb(() => {
|
||||||
ref(target)
|
ref(target)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -529,9 +534,10 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (nextData != null && nextData.vnodeUpdated) {
|
if (nextData != null && nextData.vnodeUpdated) {
|
||||||
vnodeUpdatedHooks.push(() => {
|
// TODO fix me
|
||||||
nextData.vnodeUpdated(nextVNode, prevVNode)
|
// vnodeUpdatedHooks.push(() => {
|
||||||
})
|
// nextData.vnodeUpdated(nextVNode, prevVNode)
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,7 +617,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
// then retrieve its next sibling to use as the end node for patchChildren.
|
// then retrieve its next sibling to use as the end node for patchChildren.
|
||||||
const endNode = platformNextSibling(getVNodeLastEl(prevVNode))
|
const endNode = platformNextSibling(getVNodeLastEl(prevVNode))
|
||||||
const { childFlags, children } = nextVNode
|
const { childFlags, children } = nextVNode
|
||||||
queuePostCommitHook(() => {
|
queuePostCommitCb(() => {
|
||||||
switch (childFlags) {
|
switch (childFlags) {
|
||||||
case ChildrenFlags.SINGLE_VNODE:
|
case ChildrenFlags.SINGLE_VNODE:
|
||||||
nextVNode.el = (children as MountedVNode).el
|
nextVNode.el = (children as MountedVNode).el
|
||||||
@ -1192,7 +1198,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
container: RenderNode | null,
|
container: RenderNode | null,
|
||||||
isSVG: boolean,
|
isSVG: boolean,
|
||||||
endNode: RenderNode | null
|
endNode: RenderNode | null
|
||||||
) {
|
): ComponentInstance {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
pushWarningContext(vnode)
|
pushWarningContext(vnode)
|
||||||
}
|
}
|
||||||
@ -1212,12 +1218,8 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
$options: { beforeMount, renderTracked, renderTriggered }
|
$options: { beforeMount, renderTracked, renderTriggered }
|
||||||
} = instance
|
} = instance
|
||||||
|
|
||||||
if (beforeMount) {
|
|
||||||
callLifecycleHookWithHandler(beforeMount, $proxy, ErrorTypes.BEFORE_MOUNT)
|
|
||||||
}
|
|
||||||
|
|
||||||
const queueUpdate = (instance.$forceUpdate = () => {
|
const queueUpdate = (instance.$forceUpdate = () => {
|
||||||
queueJob(instance._updateHandle, flushHooks)
|
queueJob(instance._updateHandle)
|
||||||
})
|
})
|
||||||
|
|
||||||
const autorunOptions: AutorunOptions = {
|
const autorunOptions: AutorunOptions = {
|
||||||
@ -1254,10 +1256,18 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
if (instance._mounted) {
|
if (instance._mounted) {
|
||||||
updateComponentInstance(instance, isSVG)
|
updateComponentInstance(instance, isSVG)
|
||||||
} else {
|
} else {
|
||||||
|
if (beforeMount) {
|
||||||
|
callLifecycleHookWithHandler(
|
||||||
|
beforeMount,
|
||||||
|
$proxy,
|
||||||
|
ErrorTypes.BEFORE_MOUNT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// this will be executed synchronously right here
|
// this will be executed synchronously right here
|
||||||
instance.$vnode = renderInstanceRoot(instance) as MountedVNode
|
instance.$vnode = renderInstanceRoot(instance) as MountedVNode
|
||||||
|
|
||||||
queuePostCommitHook(() => {
|
queuePostCommitCb(() => {
|
||||||
vnode.el = instance.$vnode.el
|
vnode.el = instance.$vnode.el
|
||||||
if (__COMPAT__) {
|
if (__COMPAT__) {
|
||||||
// expose __vue__ for devtools
|
// expose __vue__ for devtools
|
||||||
@ -1283,6 +1293,8 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
popWarningContext()
|
popWarningContext()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return instance
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateComponentInstance(
|
function updateComponentInstance(
|
||||||
@ -1312,7 +1324,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
instance
|
instance
|
||||||
) as MountedVNode)
|
) as MountedVNode)
|
||||||
|
|
||||||
queuePostCommitHook(() => {
|
queuePostCommitCb(() => {
|
||||||
const el = nextVNode.el as RenderNode
|
const el = nextVNode.el as RenderNode
|
||||||
if (__COMPAT__) {
|
if (__COMPAT__) {
|
||||||
// expose __vue__ for devtools
|
// expose __vue__ for devtools
|
||||||
@ -1337,15 +1349,15 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
nextVNode
|
nextVNode
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (vnodeUpdatedHooks.length > 0) {
|
|
||||||
const vnodeUpdatedHooksForCurrentInstance = vnodeUpdatedHooks.slice()
|
// TODO fix me
|
||||||
vnodeUpdatedHooks.length = 0
|
// if (vnodeUpdatedHooks.length > 0) {
|
||||||
queuePostCommitHook(() => {
|
// const vnodeUpdatedHooksForCurrentInstance = vnodeUpdatedHooks.slice()
|
||||||
for (let i = 0; i < vnodeUpdatedHooksForCurrentInstance.length; i++) {
|
// vnodeUpdatedHooks.length = 0
|
||||||
vnodeUpdatedHooksForCurrentInstance[i]()
|
// for (let i = 0; i < vnodeUpdatedHooksForCurrentInstance.length; i++) {
|
||||||
}
|
// vnodeUpdatedHooksForCurrentInstance[i]()
|
||||||
})
|
// }
|
||||||
}
|
// }
|
||||||
})
|
})
|
||||||
|
|
||||||
const container = platformParentNode(prevVNode.el) as RenderNode
|
const container = platformParentNode(prevVNode.el) as RenderNode
|
||||||
@ -1363,7 +1375,6 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
const {
|
const {
|
||||||
$vnode,
|
$vnode,
|
||||||
$proxy,
|
$proxy,
|
||||||
_updateHandle,
|
|
||||||
$options: { beforeUnmount, unmounted }
|
$options: { beforeUnmount, unmounted }
|
||||||
} = instance
|
} = instance
|
||||||
if (beforeUnmount) {
|
if (beforeUnmount) {
|
||||||
@ -1376,7 +1387,6 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
if ($vnode) {
|
if ($vnode) {
|
||||||
unmount($vnode)
|
unmount($vnode)
|
||||||
}
|
}
|
||||||
stop(_updateHandle)
|
|
||||||
teardownComponentInstance(instance)
|
teardownComponentInstance(instance)
|
||||||
instance._unmounted = true
|
instance._unmounted = true
|
||||||
if (unmounted) {
|
if (unmounted) {
|
||||||
@ -1402,7 +1412,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
popWarningContext()
|
popWarningContext()
|
||||||
}
|
}
|
||||||
queuePostCommitHook(() => {
|
queuePostCommitCb(() => {
|
||||||
callActivatedHook(instance, true)
|
callActivatedHook(instance, true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1486,7 +1496,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (__COMPAT__) {
|
if (__COMPAT__) {
|
||||||
flushHooks()
|
flushPostCommitCbs()
|
||||||
return vnode && vnode.flags & VNodeFlags.COMPONENT_STATEFUL
|
return vnode && vnode.flags & VNodeFlags.COMPONENT_STATEFUL
|
||||||
? (vnode.children as ComponentInstance).$proxy
|
? (vnode.children as ComponentInstance).$proxy
|
||||||
: null
|
: null
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { queueJob, nextTick } from '../src/index'
|
import { queueJob, queuePostCommitCb, nextTick } from '../src/index'
|
||||||
|
|
||||||
describe('scheduler', () => {
|
describe('scheduler', () => {
|
||||||
it('queueJob', async () => {
|
it('queueJob', async () => {
|
||||||
@ -32,13 +32,15 @@ describe('scheduler', () => {
|
|||||||
expect(calls).toEqual(['job1', 'job2'])
|
expect(calls).toEqual(['job1', 'job2'])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('queueJob w/ postFlushCb', async () => {
|
it('queueJob w/ postCommitCb', async () => {
|
||||||
const calls: any = []
|
const calls: any = []
|
||||||
const job1 = () => {
|
const job1 = () => {
|
||||||
calls.push('job1')
|
calls.push('job1')
|
||||||
|
queuePostCommitCb(cb1)
|
||||||
}
|
}
|
||||||
const job2 = () => {
|
const job2 = () => {
|
||||||
calls.push('job2')
|
calls.push('job2')
|
||||||
|
queuePostCommitCb(cb2)
|
||||||
}
|
}
|
||||||
const cb1 = () => {
|
const cb1 = () => {
|
||||||
calls.push('cb1')
|
calls.push('cb1')
|
||||||
@ -46,21 +48,24 @@ describe('scheduler', () => {
|
|||||||
const cb2 = () => {
|
const cb2 = () => {
|
||||||
calls.push('cb2')
|
calls.push('cb2')
|
||||||
}
|
}
|
||||||
queueJob(job1, cb1)
|
queueJob(job1)
|
||||||
queueJob(job2, cb2)
|
queueJob(job2)
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(calls).toEqual(['job1', 'job2', 'cb1', 'cb2'])
|
// post commit cbs are called in reverse!
|
||||||
|
expect(calls).toEqual(['job1', 'job2', 'cb2', 'cb1'])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('queueJob w/ postFlushCb while flushing', async () => {
|
it('queueJob w/ postFlushCb while flushing', async () => {
|
||||||
const calls: any = []
|
const calls: any = []
|
||||||
const job1 = () => {
|
const job1 = () => {
|
||||||
calls.push('job1')
|
calls.push('job1')
|
||||||
|
queuePostCommitCb(cb1)
|
||||||
// job1 queues job2
|
// job1 queues job2
|
||||||
queueJob(job2, cb2)
|
queueJob(job2)
|
||||||
}
|
}
|
||||||
const job2 = () => {
|
const job2 = () => {
|
||||||
calls.push('job2')
|
calls.push('job2')
|
||||||
|
queuePostCommitCb(cb2)
|
||||||
}
|
}
|
||||||
const cb1 = () => {
|
const cb1 = () => {
|
||||||
calls.push('cb1')
|
calls.push('cb1')
|
||||||
@ -68,10 +73,10 @@ describe('scheduler', () => {
|
|||||||
const cb2 = () => {
|
const cb2 = () => {
|
||||||
calls.push('cb2')
|
calls.push('cb2')
|
||||||
}
|
}
|
||||||
queueJob(job1, cb1)
|
queueJob(job1)
|
||||||
expect(calls).toEqual([])
|
expect(calls).toEqual([])
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(calls).toEqual(['job1', 'job2', 'cb1', 'cb2'])
|
expect(calls).toEqual(['job1', 'job2', 'cb2', 'cb1'])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should dedupe queued tasks', async () => {
|
it('should dedupe queued tasks', async () => {
|
||||||
@ -91,26 +96,28 @@ describe('scheduler', () => {
|
|||||||
expect(calls).toEqual(['job1', 'job2'])
|
expect(calls).toEqual(['job1', 'job2'])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('queueJob inside postFlushCb', async () => {
|
it('queueJob inside postCommitCb', async () => {
|
||||||
const calls: any = []
|
const calls: any = []
|
||||||
const job1 = () => {
|
const job1 = () => {
|
||||||
calls.push('job1')
|
calls.push('job1')
|
||||||
|
queuePostCommitCb(cb1)
|
||||||
}
|
}
|
||||||
const cb1 = () => {
|
const cb1 = () => {
|
||||||
// queue another job in postFlushCb
|
// queue another job in postFlushCb
|
||||||
calls.push('cb1')
|
calls.push('cb1')
|
||||||
queueJob(job2, cb2)
|
queueJob(job2)
|
||||||
}
|
}
|
||||||
const job2 = () => {
|
const job2 = () => {
|
||||||
calls.push('job2')
|
calls.push('job2')
|
||||||
|
queuePostCommitCb(cb2)
|
||||||
}
|
}
|
||||||
const cb2 = () => {
|
const cb2 = () => {
|
||||||
calls.push('cb2')
|
calls.push('cb2')
|
||||||
}
|
}
|
||||||
|
|
||||||
queueJob(job1, cb1)
|
queueJob(job1)
|
||||||
queueJob(job2, cb2)
|
queueJob(job2)
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(calls).toEqual(['job1', 'job2', 'cb1', 'cb2', 'job2', 'cb2'])
|
expect(calls).toEqual(['job1', 'job2', 'cb2', 'cb1', 'job2', 'cb2'])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
// TODO infinite updates detection
|
||||||
|
|
||||||
import { Op, setCurrentOps } from './patchNodeOps'
|
import { Op, setCurrentOps } from './patchNodeOps'
|
||||||
|
|
||||||
interface Job extends Function {
|
interface Job extends Function {
|
||||||
ops: Op[]
|
ops: Op[]
|
||||||
post: Function | null
|
post: Function[]
|
||||||
|
cleanup: Function | null
|
||||||
expiration: number
|
expiration: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -12,6 +15,8 @@ const enum Priorities {
|
|||||||
|
|
||||||
type ErrorHandler = (err: Error) => any
|
type ErrorHandler = (err: Error) => any
|
||||||
|
|
||||||
|
let currentJob: Job | null = null
|
||||||
|
|
||||||
let start: number = 0
|
let start: number = 0
|
||||||
const getNow = () => window.performance.now()
|
const getNow = () => window.performance.now()
|
||||||
const frameBudget = __JSDOM__ ? Infinity : 1000 / 60
|
const frameBudget = __JSDOM__ ? Infinity : 1000 / 60
|
||||||
@ -85,10 +90,10 @@ export function handleSchedulerError(handler: ErrorHandler) {
|
|||||||
|
|
||||||
let hasPendingFlush = false
|
let hasPendingFlush = false
|
||||||
|
|
||||||
export function queueJob(rawJob: Function, postJob?: Function | null) {
|
export function queueJob(rawJob: Function) {
|
||||||
const job = rawJob as Job
|
const job = rawJob as Job
|
||||||
job.post = postJob || null
|
|
||||||
job.ops = job.ops || []
|
job.ops = job.ops || []
|
||||||
|
job.post = job.post || []
|
||||||
// 1. let's see if this invalidates any work that
|
// 1. let's see if this invalidates any work that
|
||||||
// has already been done.
|
// has already been done.
|
||||||
const commitIndex = commitQueue.indexOf(job)
|
const commitIndex = commitQueue.indexOf(job)
|
||||||
@ -118,6 +123,24 @@ export function queueJob(rawJob: Function, postJob?: Function | null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function queuePostCommitCb(fn: Function) {
|
||||||
|
if (currentJob) {
|
||||||
|
currentJob.post.push(fn)
|
||||||
|
} else {
|
||||||
|
postCommitQueue.push(fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function flushPostCommitCbs() {
|
||||||
|
// post commit hooks (updated, mounted)
|
||||||
|
// this queue is flushed in reverse becuase these hooks should be invoked
|
||||||
|
// child first
|
||||||
|
let job
|
||||||
|
while ((job = postCommitQueue.pop())) {
|
||||||
|
job()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function flush(): void {
|
function flush(): void {
|
||||||
let job
|
let job
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -139,14 +162,12 @@ function flush(): void {
|
|||||||
// all done, time to commit!
|
// all done, time to commit!
|
||||||
while ((job = commitQueue.shift())) {
|
while ((job = commitQueue.shift())) {
|
||||||
commitJob(job)
|
commitJob(job)
|
||||||
if (job.post && postCommitQueue.indexOf(job.post) < 0) {
|
if (job.post) {
|
||||||
postCommitQueue.push(job.post)
|
postCommitQueue.push(...job.post)
|
||||||
|
job.post.length = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// post commit hooks (updated, mounted)
|
flushPostCommitCbs()
|
||||||
while ((job = postCommitQueue.shift())) {
|
|
||||||
job()
|
|
||||||
}
|
|
||||||
// some post commit hook triggered more updates...
|
// some post commit hook triggered more updates...
|
||||||
if (patchQueue.length > 0) {
|
if (patchQueue.length > 0) {
|
||||||
if (!__COMPAT__ && getNow() - start > frameBudget) {
|
if (!__COMPAT__ && getNow() - start > frameBudget) {
|
||||||
@ -174,7 +195,9 @@ function patchJob(job: Job) {
|
|||||||
// job with existing ops means it's already been patched in a low priority queue
|
// job with existing ops means it's already been patched in a low priority queue
|
||||||
if (job.ops.length === 0) {
|
if (job.ops.length === 0) {
|
||||||
setCurrentOps(job.ops)
|
setCurrentOps(job.ops)
|
||||||
job()
|
currentJob = job
|
||||||
|
job.cleanup = job()
|
||||||
|
currentJob = null
|
||||||
setCurrentOps(null)
|
setCurrentOps(null)
|
||||||
commitQueue.push(job)
|
commitQueue.push(job)
|
||||||
}
|
}
|
||||||
@ -190,4 +213,9 @@ function commitJob({ ops }: Job) {
|
|||||||
|
|
||||||
function invalidateJob(job: Job) {
|
function invalidateJob(job: Job) {
|
||||||
job.ops.length = 0
|
job.ops.length = 0
|
||||||
|
job.post.length = 0
|
||||||
|
if (job.cleanup) {
|
||||||
|
job.cleanup()
|
||||||
|
job.cleanup = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,9 @@ describe('2.x compat build', async () => {
|
|||||||
const root = document.createElement('div')
|
const root = document.createElement('div')
|
||||||
document.body.appendChild(root)
|
document.body.appendChild(root)
|
||||||
|
|
||||||
|
const mounted = jest.fn()
|
||||||
|
const updated = jest.fn()
|
||||||
|
|
||||||
const instance = new Vue({
|
const instance = new Vue({
|
||||||
data() {
|
data() {
|
||||||
return { count: 0 }
|
return { count: 0 }
|
||||||
@ -18,15 +21,19 @@ describe('2.x compat build', async () => {
|
|||||||
},
|
},
|
||||||
render(h: any) {
|
render(h: any) {
|
||||||
return h('div', this.count)
|
return h('div', this.count)
|
||||||
}
|
},
|
||||||
|
mounted,
|
||||||
|
updated
|
||||||
}).$mount(root)
|
}).$mount(root)
|
||||||
|
|
||||||
expect(instance.count).toBe(0)
|
expect(instance.count).toBe(0)
|
||||||
expect(root.textContent).toBe('0')
|
expect(root.textContent).toBe('0')
|
||||||
|
expect(mounted).toHaveBeenCalled()
|
||||||
|
|
||||||
instance.change()
|
instance.change()
|
||||||
expect(instance.count).toBe(1)
|
expect(instance.count).toBe(1)
|
||||||
await Vue.nextTick()
|
await Vue.nextTick()
|
||||||
expect(root.textContent).toBe('1')
|
expect(root.textContent).toBe('1')
|
||||||
|
expect(updated).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user