refactor: watch APIs default to trigger pre-flush

BREAKING CHANGE: watch APIs now default to use `flush: 'pre'` instead of
`flush: 'post'`.

  - This change affects `watch`, `watchEffect`, the `watch` component
    option, and `this.$watch`.

  - As pointed out by @skirtles-code in
    [this comment](https://github.com/vuejs/vue-next/issues/1706#issuecomment-666258948),
    Vue 2's watch behavior is pre-flush, and the ecosystem has many uses
    of watch that assumes the pre-flush behavior. Defaulting to post-flush
    can result in unnecessary re-renders without the users being aware of
    it.

  - With this change, watchers need to specify `{ flush: 'post' }` via
    options to trigger callback after Vue render updates. Note that
    specifying `{ flush: 'post' }` will also defer `watchEffect`'s
    initial run to wait for the component's initial render.
This commit is contained in:
Evan You
2020-09-17 23:17:21 -04:00
parent 58c31e3699
commit 49bb44756f
4 changed files with 78 additions and 67 deletions

View File

@@ -268,9 +268,10 @@ function doWatch(
let scheduler: (job: () => any) => void
if (flush === 'sync') {
scheduler = job
} else if (flush === 'pre') {
// ensure it's queued before component updates (which have positive ids)
job.id = -1
} else if (flush === 'post') {
scheduler = () => queuePostRenderEffect(job, instance && instance.suspense)
} else {
// default: 'pre'
scheduler = () => {
if (!instance || instance.isMounted) {
queuePreFlushCb(job)
@@ -280,8 +281,6 @@ function doWatch(
job()
}
}
} else {
scheduler = () => queuePostRenderEffect(job, instance && instance.suspense)
}
const runner = effect(getter, {
@@ -300,6 +299,8 @@ function doWatch(
} else {
oldValue = runner()
}
} else if (flush === 'post') {
queuePostRenderEffect(runner, instance && instance.suspense)
} else {
runner()
}

View File

@@ -171,15 +171,18 @@ const KeepAliveImpl = {
keys.delete(key)
}
// prune cache on include/exclude prop change
watch(
() => [props.include, props.exclude],
([include, exclude]) => {
include && pruneCache(name => matches(include, name))
exclude && pruneCache(name => !matches(exclude, name))
}
},
// prune post-render after `current` has been updated
{ flush: 'post' }
)
// cache sub tree in beforeMount/Update (i.e. right after the render)
// cache sub tree after render
let pendingCacheKey: CacheKey | null = null
const cacheSubtree = () => {
// fix #1621, the pendingCacheKey could be 0