perf(reactivity): improve reactive effect memory usage (#4001)
Based on #2345 , but with smaller API change - Use class implementation for `ReactiveEffect` - Switch internal creation of effects to use the class constructor - Avoid options object allocation - Avoid creating bound effect runner function (used in schedulers) when not necessary. - Consumes ~17% less memory compared to last commit - Introduces a very minor breaking change: the `scheduler` option passed to `effect` no longer receives the runner function.
This commit is contained in:
@@ -48,15 +48,13 @@ import {
|
||||
flushPostFlushCbs,
|
||||
invalidateJob,
|
||||
flushPreFlushCbs,
|
||||
SchedulerCb
|
||||
SchedulerJob
|
||||
} from './scheduler'
|
||||
import {
|
||||
effect,
|
||||
stop,
|
||||
ReactiveEffectOptions,
|
||||
isRef,
|
||||
pauseTracking,
|
||||
resetTracking
|
||||
resetTracking,
|
||||
ReactiveEffect
|
||||
} from '@vue/reactivity'
|
||||
import { updateProps } from './componentProps'
|
||||
import { updateSlots } from './componentSlots'
|
||||
@@ -286,23 +284,6 @@ export const enum MoveType {
|
||||
REORDER
|
||||
}
|
||||
|
||||
const prodEffectOptions = {
|
||||
scheduler: queueJob,
|
||||
// #1801, #2043 component render effects should allow recursive updates
|
||||
allowRecurse: true
|
||||
}
|
||||
|
||||
function createDevEffectOptions(
|
||||
instance: ComponentInternalInstance
|
||||
): ReactiveEffectOptions {
|
||||
return {
|
||||
scheduler: queueJob,
|
||||
allowRecurse: true,
|
||||
onTrack: instance.rtc ? e => invokeArrayFns(instance.rtc!, e) : void 0,
|
||||
onTrigger: instance.rtg ? e => invokeArrayFns(instance.rtg!, e) : void 0
|
||||
}
|
||||
}
|
||||
|
||||
export const queuePostRenderEffect = __FEATURE_SUSPENSE__
|
||||
? queueEffectWithSuspense
|
||||
: queuePostFlushCb
|
||||
@@ -378,7 +359,7 @@ export const setRef = (
|
||||
// null values means this is unmount and it should not overwrite another
|
||||
// ref with the same key
|
||||
if (value) {
|
||||
;(doSet as SchedulerCb).id = -1
|
||||
;(doSet as SchedulerJob).id = -1
|
||||
queuePostRenderEffect(doSet, parentSuspense)
|
||||
} else {
|
||||
doSet()
|
||||
@@ -388,7 +369,7 @@ export const setRef = (
|
||||
ref.value = value
|
||||
}
|
||||
if (value) {
|
||||
;(doSet as SchedulerCb).id = -1
|
||||
;(doSet as SchedulerJob).id = -1
|
||||
queuePostRenderEffect(doSet, parentSuspense)
|
||||
} else {
|
||||
doSet()
|
||||
@@ -1394,7 +1375,7 @@ function baseCreateRenderer(
|
||||
// in case the child component is also queued, remove it to avoid
|
||||
// double updating the same child component in the same flush.
|
||||
invalidateJob(instance.update)
|
||||
// instance.update is the reactive effect runner.
|
||||
// instance.update is the reactive effect.
|
||||
instance.update()
|
||||
}
|
||||
} else {
|
||||
@@ -1414,8 +1395,7 @@ function baseCreateRenderer(
|
||||
isSVG,
|
||||
optimized
|
||||
) => {
|
||||
// create reactive effect for rendering
|
||||
instance.update = effect(function componentEffect() {
|
||||
const componentUpdateFn = () => {
|
||||
if (!instance.isMounted) {
|
||||
let vnodeHook: VNodeHook | null | undefined
|
||||
const { el, props } = initialVNode
|
||||
@@ -1639,12 +1619,33 @@ function baseCreateRenderer(
|
||||
popWarningContext()
|
||||
}
|
||||
}
|
||||
}, __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions)
|
||||
}
|
||||
|
||||
// create reactive effect for rendering
|
||||
const effect = (instance.effect = new ReactiveEffect(
|
||||
componentUpdateFn,
|
||||
() => queueJob(instance.update),
|
||||
true /* allowRecurse */
|
||||
))
|
||||
|
||||
const update = (instance.update = effect.run.bind(effect) as SchedulerJob)
|
||||
update.id = instance.uid
|
||||
// allowRecurse
|
||||
// #1801, #2043 component render effects should allow recursive updates
|
||||
update.allowRecurse = true
|
||||
|
||||
if (__DEV__) {
|
||||
// @ts-ignore
|
||||
instance.update.ownerInstance = instance
|
||||
effect.onTrack = instance.rtc
|
||||
? e => invokeArrayFns(instance.rtc!, e)
|
||||
: void 0
|
||||
effect.onTrigger = instance.rtg
|
||||
? e => invokeArrayFns(instance.rtg!, e)
|
||||
: void 0
|
||||
// @ts-ignore (for scheduler)
|
||||
update.ownerInstance = instance
|
||||
}
|
||||
|
||||
update()
|
||||
}
|
||||
|
||||
const updateComponentPreRender = (
|
||||
@@ -2284,7 +2285,7 @@ function baseCreateRenderer(
|
||||
unregisterHMR(instance)
|
||||
}
|
||||
|
||||
const { bum, effects, update, subTree, um } = instance
|
||||
const { bum, effect, effects, update, subTree, um } = instance
|
||||
|
||||
// beforeUnmount hook
|
||||
if (bum) {
|
||||
@@ -2299,13 +2300,15 @@ function baseCreateRenderer(
|
||||
|
||||
if (effects) {
|
||||
for (let i = 0; i < effects.length; i++) {
|
||||
stop(effects[i])
|
||||
effects[i].stop()
|
||||
}
|
||||
}
|
||||
// update may be null if a component is unmounted before its async
|
||||
// setup has resolved.
|
||||
if (update) {
|
||||
stop(update)
|
||||
if (effect) {
|
||||
effect.stop()
|
||||
// so that scheduler will no longer invoke it
|
||||
update.active = false
|
||||
unmount(subTree, instance, parentSuspense, doRemove)
|
||||
}
|
||||
// unmounted hook
|
||||
|
||||
Reference in New Issue
Block a user