From ebaac9a56d82d266e333d077b6457543d7cab9ae Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 6 Jul 2021 22:01:59 -0400 Subject: [PATCH] perf(reactivity): avoid triggering re-render if computed value did not change --- packages/reactivity/src/computed.ts | 37 +++++++++++++++++++++++++- packages/reactivity/src/index.ts | 1 + packages/runtime-core/src/scheduler.ts | 4 +++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 396e50ecdb0..0fe19191eb7 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -5,6 +5,7 @@ import { ReactiveFlags, toRaw } from './reactive' export interface ComputedRef extends WritableComputedRef { readonly value: T + defer?: (fn: () => void) => void } export interface WritableComputedRef extends Ref { @@ -19,6 +20,16 @@ export interface WritableComputedOptions { set: ComputedSetter } +type ComputedScheduler = (fn: () => void) => void +let scheduler: ComputedScheduler | undefined + +/** + * Set a scheduler for deferring computed computations + */ +export const setComputedScheduler = (s: ComputedScheduler | undefined) => { + scheduler = s +} + class ComputedRefImpl { public dep?: Set = undefined @@ -35,10 +46,34 @@ class ComputedRefImpl { private readonly _setter: ComputedSetter, isReadonly: boolean ) { + let deferFn: () => void + let scheduled = false this.effect = new ReactiveEffect(getter, () => { if (!this._dirty) { this._dirty = true - triggerRefValue(this) + if (scheduler) { + if (!scheduled) { + scheduled = true + scheduler( + deferFn || + (deferFn = () => { + scheduled = false + if (this._dirty) { + this._dirty = false + const newValue = this.effect.run()! + if (this._value !== newValue) { + this._value = newValue + triggerRefValue(this) + } + } else { + triggerRefValue(this) + } + }) + ) + } + } else { + triggerRefValue(this) + } } }) this[ReactiveFlags.IS_READONLY] = isReadonly diff --git a/packages/reactivity/src/index.ts b/packages/reactivity/src/index.ts index d86f0bde882..3d4b05730a5 100644 --- a/packages/reactivity/src/index.ts +++ b/packages/reactivity/src/index.ts @@ -30,6 +30,7 @@ export { } from './reactive' export { computed, + setComputedScheduler, ComputedRef, WritableComputedRef, WritableComputedOptions, diff --git a/packages/runtime-core/src/scheduler.ts b/packages/runtime-core/src/scheduler.ts index 3c79a3be210..e5ef4e849c7 100644 --- a/packages/runtime-core/src/scheduler.ts +++ b/packages/runtime-core/src/scheduler.ts @@ -2,6 +2,10 @@ import { ErrorCodes, callWithErrorHandling } from './errorHandling' import { isArray } from '@vue/shared' import { ComponentInternalInstance, getComponentName } from './component' import { warn } from './warning' +import { setComputedScheduler } from '@vue/reactivity' + +// set scheduler for computed +setComputedScheduler(queueJob) export interface SchedulerJob extends Function { id?: number