fix(watch): post flush watchers should not fire when component is unmounted

fix #1603
This commit is contained in:
Evan You
2020-07-17 11:17:29 -04:00
parent 024a8f10f5
commit 341b30c961
4 changed files with 100 additions and 39 deletions

View File

@@ -234,33 +234,39 @@ function doWatch(
}
let oldValue = isArray(source) ? [] : INITIAL_WATCHER_VALUE
const applyCb = cb
? () => {
if (instance && instance.isUnmounted) {
return
}
const newValue = runner()
if (deep || hasChanged(newValue, oldValue)) {
// cleanup before running cb again
if (cleanup) {
cleanup()
}
callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
newValue,
// pass undefined as the old value when it's changed for the first time
oldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue,
onInvalidate
])
oldValue = newValue
const job = () => {
if (!runner.active) {
return
}
if (cb) {
// watch(source, cb)
const newValue = runner()
if (deep || hasChanged(newValue, oldValue)) {
// cleanup before running cb again
if (cleanup) {
cleanup()
}
callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
newValue,
// pass undefined as the old value when it's changed for the first time
oldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue,
onInvalidate
])
oldValue = newValue
}
: void 0
} else {
// watchEffect
runner()
}
}
let scheduler: (job: () => any) => void
if (flush === 'sync') {
scheduler = invoke
} else if (flush === 'pre') {
scheduler = job => {
// ensure it's queued before component updates (which have positive ids)
job.id = -1
scheduler = () => {
if (!instance || instance.isMounted) {
queueJob(job)
} else {
@@ -270,22 +276,22 @@ function doWatch(
}
}
} else {
scheduler = job => queuePostRenderEffect(job, instance && instance.suspense)
scheduler = () => queuePostRenderEffect(job, instance && instance.suspense)
}
const runner = effect(getter, {
lazy: true,
onTrack,
onTrigger,
scheduler: applyCb ? () => scheduler(applyCb) : scheduler
scheduler
})
recordInstanceBoundEffect(runner)
// initial run
if (applyCb) {
if (cb) {
if (immediate) {
applyCb()
job()
} else {
oldValue = runner()
}

View File

@@ -2025,19 +2025,17 @@ function baseCreateRenderer(
if (bum) {
invokeArrayFns(bum)
}
if (effects) {
for (let i = 0; i < effects.length; i++) {
stop(effects[i])
}
}
// update may be null if a component is unmounted before its async
// setup has resolved.
if (update) {
stop(update)
unmount(subTree, instance, parentSuspense, doRemove)
}
if (effects) {
queuePostRenderEffect(() => {
for (let i = 0; i < effects.length; i++) {
stop(effects[i])
}
}, parentSuspense)
}
// unmounted hook
if (um) {
queuePostRenderEffect(um, parentSuspense)