From f485ea01a9d1d7aca3bdc61b12f768de91c8d583 Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Fri, 13 May 2022 19:01:02 +0800 Subject: [PATCH 001/110] perf: calculate embedded `computed()` on-demand --- .../reactivity/__tests__/computed.spec.ts | 51 +++++++++++++++++++ packages/reactivity/src/baseHandlers.ts | 4 +- packages/reactivity/src/collectionHandlers.ts | 8 +-- packages/reactivity/src/computed.ts | 25 +++++++-- packages/reactivity/src/deferredComputed.ts | 6 +-- packages/reactivity/src/effect.ts | 24 ++++----- packages/reactivity/src/ref.ts | 11 ++-- packages/runtime-core/src/compat/global.ts | 2 +- packages/runtime-core/src/componentProps.ts | 2 +- 9 files changed, 99 insertions(+), 34 deletions(-) diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index 51157944355..9169d75207c 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -290,4 +290,55 @@ describe('reactivity/computed', () => { oldValue: 2 }) }) + + it('chained computed value on-demand trigger', () => { + const c1Spy = jest.fn() + const c2Spy = jest.fn() + + const src = ref(0) + const c1 = computed(() => { + c1Spy() + return src.value < 5 + }) + const c2 = computed(() => { + c2Spy() + return c1.value ? '< 5' : '>= 5' + }) + + expect(c1Spy).toHaveBeenCalledTimes(0) + expect(c2Spy).toHaveBeenCalledTimes(0) + + expect(src.value).toBe(0) + expect(c2.value).toBe('< 5') + expect(c1Spy).toHaveBeenCalledTimes(1) + expect(c2Spy).toHaveBeenCalledTimes(1) + + src.value++ + expect(c2.value).toBe('< 5') + expect(c1Spy).toHaveBeenCalledTimes(2) + expect(c2Spy).toHaveBeenCalledTimes(1) + + for (let i = 0; i < 10; i++) { + src.value++ + } + expect(src.value).toBe(11) + expect(c2.value).toBe('>= 5') + expect(c1Spy).toHaveBeenCalledTimes(3) + expect(c2Spy).toHaveBeenCalledTimes(2) + + src.value++ + expect(src.value).toBe(12) + expect(c2.value).toBe('>= 5') + expect(c1Spy).toHaveBeenCalledTimes(4) + expect(c2Spy).toHaveBeenCalledTimes(2) + + for (let i = 0; i < 100; i++) { + src.value++ + c2.value + } + expect(src.value).toBe(112) + expect(c2.value).toBe('>= 5') + expect(c1Spy).toHaveBeenCalledTimes(104) + expect(c2Spy).toHaveBeenCalledTimes(2) + }) }) diff --git a/packages/reactivity/src/baseHandlers.ts b/packages/reactivity/src/baseHandlers.ts index 45ecfa6d38a..43bd425d2a7 100644 --- a/packages/reactivity/src/baseHandlers.ts +++ b/packages/reactivity/src/baseHandlers.ts @@ -190,9 +190,9 @@ function createSetter(shallow = false) { // don't trigger if target is something up in the prototype chain of original if (target === toRaw(receiver)) { if (!hadKey) { - trigger(target, TriggerOpTypes.ADD, key, value) + trigger(target, TriggerOpTypes.ADD, undefined, key, value) } else if (hasChanged(value, oldValue)) { - trigger(target, TriggerOpTypes.SET, key, value, oldValue) + trigger(target, TriggerOpTypes.SET, undefined, key, value, oldValue) } } return result diff --git a/packages/reactivity/src/collectionHandlers.ts b/packages/reactivity/src/collectionHandlers.ts index 381bbad6c28..651cb1e289f 100644 --- a/packages/reactivity/src/collectionHandlers.ts +++ b/packages/reactivity/src/collectionHandlers.ts @@ -73,7 +73,7 @@ function add(this: SetTypes, value: unknown) { const hadKey = proto.has.call(target, value) if (!hadKey) { target.add(value) - trigger(target, TriggerOpTypes.ADD, value, value) + trigger(target, TriggerOpTypes.ADD, undefined, value, value) } return this } @@ -94,9 +94,9 @@ function set(this: MapTypes, key: unknown, value: unknown) { const oldValue = get.call(target, key) target.set(key, value) if (!hadKey) { - trigger(target, TriggerOpTypes.ADD, key, value) + trigger(target, TriggerOpTypes.ADD, undefined, key, value) } else if (hasChanged(value, oldValue)) { - trigger(target, TriggerOpTypes.SET, key, value, oldValue) + trigger(target, TriggerOpTypes.SET, undefined, key, value, oldValue) } return this } @@ -116,7 +116,7 @@ function deleteEntry(this: CollectionTypes, key: unknown) { // forward the operation before queueing reactions const result = target.delete(key) if (hadKey) { - trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue) + trigger(target, TriggerOpTypes.DELETE, undefined, key, undefined, oldValue) } return result } diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index b24484c9e62..1d177afbcff 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -33,6 +33,7 @@ export class ComputedRefImpl { public readonly [ReactiveFlags.IS_READONLY]: boolean = false public _dirty = true + public _computedsToAskDirty: ComputedRefImpl[] = [] public _cacheable: boolean constructor( @@ -41,10 +42,13 @@ export class ComputedRefImpl { isReadonly: boolean, isSSR: boolean ) { - this.effect = new ReactiveEffect(getter, () => { - if (!this._dirty) { + this.effect = new ReactiveEffect(getter, (_c) => { + if (_c) { + this._computedsToAskDirty.push(_c) + } + else if (!this._dirty) { this._dirty = true - triggerRefValue(this) + triggerRefValue(this, this) } }) this.effect.computed = this @@ -55,11 +59,24 @@ export class ComputedRefImpl { get value() { // the computed ref may get wrapped by other proxies e.g. readonly() #3376 const self = toRaw(this) + if (!self._dirty) { + for (const computedToAskDirty of self._computedsToAskDirty) { + computedToAskDirty.value + if (self._dirty) { + break + } + } + } trackRefValue(self) if (self._dirty || !self._cacheable) { + const newValue = self.effect.run()! + if (self._value !== newValue) { + triggerRefValue(this, undefined) + } + self._value = newValue self._dirty = false - self._value = self.effect.run()! } + self._computedsToAskDirty.length = 0 return self._value } diff --git a/packages/reactivity/src/deferredComputed.ts b/packages/reactivity/src/deferredComputed.ts index a23122046a4..07b4a496bf4 100644 --- a/packages/reactivity/src/deferredComputed.ts +++ b/packages/reactivity/src/deferredComputed.ts @@ -38,7 +38,7 @@ class DeferredComputedRefImpl { let compareTarget: any let hasCompareTarget = false let scheduled = false - this.effect = new ReactiveEffect(getter, (computedTrigger?: boolean) => { + this.effect = new ReactiveEffect(getter, (_c, computedTrigger?: boolean) => { if (this.dep) { if (computedTrigger) { compareTarget = this._value @@ -49,7 +49,7 @@ class DeferredComputedRefImpl { hasCompareTarget = false scheduler(() => { if (this.effect.active && this._get() !== valueToCompare) { - triggerRefValue(this) + triggerRefValue(this, undefined) } scheduled = false }) @@ -59,7 +59,7 @@ class DeferredComputedRefImpl { // deferred to be triggered in scheduler. for (const e of this.dep) { if (e.computed instanceof DeferredComputedRefImpl) { - e.scheduler!(true /* computedTrigger */) + e.scheduler!(undefined, true /* computedTrigger */) } } } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index d4a34edfef4..4cfb9ec8dda 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -30,7 +30,7 @@ export let trackOpBit = 1 */ const maxMarkerBits = 30 -export type EffectScheduler = (...args: any[]) => any +export type EffectScheduler = (computedToAskDirty: ComputedRefImpl | undefined, ...args: any[]) => any export type DebuggerEvent = { effect: ReactiveEffect @@ -305,6 +305,7 @@ export function trackEffects( export function trigger( target: object, type: TriggerOpTypes, + computedToAskDirty: ComputedRefImpl | undefined, key?: unknown, newValue?: unknown, oldValue?: unknown, @@ -370,9 +371,9 @@ export function trigger( if (deps.length === 1) { if (deps[0]) { if (__DEV__) { - triggerEffects(deps[0], eventInfo) + triggerEffects(deps[0], computedToAskDirty, eventInfo) } else { - triggerEffects(deps[0]) + triggerEffects(deps[0], computedToAskDirty) } } } else { @@ -383,33 +384,28 @@ export function trigger( } } if (__DEV__) { - triggerEffects(createDep(effects), eventInfo) + triggerEffects(createDep(effects), computedToAskDirty, eventInfo) } else { - triggerEffects(createDep(effects)) + triggerEffects(createDep(effects), computedToAskDirty) } } } export function triggerEffects( dep: Dep | ReactiveEffect[], + computedToAskDirty: ComputedRefImpl | undefined, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { // spread into array for stabilization const effects = isArray(dep) ? dep : [...dep] for (const effect of effects) { - if (effect.computed) { - triggerEffect(effect, debuggerEventExtraInfo) - } - } - for (const effect of effects) { - if (!effect.computed) { - triggerEffect(effect, debuggerEventExtraInfo) - } + triggerEffect(effect, computedToAskDirty, debuggerEventExtraInfo) } } function triggerEffect( effect: ReactiveEffect, + computedToAskDirty: ComputedRefImpl | undefined, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { if (effect !== activeEffect || effect.allowRecurse) { @@ -417,7 +413,7 @@ function triggerEffect( effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) } if (effect.scheduler) { - effect.scheduler() + effect.scheduler(computedToAskDirty) } else { effect.run() } diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 5dd31a9f8ca..b131ce58c58 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -18,6 +18,7 @@ import { import type { ShallowReactiveMarker } from './reactive' import { CollectionTypes } from './collectionHandlers' import { createDep, Dep } from './dep' +import { ComputedRefImpl } from './computed' declare const RefSymbol: unique symbol export declare const RawSymbol: unique symbol @@ -52,19 +53,19 @@ export function trackRefValue(ref: RefBase) { } } -export function triggerRefValue(ref: RefBase, newVal?: any) { +export function triggerRefValue(ref: RefBase, computedToAskDirty: ComputedRefImpl | undefined, newVal?: any) { ref = toRaw(ref) const dep = ref.dep if (dep) { if (__DEV__) { - triggerEffects(dep, { + triggerEffects(dep, computedToAskDirty, { target: ref, type: TriggerOpTypes.SET, key: 'value', newValue: newVal }) } else { - triggerEffects(dep) + triggerEffects(dep, computedToAskDirty) } } } @@ -155,7 +156,7 @@ class RefImpl { if (hasChanged(newVal, this._rawValue)) { this._rawValue = newVal this._value = useDirectValue ? newVal : toReactive(newVal) - triggerRefValue(this, newVal) + triggerRefValue(this, undefined, newVal) } } } @@ -282,7 +283,7 @@ class CustomRefImpl { constructor(factory: CustomRefFactory) { const { get, set } = factory( () => trackRefValue(this), - () => triggerRefValue(this) + () => triggerRefValue(this, undefined) ) this._get = get this._set = set diff --git a/packages/runtime-core/src/compat/global.ts b/packages/runtime-core/src/compat/global.ts index 9f1a6d1cd12..1cd9e886984 100644 --- a/packages/runtime-core/src/compat/global.ts +++ b/packages/runtime-core/src/compat/global.ts @@ -648,7 +648,7 @@ function defineReactiveSimple(obj: any, key: string, val: any) { }, set(newVal) { val = isObject(newVal) ? reactive(newVal) : newVal - trigger(obj, TriggerOpTypes.SET, key, newVal) + trigger(obj, TriggerOpTypes.SET, undefined, key, newVal) } }) } diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index 186384d5b82..b1179647871 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -333,7 +333,7 @@ export function updateProps( // trigger updates for $attrs in case it's used in component slots if (hasAttrsChanged) { - trigger(instance, TriggerOpTypes.SET, '$attrs') + trigger(instance, TriggerOpTypes.SET, undefined, '$attrs') } if (__DEV__) { From 208e59c9d9e44765b2bd6eccbf7f6f395ae18979 Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Fri, 13 May 2022 19:38:25 +0800 Subject: [PATCH 002/110] fix: computed effect dons't transfer --- packages/reactivity/src/computed.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 1d177afbcff..e7bd253ea49 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -45,6 +45,7 @@ export class ComputedRefImpl { this.effect = new ReactiveEffect(getter, (_c) => { if (_c) { this._computedsToAskDirty.push(_c) + triggerRefValue(this, this) } else if (!this._dirty) { this._dirty = true From 4a3e554d56a0e35c709801a087aadaaf956cd380 Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Fri, 13 May 2022 21:11:51 +0800 Subject: [PATCH 003/110] fix: revert effects trigger order --- packages/reactivity/src/effect.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 4cfb9ec8dda..14c2339478c 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -399,7 +399,14 @@ export function triggerEffects( // spread into array for stabilization const effects = isArray(dep) ? dep : [...dep] for (const effect of effects) { - triggerEffect(effect, computedToAskDirty, debuggerEventExtraInfo) + if (effect.computed) { + triggerEffect(effect, computedToAskDirty, debuggerEventExtraInfo) + } + } + for (const effect of effects) { + if (!effect.computed) { + triggerEffect(effect, computedToAskDirty, debuggerEventExtraInfo) + } } } From ed871c2cb95b493cbee399dce45996df1a2dc778 Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Sat, 14 May 2022 04:57:41 +0800 Subject: [PATCH 004/110] refactor: remove `computedToAskDirty` arg from `trigger()` --- packages/reactivity/src/baseHandlers.ts | 4 ++-- packages/reactivity/src/collectionHandlers.ts | 8 ++++---- packages/reactivity/src/effect.ts | 9 ++++----- packages/runtime-core/src/compat/global.ts | 2 +- packages/runtime-core/src/componentProps.ts | 2 +- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/packages/reactivity/src/baseHandlers.ts b/packages/reactivity/src/baseHandlers.ts index 43bd425d2a7..45ecfa6d38a 100644 --- a/packages/reactivity/src/baseHandlers.ts +++ b/packages/reactivity/src/baseHandlers.ts @@ -190,9 +190,9 @@ function createSetter(shallow = false) { // don't trigger if target is something up in the prototype chain of original if (target === toRaw(receiver)) { if (!hadKey) { - trigger(target, TriggerOpTypes.ADD, undefined, key, value) + trigger(target, TriggerOpTypes.ADD, key, value) } else if (hasChanged(value, oldValue)) { - trigger(target, TriggerOpTypes.SET, undefined, key, value, oldValue) + trigger(target, TriggerOpTypes.SET, key, value, oldValue) } } return result diff --git a/packages/reactivity/src/collectionHandlers.ts b/packages/reactivity/src/collectionHandlers.ts index 651cb1e289f..381bbad6c28 100644 --- a/packages/reactivity/src/collectionHandlers.ts +++ b/packages/reactivity/src/collectionHandlers.ts @@ -73,7 +73,7 @@ function add(this: SetTypes, value: unknown) { const hadKey = proto.has.call(target, value) if (!hadKey) { target.add(value) - trigger(target, TriggerOpTypes.ADD, undefined, value, value) + trigger(target, TriggerOpTypes.ADD, value, value) } return this } @@ -94,9 +94,9 @@ function set(this: MapTypes, key: unknown, value: unknown) { const oldValue = get.call(target, key) target.set(key, value) if (!hadKey) { - trigger(target, TriggerOpTypes.ADD, undefined, key, value) + trigger(target, TriggerOpTypes.ADD, key, value) } else if (hasChanged(value, oldValue)) { - trigger(target, TriggerOpTypes.SET, undefined, key, value, oldValue) + trigger(target, TriggerOpTypes.SET, key, value, oldValue) } return this } @@ -116,7 +116,7 @@ function deleteEntry(this: CollectionTypes, key: unknown) { // forward the operation before queueing reactions const result = target.delete(key) if (hadKey) { - trigger(target, TriggerOpTypes.DELETE, undefined, key, undefined, oldValue) + trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue) } return result } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 14c2339478c..1ef95b31cd7 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -305,7 +305,6 @@ export function trackEffects( export function trigger( target: object, type: TriggerOpTypes, - computedToAskDirty: ComputedRefImpl | undefined, key?: unknown, newValue?: unknown, oldValue?: unknown, @@ -371,9 +370,9 @@ export function trigger( if (deps.length === 1) { if (deps[0]) { if (__DEV__) { - triggerEffects(deps[0], computedToAskDirty, eventInfo) + triggerEffects(deps[0], undefined, eventInfo) } else { - triggerEffects(deps[0], computedToAskDirty) + triggerEffects(deps[0], undefined) } } } else { @@ -384,9 +383,9 @@ export function trigger( } } if (__DEV__) { - triggerEffects(createDep(effects), computedToAskDirty, eventInfo) + triggerEffects(createDep(effects), undefined, eventInfo) } else { - triggerEffects(createDep(effects), computedToAskDirty) + triggerEffects(createDep(effects), undefined) } } } diff --git a/packages/runtime-core/src/compat/global.ts b/packages/runtime-core/src/compat/global.ts index 1cd9e886984..9f1a6d1cd12 100644 --- a/packages/runtime-core/src/compat/global.ts +++ b/packages/runtime-core/src/compat/global.ts @@ -648,7 +648,7 @@ function defineReactiveSimple(obj: any, key: string, val: any) { }, set(newVal) { val = isObject(newVal) ? reactive(newVal) : newVal - trigger(obj, TriggerOpTypes.SET, undefined, key, newVal) + trigger(obj, TriggerOpTypes.SET, key, newVal) } }) } diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index b1179647871..186384d5b82 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -333,7 +333,7 @@ export function updateProps( // trigger updates for $attrs in case it's used in component slots if (hasAttrsChanged) { - trigger(instance, TriggerOpTypes.SET, undefined, '$attrs') + trigger(instance, TriggerOpTypes.SET, '$attrs') } if (__DEV__) { From fbf214ab21f25b3f407d549dd79ff3e46faf83cf Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Sat, 14 May 2022 05:09:22 +0800 Subject: [PATCH 005/110] chore: use `hasChanged()` instead of `!==` --- packages/reactivity/src/computed.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index e7bd253ea49..4888c1c7d01 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -1,6 +1,6 @@ import { DebuggerOptions, ReactiveEffect } from './effect' import { Ref, trackRefValue, triggerRefValue } from './ref' -import { isFunction, NOOP } from '@vue/shared' +import { hasChanged, isFunction, NOOP } from '@vue/shared' import { ReactiveFlags, toRaw } from './reactive' import { Dep } from './dep' @@ -71,7 +71,7 @@ export class ComputedRefImpl { trackRefValue(self) if (self._dirty || !self._cacheable) { const newValue = self.effect.run()! - if (self._value !== newValue) { + if (hasChanged(self._value, newValue)) { triggerRefValue(this, undefined) } self._value = newValue From 399f1553c889a8b7486191ee56ce961814f26b9d Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Sun, 15 May 2022 07:37:45 +0800 Subject: [PATCH 006/110] perf: reduce `triggerRefValue()` triggered --- packages/reactivity/src/computed.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 4888c1c7d01..bc78fc2a54b 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -43,12 +43,13 @@ export class ComputedRefImpl { isSSR: boolean ) { this.effect = new ReactiveEffect(getter, (_c) => { - if (_c) { - this._computedsToAskDirty.push(_c) - triggerRefValue(this, this) - } - else if (!this._dirty) { - this._dirty = true + if (!this._dirty) { + if (_c) { + this._computedsToAskDirty.push(_c) + } + else { + this._dirty = true + } triggerRefValue(this, this) } }) From 0e0c44e74f51037253c02c7186adbffb49cc3a40 Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Wed, 11 Jan 2023 13:47:36 +0800 Subject: [PATCH 007/110] perf: avoid duplicate trigger effect to deps --- packages/reactivity/src/computed.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index bc78fc2a54b..c29bf83f1d8 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -36,21 +36,25 @@ export class ComputedRefImpl { public _computedsToAskDirty: ComputedRefImpl[] = [] public _cacheable: boolean + private _triggeredAfterLastEffect = false + constructor( getter: ComputedGetter, private readonly _setter: ComputedSetter, isReadonly: boolean, isSSR: boolean ) { - this.effect = new ReactiveEffect(getter, (_c) => { + this.effect = new ReactiveEffect(getter, _c => { if (!this._dirty) { if (_c) { this._computedsToAskDirty.push(_c) - } - else { + } else { this._dirty = true } - triggerRefValue(this, this) + if (!this._triggeredAfterLastEffect) { + this._triggeredAfterLastEffect = true + triggerRefValue(this, this) + } } }) this.effect.computed = this @@ -77,6 +81,7 @@ export class ComputedRefImpl { } self._value = newValue self._dirty = false + self._triggeredAfterLastEffect = false } self._computedsToAskDirty.length = 0 return self._value From 7768d43175ca58dd7bb8eec00a3db3f13f12ef45 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 2 Feb 2023 10:27:15 +0000 Subject: [PATCH 008/110] fix: avoid track unrelated effects --- packages/reactivity/src/computed.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index c29bf83f1d8..9cae1b26e3a 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -1,4 +1,9 @@ -import { DebuggerOptions, ReactiveEffect } from './effect' +import { + DebuggerOptions, + pauseTracking, + ReactiveEffect, + resetTracking +} from './effect' import { Ref, trackRefValue, triggerRefValue } from './ref' import { hasChanged, isFunction, NOOP } from '@vue/shared' import { ReactiveFlags, toRaw } from './reactive' @@ -65,19 +70,21 @@ export class ComputedRefImpl { get value() { // the computed ref may get wrapped by other proxies e.g. readonly() #3376 const self = toRaw(this) - if (!self._dirty) { + if (!self._dirty && self._computedsToAskDirty.length) { + pauseTracking() for (const computedToAskDirty of self._computedsToAskDirty) { computedToAskDirty.value if (self._dirty) { break } } + resetTracking() } trackRefValue(self) if (self._dirty || !self._cacheable) { const newValue = self.effect.run()! if (hasChanged(self._value, newValue)) { - triggerRefValue(this, undefined) + triggerRefValue(self, undefined) } self._value = newValue self._dirty = false From a353eac19b8636a13b3ab763292acd79bf162f5f Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 2 Feb 2023 10:31:02 +0000 Subject: [PATCH 009/110] chore: make `_computedsToAskDirty` private --- packages/reactivity/src/computed.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 9cae1b26e3a..afb7aa70d67 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -38,9 +38,9 @@ export class ComputedRefImpl { public readonly [ReactiveFlags.IS_READONLY]: boolean = false public _dirty = true - public _computedsToAskDirty: ComputedRefImpl[] = [] public _cacheable: boolean + private _computedsToAskDirty: ComputedRefImpl[] = [] private _triggeredAfterLastEffect = false constructor( From 014dcd5aa45a62927a96424d522d36255b741575 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 2 Feb 2023 10:43:11 +0000 Subject: [PATCH 010/110] fix: jest -> vitest --- packages/reactivity/__tests__/computed.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index 9169d75207c..b7ce4717cdc 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -292,8 +292,8 @@ describe('reactivity/computed', () => { }) it('chained computed value on-demand trigger', () => { - const c1Spy = jest.fn() - const c2Spy = jest.fn() + const c1Spy = vi.fn() + const c2Spy = vi.fn() const src = ref(0) const c1 = computed(() => { From eb0f8fae3891dbd841f3c59adbae51fc88a34e8f Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Wed, 5 Apr 2023 23:51:49 +0800 Subject: [PATCH 011/110] fix: urgent assessment edge case --- .../reactivity/__tests__/computed.spec.ts | 27 +++++++++++++++++++ packages/reactivity/src/computed.ts | 7 +++++ 2 files changed, 34 insertions(+) diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index b7ce4717cdc..76e2c1066b2 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -341,4 +341,31 @@ describe('reactivity/computed', () => { expect(c1Spy).toHaveBeenCalledTimes(104) expect(c2Spy).toHaveBeenCalledTimes(2) }) + + it('chained computed value urgent assessment edge case', () => { + const cSpy = vi.fn() + + const a = ref({ + v: 1 + }) + const b = computed(() => { + return a.value + }) + const c = computed(() => { + cSpy() + return b.value?.v + }) + const d = computed(() => { + if (b.value) { + return c.value + } + return 0 + }) + + d.value + a.value!.v = 2 + a.value = null + d.value + expect(cSpy).toHaveBeenCalledTimes(1) + }) }) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index afb7aa70d67..5a92b42624e 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -72,6 +72,13 @@ export class ComputedRefImpl { const self = toRaw(this) if (!self._dirty && self._computedsToAskDirty.length) { pauseTracking() + if (self._computedsToAskDirty.length >= 2) { + self._computedsToAskDirty = self._computedsToAskDirty.sort((a, b) => { + const aIndex = self.effect.deps.indexOf(a.dep!) + const bIndex = self.effect.deps.indexOf(b.dep!) + return aIndex - bIndex + }) + } for (const computedToAskDirty of self._computedsToAskDirty) { computedToAskDirty.value if (self._dirty) { From f3a007124d93b9a369692e545c5b9378e08736dd Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Thu, 6 Apr 2023 02:19:38 +0800 Subject: [PATCH 012/110] feat: more purposeful test --- .../reactivity/__tests__/computed.spec.ts | 57 +++++-------------- 1 file changed, 14 insertions(+), 43 deletions(-) diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index 76e2c1066b2..c18e4367fa1 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -292,54 +292,25 @@ describe('reactivity/computed', () => { }) it('chained computed value on-demand trigger', () => { - const c1Spy = vi.fn() - const c2Spy = vi.fn() + const minSpy = vi.fn() + const hourSpy = vi.fn() - const src = ref(0) - const c1 = computed(() => { - c1Spy() - return src.value < 5 + const sec = ref(0) + const min = computed(() => { + minSpy() + return Math.floor(sec.value / 60) }) - const c2 = computed(() => { - c2Spy() - return c1.value ? '< 5' : '>= 5' + const hour = computed(() => { + hourSpy() + return Math.floor(min.value / 60) }) - expect(c1Spy).toHaveBeenCalledTimes(0) - expect(c2Spy).toHaveBeenCalledTimes(0) - - expect(src.value).toBe(0) - expect(c2.value).toBe('< 5') - expect(c1Spy).toHaveBeenCalledTimes(1) - expect(c2Spy).toHaveBeenCalledTimes(1) - - src.value++ - expect(c2.value).toBe('< 5') - expect(c1Spy).toHaveBeenCalledTimes(2) - expect(c2Spy).toHaveBeenCalledTimes(1) - - for (let i = 0; i < 10; i++) { - src.value++ + for (sec.value = 0; sec.value < 1000; sec.value++) { + hour.value } - expect(src.value).toBe(11) - expect(c2.value).toBe('>= 5') - expect(c1Spy).toHaveBeenCalledTimes(3) - expect(c2Spy).toHaveBeenCalledTimes(2) - - src.value++ - expect(src.value).toBe(12) - expect(c2.value).toBe('>= 5') - expect(c1Spy).toHaveBeenCalledTimes(4) - expect(c2Spy).toHaveBeenCalledTimes(2) - - for (let i = 0; i < 100; i++) { - src.value++ - c2.value - } - expect(src.value).toBe(112) - expect(c2.value).toBe('>= 5') - expect(c1Spy).toHaveBeenCalledTimes(104) - expect(c2Spy).toHaveBeenCalledTimes(2) + + expect(minSpy).toHaveBeenCalledTimes(1000) + expect(hourSpy).toHaveBeenCalledTimes(17) }) it('chained computed value urgent assessment edge case', () => { From 998c400cad7dcbe44dabc2b223ab99c0557f5106 Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Thu, 6 Apr 2023 16:26:39 +0800 Subject: [PATCH 013/110] chore: add test for effect() on-demand trigger --- .../reactivity/__tests__/computed.spec.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index c18e4367fa1..988bead0cce 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -313,6 +313,34 @@ describe('reactivity/computed', () => { expect(hourSpy).toHaveBeenCalledTimes(17) }) + it('effect callback on-demand trigger', () => { + const minSpy = vi.fn() + const hourSpy = vi.fn() + const effectSpy = vi.fn() + + const sec = ref(0) + const min = computed(() => { + minSpy() + return Math.floor(sec.value / 60) + }) + const hour = computed(() => { + hourSpy() + return Math.floor(min.value / 60) + }) + + effect(() => { + effectSpy() + min.value + hour.value + }) + + for (sec.value = 0; sec.value < 1000; sec.value++) {} + + expect(minSpy).toHaveBeenCalledTimes(1001) + expect(hourSpy).toHaveBeenCalledTimes(17) + expect(effectSpy).toHaveBeenCalledTimes(1001) + }) + it('chained computed value urgent assessment edge case', () => { const cSpy = vi.fn() From b5527a17fa0374de2e2f39175788af30db1f594f Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Thu, 6 Apr 2023 18:15:15 +0800 Subject: [PATCH 014/110] Revert "chore: add test for effect() on-demand trigger" This reverts commit 998c400cad7dcbe44dabc2b223ab99c0557f5106. --- .../reactivity/__tests__/computed.spec.ts | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index 988bead0cce..c18e4367fa1 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -313,34 +313,6 @@ describe('reactivity/computed', () => { expect(hourSpy).toHaveBeenCalledTimes(17) }) - it('effect callback on-demand trigger', () => { - const minSpy = vi.fn() - const hourSpy = vi.fn() - const effectSpy = vi.fn() - - const sec = ref(0) - const min = computed(() => { - minSpy() - return Math.floor(sec.value / 60) - }) - const hour = computed(() => { - hourSpy() - return Math.floor(min.value / 60) - }) - - effect(() => { - effectSpy() - min.value - hour.value - }) - - for (sec.value = 0; sec.value < 1000; sec.value++) {} - - expect(minSpy).toHaveBeenCalledTimes(1001) - expect(hourSpy).toHaveBeenCalledTimes(17) - expect(effectSpy).toHaveBeenCalledTimes(1001) - }) - it('chained computed value urgent assessment edge case', () => { const cSpy = vi.fn() From 3e0c2af3c00a8d74edabe8e5d32c1deae889d205 Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Thu, 6 Apr 2023 20:23:24 +0800 Subject: [PATCH 015/110] wip: on-demand effect() --- .../reactivity/__tests__/computed.spec.ts | 22 ++++++ packages/reactivity/src/effect.ts | 73 ++++++++++++++++++- 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index c18e4367fa1..2091ae1dc93 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -313,6 +313,28 @@ describe('reactivity/computed', () => { expect(hourSpy).toHaveBeenCalledTimes(17) }) + it('effect callback on-demand trigger', () => { + const effectSpy = vi.fn() + + const sec = ref(0) + const min = computed(() => { + return Math.floor(sec.value / 60) + }) + const hour = computed(() => { + return Math.floor(min.value / 60) + }) + + effect(() => { + effectSpy() + min.value + hour.value + }) + + for (sec.value = 0; sec.value < 1000; sec.value++) {} + + expect(effectSpy).toHaveBeenCalledTimes(17) + }) + it('chained computed value urgent assessment edge case', () => { const cSpy = vi.fn() diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 1ef95b31cd7..a4d675ab411 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -30,7 +30,10 @@ export let trackOpBit = 1 */ const maxMarkerBits = 30 -export type EffectScheduler = (computedToAskDirty: ComputedRefImpl | undefined, ...args: any[]) => any +export type EffectScheduler = ( + computedToAskDirty: ComputedRefImpl | undefined, + ...args: any[] +) => any export type DebuggerEvent = { effect: ReactiveEffect @@ -185,7 +188,26 @@ export function effect( fn = (fn as ReactiveEffectRunner).effect.fn } - const _effect = new ReactiveEffect(fn) + let _dirty = false + let _triggeredAfterLastEffect = false + let _computedsToAskDirty: ComputedRefImpl[] = [] + + const _effect = new ReactiveEffect(fn, _c => { + if (!_dirty) { + if (!_c) { + _dirty = true + } + if (state === EffectState.TRACKING) { + if (_c) { + _computedsToAskDirty.push(_c) + } + if (!_triggeredAfterLastEffect) { + _triggeredAfterLastEffect = true + schedulerCallbacks.push(cb) + } + } + } + }) if (options) { extend(_effect, options) if (options.scope) recordEffectScope(_effect, options.scope) @@ -196,6 +218,32 @@ export function effect( const runner = _effect.run.bind(_effect) as ReactiveEffectRunner runner.effect = _effect return runner + + function cb() { + if (!_dirty && _computedsToAskDirty.length) { + pauseTracking() + if (_computedsToAskDirty.length >= 2) { + _computedsToAskDirty = _computedsToAskDirty.sort((a, b) => { + const aIndex = _effect.deps.indexOf(a.dep!) + const bIndex = _effect.deps.indexOf(b.dep!) + return aIndex - bIndex + }) + } + for (const computedToAskDirty of _computedsToAskDirty) { + computedToAskDirty.value + if (_dirty) { + break + } + } + resetTracking() + } + if (_dirty) { + _dirty = false + _effect.run() + } + _computedsToAskDirty.length = 0 + _triggeredAfterLastEffect = false + } } /** @@ -409,11 +457,26 @@ export function triggerEffects( } } +const schedulerCallbacks: (() => void)[] = [] + +const enum EffectState { + NOT_TRACKING = 0, + TRACKING = 1, + POST_SCHEDULER = 2 +} + +let state = EffectState.NOT_TRACKING + function triggerEffect( effect: ReactiveEffect, computedToAskDirty: ComputedRefImpl | undefined, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { + let isRootEffect = false + if (state === EffectState.NOT_TRACKING) { + state = EffectState.TRACKING + isRootEffect = true + } if (effect !== activeEffect || effect.allowRecurse) { if (__DEV__ && effect.onTrigger) { effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) @@ -424,6 +487,12 @@ function triggerEffect( effect.run() } } + if (isRootEffect) { + state = EffectState.POST_SCHEDULER + schedulerCallbacks.forEach(cb => cb()) + schedulerCallbacks.length = 0 + state = EffectState.NOT_TRACKING + } } export function getDepFromReactive(object: any, key: string | number | symbol) { From 62dc5ee1d132fdbb711a1ae9736293c75593f82a Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Fri, 7 Apr 2023 19:05:10 +0800 Subject: [PATCH 016/110] fix: ref newVal arg incorrect --- packages/reactivity/src/ref.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index b131ce58c58..834071a7a5b 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -53,7 +53,11 @@ export function trackRefValue(ref: RefBase) { } } -export function triggerRefValue(ref: RefBase, computedToAskDirty: ComputedRefImpl | undefined, newVal?: any) { +export function triggerRefValue( + ref: RefBase, + computedToAskDirty: ComputedRefImpl | undefined, + newVal?: any +) { ref = toRaw(ref) const dep = ref.dep if (dep) { @@ -187,7 +191,7 @@ class RefImpl { * @see {@link https://vuejs.org/api/reactivity-advanced.html#triggerref} */ export function triggerRef(ref: Ref) { - triggerRefValue(ref, __DEV__ ? ref.value : void 0) + triggerRefValue(ref, undefined, __DEV__ ? ref.value : void 0) } export type MaybeRef = T | Ref From 889511719721b754e774b42f1bc3e43391a30f35 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Fri, 7 Apr 2023 20:18:53 +0800 Subject: [PATCH 017/110] release: v3.3.0-alpha.9 --- CHANGELOG.md | 38 ++++++++++++++++ package.json | 2 +- packages/compiler-core/package.json | 4 +- packages/compiler-dom/package.json | 6 +-- packages/compiler-sfc/package.json | 12 ++--- packages/compiler-ssr/package.json | 6 +-- packages/dts-test/package.json | 2 +- packages/reactivity-transform/package.json | 6 +-- packages/reactivity/package.json | 4 +- packages/runtime-core/package.json | 6 +-- packages/runtime-dom/package.json | 6 +-- packages/runtime-test/package.json | 6 +-- packages/server-renderer/package.json | 8 ++-- packages/sfc-playground/package.json | 2 +- packages/shared/package.json | 2 +- packages/size-check/package.json | 2 +- packages/template-explorer/package.json | 2 +- packages/vue-compat/package.json | 4 +- packages/vue/package.json | 12 ++--- pnpm-lock.yaml | 52 +++++++++++----------- 20 files changed, 110 insertions(+), 72 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c7ba846f5c..5841b02aaa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,41 @@ +# [3.3.0-alpha.9](https://github.com/vuejs/core/compare/v3.3.0-alpha.8...v3.3.0-alpha.9) (2023-04-07) + + +### Bug Fixes + +* avoid track unrelated effects ([7768d43](https://github.com/vuejs/core/commit/7768d43175ca58dd7bb8eec00a3db3f13f12ef45)) +* **compiler-sfc:** skip empty `defineOptions` and support TypeScript type assertions ([#8028](https://github.com/vuejs/core/issues/8028)) ([9557529](https://github.com/vuejs/core/commit/955752951e1d31b90d817bd20830fe3f89018771)) +* **compiler-ssr:** disable v-once transform in ssr vdom fallback branch ([05f94cf](https://github.com/vuejs/core/commit/05f94cf7b01dd05ed7d3170916a38b175d5df292)), closes [#7644](https://github.com/vuejs/core/issues/7644) +* computed effect dons't transfer ([208e59c](https://github.com/vuejs/core/commit/208e59c9d9e44765b2bd6eccbf7f6f395ae18979)) +* jest -> vitest ([014dcd5](https://github.com/vuejs/core/commit/014dcd5aa45a62927a96424d522d36255b741575)) +* ref newVal arg incorrect ([62dc5ee](https://github.com/vuejs/core/commit/62dc5ee1d132fdbb711a1ae9736293c75593f82a)) +* revert effects trigger order ([4a3e554](https://github.com/vuejs/core/commit/4a3e554d56a0e35c709801a087aadaaf956cd380)) +* **types:** improve defineProps return type with generic arguments ([91a931a](https://github.com/vuejs/core/commit/91a931ae8707b8d43f10216e1ce8e18b12158f99)) +* **types:** more public type argument order fix ([af563bf](https://github.com/vuejs/core/commit/af563bf428200367b6f5bb7944f690c85d810202)) +* **types:** retain type parameters order for public types ([bdf557f](https://github.com/vuejs/core/commit/bdf557f6f233c039fff8007b1b16aec00c4e68aa)) +* urgent assessment edge case ([eb0f8fa](https://github.com/vuejs/core/commit/eb0f8fae3891dbd841f3c59adbae51fc88a34e8f)) + + +### Features + +* **app:** app.runWithContext() ([#7451](https://github.com/vuejs/core/issues/7451)) ([869f3fb](https://github.com/vuejs/core/commit/869f3fb93e61400be4fd925e0850c2b1564749e2)) +* more purposeful test ([f3a0071](https://github.com/vuejs/core/commit/f3a007124d93b9a369692e545c5b9378e08736dd)) + + +### Performance Improvements + +* avoid duplicate trigger effect to deps ([0e0c44e](https://github.com/vuejs/core/commit/0e0c44e74f51037253c02c7186adbffb49cc3a40)) +* calculate embedded `computed()` on-demand ([f485ea0](https://github.com/vuejs/core/commit/f485ea01a9d1d7aca3bdc61b12f768de91c8d583)) +* reduce `triggerRefValue()` triggered ([399f155](https://github.com/vuejs/core/commit/399f1553c889a8b7486191ee56ce961814f26b9d)) + + +### Reverts + +* Revert "chore: add test for effect() on-demand trigger" ([b5527a1](https://github.com/vuejs/core/commit/b5527a17fa0374de2e2f39175788af30db1f594f)) +* Revert "chore: remove unused args passed to ssrRender" ([b117b88](https://github.com/vuejs/core/commit/b117b8844881a732a021432066230ff2215049ea)) + + + # [3.3.0-alpha.8](https://github.com/vuejs/core/compare/v3.3.0-alpha.7...v3.3.0-alpha.8) (2023-04-04) diff --git a/package.json b/package.json index 2f6db271c76..7da3028e73a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "3.3.0-alpha.8", + "version": "3.3.0-alpha.9", "packageManager": "pnpm@7.26.0", "type": "module", "scripts": { diff --git a/packages/compiler-core/package.json b/packages/compiler-core/package.json index 152fc9ce6cf..a7c20e8e1b8 100644 --- a/packages/compiler-core/package.json +++ b/packages/compiler-core/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-core", - "version": "3.3.0-alpha.8", + "version": "3.3.0-alpha.9", "description": "@vue/compiler-core", "main": "index.js", "module": "dist/compiler-core.esm-bundler.js", @@ -33,7 +33,7 @@ "homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-core#readme", "dependencies": { "@babel/parser": "^7.21.3", - "@vue/shared": "3.3.0-alpha.8", + "@vue/shared": "3.3.0-alpha.9", "estree-walker": "^2.0.2", "source-map": "^0.6.1" }, diff --git a/packages/compiler-dom/package.json b/packages/compiler-dom/package.json index 007568fec7c..a73978cec7e 100644 --- a/packages/compiler-dom/package.json +++ b/packages/compiler-dom/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-dom", - "version": "3.3.0-alpha.8", + "version": "3.3.0-alpha.9", "description": "@vue/compiler-dom", "main": "index.js", "module": "dist/compiler-dom.esm-bundler.js", @@ -37,7 +37,7 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-dom#readme", "dependencies": { - "@vue/shared": "3.3.0-alpha.8", - "@vue/compiler-core": "3.3.0-alpha.8" + "@vue/shared": "3.3.0-alpha.9", + "@vue/compiler-core": "3.3.0-alpha.9" } } diff --git a/packages/compiler-sfc/package.json b/packages/compiler-sfc/package.json index ce831788a9a..8c97d3b3b9b 100644 --- a/packages/compiler-sfc/package.json +++ b/packages/compiler-sfc/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-sfc", - "version": "3.3.0-alpha.8", + "version": "3.3.0-alpha.9", "description": "@vue/compiler-sfc", "main": "dist/compiler-sfc.cjs.js", "module": "dist/compiler-sfc.esm-browser.js", @@ -33,11 +33,11 @@ "homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-sfc#readme", "dependencies": { "@babel/parser": "^7.20.15", - "@vue/compiler-core": "3.3.0-alpha.8", - "@vue/compiler-dom": "3.3.0-alpha.8", - "@vue/compiler-ssr": "3.3.0-alpha.8", - "@vue/reactivity-transform": "3.3.0-alpha.8", - "@vue/shared": "3.3.0-alpha.8", + "@vue/compiler-core": "3.3.0-alpha.9", + "@vue/compiler-dom": "3.3.0-alpha.9", + "@vue/compiler-ssr": "3.3.0-alpha.9", + "@vue/reactivity-transform": "3.3.0-alpha.9", + "@vue/shared": "3.3.0-alpha.9", "estree-walker": "^2.0.2", "magic-string": "^0.30.0", "postcss": "^8.1.10", diff --git a/packages/compiler-ssr/package.json b/packages/compiler-ssr/package.json index b692b9fdd0f..b1809422b1d 100644 --- a/packages/compiler-ssr/package.json +++ b/packages/compiler-ssr/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-ssr", - "version": "3.3.0-alpha.8", + "version": "3.3.0-alpha.9", "description": "@vue/compiler-ssr", "main": "dist/compiler-ssr.cjs.js", "types": "dist/compiler-ssr.d.ts", @@ -28,7 +28,7 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-ssr#readme", "dependencies": { - "@vue/shared": "3.3.0-alpha.8", - "@vue/compiler-dom": "3.3.0-alpha.8" + "@vue/shared": "3.3.0-alpha.9", + "@vue/compiler-dom": "3.3.0-alpha.9" } } diff --git a/packages/dts-test/package.json b/packages/dts-test/package.json index 5d5acc0989d..833b92e5008 100644 --- a/packages/dts-test/package.json +++ b/packages/dts-test/package.json @@ -4,5 +4,5 @@ "dependencies": { "vue": "workspace:*" }, - "version": "3.3.0-alpha.8" + "version": "3.3.0-alpha.9" } diff --git a/packages/reactivity-transform/package.json b/packages/reactivity-transform/package.json index 29f37dd6a49..5045a40a818 100644 --- a/packages/reactivity-transform/package.json +++ b/packages/reactivity-transform/package.json @@ -1,6 +1,6 @@ { "name": "@vue/reactivity-transform", - "version": "3.3.0-alpha.8", + "version": "3.3.0-alpha.9", "description": "@vue/reactivity-transform", "main": "dist/reactivity-transform.cjs.js", "files": [ @@ -29,8 +29,8 @@ "homepage": "https://github.com/vuejs/core/tree/dev/packages/reactivity-transform#readme", "dependencies": { "@babel/parser": "^7.20.15", - "@vue/compiler-core": "3.3.0-alpha.8", - "@vue/shared": "3.3.0-alpha.8", + "@vue/compiler-core": "3.3.0-alpha.9", + "@vue/shared": "3.3.0-alpha.9", "estree-walker": "^2.0.2", "magic-string": "^0.30.0" }, diff --git a/packages/reactivity/package.json b/packages/reactivity/package.json index e07312dce31..4c7988e82a6 100644 --- a/packages/reactivity/package.json +++ b/packages/reactivity/package.json @@ -1,6 +1,6 @@ { "name": "@vue/reactivity", - "version": "3.3.0-alpha.8", + "version": "3.3.0-alpha.9", "description": "@vue/reactivity", "main": "index.js", "module": "dist/reactivity.esm-bundler.js", @@ -36,6 +36,6 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/reactivity#readme", "dependencies": { - "@vue/shared": "3.3.0-alpha.8" + "@vue/shared": "3.3.0-alpha.9" } } diff --git a/packages/runtime-core/package.json b/packages/runtime-core/package.json index a282b8d87b4..606fef5414a 100644 --- a/packages/runtime-core/package.json +++ b/packages/runtime-core/package.json @@ -1,6 +1,6 @@ { "name": "@vue/runtime-core", - "version": "3.3.0-alpha.8", + "version": "3.3.0-alpha.9", "description": "@vue/runtime-core", "main": "index.js", "module": "dist/runtime-core.esm-bundler.js", @@ -32,7 +32,7 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/runtime-core#readme", "dependencies": { - "@vue/shared": "3.3.0-alpha.8", - "@vue/reactivity": "3.3.0-alpha.8" + "@vue/shared": "3.3.0-alpha.9", + "@vue/reactivity": "3.3.0-alpha.9" } } diff --git a/packages/runtime-dom/package.json b/packages/runtime-dom/package.json index 29cf32c048b..25da3f9657a 100644 --- a/packages/runtime-dom/package.json +++ b/packages/runtime-dom/package.json @@ -1,6 +1,6 @@ { "name": "@vue/runtime-dom", - "version": "3.3.0-alpha.8", + "version": "3.3.0-alpha.9", "description": "@vue/runtime-dom", "main": "index.js", "module": "dist/runtime-dom.esm-bundler.js", @@ -35,8 +35,8 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/runtime-dom#readme", "dependencies": { - "@vue/shared": "3.3.0-alpha.8", - "@vue/runtime-core": "3.3.0-alpha.8", + "@vue/shared": "3.3.0-alpha.9", + "@vue/runtime-core": "3.3.0-alpha.9", "csstype": "^3.1.1" } } diff --git a/packages/runtime-test/package.json b/packages/runtime-test/package.json index 4c778451a92..202161c9474 100644 --- a/packages/runtime-test/package.json +++ b/packages/runtime-test/package.json @@ -1,6 +1,6 @@ { "name": "@vue/runtime-test", - "version": "3.3.0-alpha.8", + "version": "3.3.0-alpha.9", "description": "@vue/runtime-test", "private": true, "main": "index.js", @@ -25,7 +25,7 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/runtime-test#readme", "dependencies": { - "@vue/shared": "3.3.0-alpha.8", - "@vue/runtime-core": "3.3.0-alpha.8" + "@vue/shared": "3.3.0-alpha.9", + "@vue/runtime-core": "3.3.0-alpha.9" } } diff --git a/packages/server-renderer/package.json b/packages/server-renderer/package.json index d6d43bf82f7..9f8a2f8eafd 100644 --- a/packages/server-renderer/package.json +++ b/packages/server-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@vue/server-renderer", - "version": "3.3.0-alpha.8", + "version": "3.3.0-alpha.9", "description": "@vue/server-renderer", "main": "index.js", "module": "dist/server-renderer.esm-bundler.js", @@ -32,10 +32,10 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/server-renderer#readme", "peerDependencies": { - "vue": "3.3.0-alpha.8" + "vue": "3.3.0-alpha.9" }, "dependencies": { - "@vue/shared": "3.3.0-alpha.8", - "@vue/compiler-ssr": "3.3.0-alpha.8" + "@vue/shared": "3.3.0-alpha.9", + "@vue/compiler-ssr": "3.3.0-alpha.9" } } diff --git a/packages/sfc-playground/package.json b/packages/sfc-playground/package.json index 214e81cce27..e434dd9495b 100644 --- a/packages/sfc-playground/package.json +++ b/packages/sfc-playground/package.json @@ -1,6 +1,6 @@ { "name": "@vue/sfc-playground", - "version": "3.3.0-alpha.8", + "version": "3.3.0-alpha.9", "private": true, "scripts": { "dev": "vite", diff --git a/packages/shared/package.json b/packages/shared/package.json index 727e24ee626..a7ceb93b4ae 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@vue/shared", - "version": "3.3.0-alpha.8", + "version": "3.3.0-alpha.9", "description": "internal utils shared across @vue packages", "main": "index.js", "module": "dist/shared.esm-bundler.js", diff --git a/packages/size-check/package.json b/packages/size-check/package.json index 94d49cc4e6a..34c7df41c92 100644 --- a/packages/size-check/package.json +++ b/packages/size-check/package.json @@ -1,6 +1,6 @@ { "name": "@vue/size-check", - "version": "3.3.0-alpha.8", + "version": "3.3.0-alpha.9", "private": true, "scripts": { "build": "vite build" diff --git a/packages/template-explorer/package.json b/packages/template-explorer/package.json index 7b42a4c3ea6..81852352839 100644 --- a/packages/template-explorer/package.json +++ b/packages/template-explorer/package.json @@ -1,6 +1,6 @@ { "name": "@vue/template-explorer", - "version": "3.3.0-alpha.8", + "version": "3.3.0-alpha.9", "private": true, "buildOptions": { "formats": [ diff --git a/packages/vue-compat/package.json b/packages/vue-compat/package.json index aa04139c247..5032c134cf8 100644 --- a/packages/vue-compat/package.json +++ b/packages/vue-compat/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compat", - "version": "3.3.0-alpha.8", + "version": "3.3.0-alpha.9", "description": "Vue 3 compatibility build for Vue 2", "main": "index.js", "module": "dist/vue.runtime.esm-bundler.js", @@ -43,6 +43,6 @@ "source-map": "^0.6.1" }, "peerDependencies": { - "vue": "3.3.0-alpha.8" + "vue": "3.3.0-alpha.9" } } diff --git a/packages/vue/package.json b/packages/vue/package.json index cb17e34700d..5a2e32f0e5a 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "vue", - "version": "3.3.0-alpha.8", + "version": "3.3.0-alpha.9", "description": "The progressive JavaScript framework for building modern web UI.", "main": "index.js", "module": "dist/vue.runtime.esm-bundler.js", @@ -81,10 +81,10 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/vue#readme", "dependencies": { - "@vue/shared": "3.3.0-alpha.8", - "@vue/compiler-dom": "3.3.0-alpha.8", - "@vue/runtime-dom": "3.3.0-alpha.8", - "@vue/compiler-sfc": "3.3.0-alpha.8", - "@vue/server-renderer": "3.3.0-alpha.8" + "@vue/shared": "3.3.0-alpha.9", + "@vue/compiler-dom": "3.3.0-alpha.9", + "@vue/runtime-dom": "3.3.0-alpha.9", + "@vue/compiler-sfc": "3.3.0-alpha.9", + "@vue/server-renderer": "3.3.0-alpha.9" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5206450c2af..489f7892681 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -102,7 +102,7 @@ importers: specifiers: '@babel/parser': ^7.21.3 '@babel/types': ^7.21.3 - '@vue/shared': 3.3.0-alpha.8 + '@vue/shared': 3.3.0-alpha.9 estree-walker: ^2.0.2 source-map: ^0.6.1 dependencies: @@ -115,8 +115,8 @@ importers: packages/compiler-dom: specifiers: - '@vue/compiler-core': 3.3.0-alpha.8 - '@vue/shared': 3.3.0-alpha.8 + '@vue/compiler-core': 3.3.0-alpha.9 + '@vue/shared': 3.3.0-alpha.9 dependencies: '@vue/compiler-core': link:../compiler-core '@vue/shared': link:../shared @@ -127,12 +127,12 @@ importers: '@babel/types': ^7.21.3 '@types/estree': ^0.0.48 '@types/lru-cache': ^5.1.0 - '@vue/compiler-core': 3.3.0-alpha.8 - '@vue/compiler-dom': 3.3.0-alpha.8 - '@vue/compiler-ssr': 3.3.0-alpha.8 + '@vue/compiler-core': 3.3.0-alpha.9 + '@vue/compiler-dom': 3.3.0-alpha.9 + '@vue/compiler-ssr': 3.3.0-alpha.9 '@vue/consolidate': ^0.17.3 - '@vue/reactivity-transform': 3.3.0-alpha.8 - '@vue/shared': 3.3.0-alpha.8 + '@vue/reactivity-transform': 3.3.0-alpha.9 + '@vue/shared': 3.3.0-alpha.9 estree-walker: ^2.0.2 hash-sum: ^2.0.0 lru-cache: ^5.1.1 @@ -170,8 +170,8 @@ importers: packages/compiler-ssr: specifiers: - '@vue/compiler-dom': 3.3.0-alpha.8 - '@vue/shared': 3.3.0-alpha.8 + '@vue/compiler-dom': 3.3.0-alpha.9 + '@vue/shared': 3.3.0-alpha.9 dependencies: '@vue/compiler-dom': link:../compiler-dom '@vue/shared': link:../shared @@ -184,7 +184,7 @@ importers: packages/reactivity: specifiers: - '@vue/shared': 3.3.0-alpha.8 + '@vue/shared': 3.3.0-alpha.9 dependencies: '@vue/shared': link:../shared @@ -193,8 +193,8 @@ importers: '@babel/core': ^7.21.3 '@babel/parser': ^7.20.15 '@babel/types': ^7.21.3 - '@vue/compiler-core': 3.3.0-alpha.8 - '@vue/shared': 3.3.0-alpha.8 + '@vue/compiler-core': 3.3.0-alpha.9 + '@vue/shared': 3.3.0-alpha.9 estree-walker: ^2.0.2 magic-string: ^0.30.0 dependencies: @@ -209,16 +209,16 @@ importers: packages/runtime-core: specifiers: - '@vue/reactivity': 3.3.0-alpha.8 - '@vue/shared': 3.3.0-alpha.8 + '@vue/reactivity': 3.3.0-alpha.9 + '@vue/shared': 3.3.0-alpha.9 dependencies: '@vue/reactivity': link:../reactivity '@vue/shared': link:../shared packages/runtime-dom: specifiers: - '@vue/runtime-core': 3.3.0-alpha.8 - '@vue/shared': 3.3.0-alpha.8 + '@vue/runtime-core': 3.3.0-alpha.9 + '@vue/shared': 3.3.0-alpha.9 csstype: ^3.1.1 dependencies: '@vue/runtime-core': link:../runtime-core @@ -227,16 +227,16 @@ importers: packages/runtime-test: specifiers: - '@vue/runtime-core': 3.3.0-alpha.8 - '@vue/shared': 3.3.0-alpha.8 + '@vue/runtime-core': 3.3.0-alpha.9 + '@vue/shared': 3.3.0-alpha.9 dependencies: '@vue/runtime-core': link:../runtime-core '@vue/shared': link:../shared packages/server-renderer: specifiers: - '@vue/compiler-ssr': 3.3.0-alpha.8 - '@vue/shared': 3.3.0-alpha.8 + '@vue/compiler-ssr': 3.3.0-alpha.9 + '@vue/shared': 3.3.0-alpha.9 dependencies: '@vue/compiler-ssr': link:../compiler-ssr '@vue/shared': link:../shared @@ -277,11 +277,11 @@ importers: packages/vue: specifiers: - '@vue/compiler-dom': 3.3.0-alpha.8 - '@vue/compiler-sfc': 3.3.0-alpha.8 - '@vue/runtime-dom': 3.3.0-alpha.8 - '@vue/server-renderer': 3.3.0-alpha.8 - '@vue/shared': 3.3.0-alpha.8 + '@vue/compiler-dom': 3.3.0-alpha.9 + '@vue/compiler-sfc': 3.3.0-alpha.9 + '@vue/runtime-dom': 3.3.0-alpha.9 + '@vue/server-renderer': 3.3.0-alpha.9 + '@vue/shared': 3.3.0-alpha.9 dependencies: '@vue/compiler-dom': link:../compiler-dom '@vue/compiler-sfc': link:../compiler-sfc From 909afcce8204c6ac659e57f870d918b2ddee5ab6 Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Fri, 7 Apr 2023 21:18:47 +0800 Subject: [PATCH 018/110] fix: fixed effect for tests --- packages/reactivity/src/effect.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index a4d675ab411..34dc6ff7eab 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -201,10 +201,13 @@ export function effect( if (_c) { _computedsToAskDirty.push(_c) } - if (!_triggeredAfterLastEffect) { - _triggeredAfterLastEffect = true - schedulerCallbacks.push(cb) - } + } + if ( + (_dirty || _computedsToAskDirty.length) && + !_triggeredAfterLastEffect + ) { + _triggeredAfterLastEffect = true + schedulerCallbacks.push(cb) } } }) @@ -489,8 +492,9 @@ function triggerEffect( } if (isRootEffect) { state = EffectState.POST_SCHEDULER - schedulerCallbacks.forEach(cb => cb()) - schedulerCallbacks.length = 0 + while (schedulerCallbacks.length) { + schedulerCallbacks.shift()!() + } state = EffectState.NOT_TRACKING } } From da6263cb9d41c0199b91f47f26c76f778e1ead9b Mon Sep 17 00:00:00 2001 From: johnsoncodehk Date: Fri, 7 Apr 2023 21:31:00 +0800 Subject: [PATCH 019/110] refactor: implement simplify --- packages/reactivity/src/effect.ts | 32 ++++++++----------------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 34dc6ff7eab..3abcf9f88de 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -194,18 +194,12 @@ export function effect( const _effect = new ReactiveEffect(fn, _c => { if (!_dirty) { - if (!_c) { + if (_c) { + _computedsToAskDirty.push(_c) + } else { _dirty = true } - if (state === EffectState.TRACKING) { - if (_c) { - _computedsToAskDirty.push(_c) - } - } - if ( - (_dirty || _computedsToAskDirty.length) && - !_triggeredAfterLastEffect - ) { + if (!_triggeredAfterLastEffect) { _triggeredAfterLastEffect = true schedulerCallbacks.push(cb) } @@ -462,24 +456,15 @@ export function triggerEffects( const schedulerCallbacks: (() => void)[] = [] -const enum EffectState { - NOT_TRACKING = 0, - TRACKING = 1, - POST_SCHEDULER = 2 -} - -let state = EffectState.NOT_TRACKING +let tracking = false function triggerEffect( effect: ReactiveEffect, computedToAskDirty: ComputedRefImpl | undefined, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { - let isRootEffect = false - if (state === EffectState.NOT_TRACKING) { - state = EffectState.TRACKING - isRootEffect = true - } + let isRootEffect = !tracking + tracking = true if (effect !== activeEffect || effect.allowRecurse) { if (__DEV__ && effect.onTrigger) { effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) @@ -491,11 +476,10 @@ function triggerEffect( } } if (isRootEffect) { - state = EffectState.POST_SCHEDULER while (schedulerCallbacks.length) { schedulerCallbacks.shift()!() } - state = EffectState.NOT_TRACKING + tracking = false } } From d2fd264982451467a2aa4089c92ac3ebf6101034 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Fri, 7 Apr 2023 23:01:59 +0800 Subject: [PATCH 020/110] Revert "release: v3.3.0-alpha.9" This reverts commit 889511719721b754e774b42f1bc3e43391a30f35. --- CHANGELOG.md | 38 ---------------- package.json | 2 +- packages/compiler-core/package.json | 4 +- packages/compiler-dom/package.json | 6 +-- packages/compiler-sfc/package.json | 12 ++--- packages/compiler-ssr/package.json | 6 +-- packages/dts-test/package.json | 2 +- packages/reactivity-transform/package.json | 6 +-- packages/reactivity/package.json | 4 +- packages/runtime-core/package.json | 6 +-- packages/runtime-dom/package.json | 6 +-- packages/runtime-test/package.json | 6 +-- packages/server-renderer/package.json | 8 ++-- packages/sfc-playground/package.json | 2 +- packages/shared/package.json | 2 +- packages/size-check/package.json | 2 +- packages/template-explorer/package.json | 2 +- packages/vue-compat/package.json | 4 +- packages/vue/package.json | 12 ++--- pnpm-lock.yaml | 52 +++++++++++----------- 20 files changed, 72 insertions(+), 110 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5841b02aaa8..1c7ba846f5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,41 +1,3 @@ -# [3.3.0-alpha.9](https://github.com/vuejs/core/compare/v3.3.0-alpha.8...v3.3.0-alpha.9) (2023-04-07) - - -### Bug Fixes - -* avoid track unrelated effects ([7768d43](https://github.com/vuejs/core/commit/7768d43175ca58dd7bb8eec00a3db3f13f12ef45)) -* **compiler-sfc:** skip empty `defineOptions` and support TypeScript type assertions ([#8028](https://github.com/vuejs/core/issues/8028)) ([9557529](https://github.com/vuejs/core/commit/955752951e1d31b90d817bd20830fe3f89018771)) -* **compiler-ssr:** disable v-once transform in ssr vdom fallback branch ([05f94cf](https://github.com/vuejs/core/commit/05f94cf7b01dd05ed7d3170916a38b175d5df292)), closes [#7644](https://github.com/vuejs/core/issues/7644) -* computed effect dons't transfer ([208e59c](https://github.com/vuejs/core/commit/208e59c9d9e44765b2bd6eccbf7f6f395ae18979)) -* jest -> vitest ([014dcd5](https://github.com/vuejs/core/commit/014dcd5aa45a62927a96424d522d36255b741575)) -* ref newVal arg incorrect ([62dc5ee](https://github.com/vuejs/core/commit/62dc5ee1d132fdbb711a1ae9736293c75593f82a)) -* revert effects trigger order ([4a3e554](https://github.com/vuejs/core/commit/4a3e554d56a0e35c709801a087aadaaf956cd380)) -* **types:** improve defineProps return type with generic arguments ([91a931a](https://github.com/vuejs/core/commit/91a931ae8707b8d43f10216e1ce8e18b12158f99)) -* **types:** more public type argument order fix ([af563bf](https://github.com/vuejs/core/commit/af563bf428200367b6f5bb7944f690c85d810202)) -* **types:** retain type parameters order for public types ([bdf557f](https://github.com/vuejs/core/commit/bdf557f6f233c039fff8007b1b16aec00c4e68aa)) -* urgent assessment edge case ([eb0f8fa](https://github.com/vuejs/core/commit/eb0f8fae3891dbd841f3c59adbae51fc88a34e8f)) - - -### Features - -* **app:** app.runWithContext() ([#7451](https://github.com/vuejs/core/issues/7451)) ([869f3fb](https://github.com/vuejs/core/commit/869f3fb93e61400be4fd925e0850c2b1564749e2)) -* more purposeful test ([f3a0071](https://github.com/vuejs/core/commit/f3a007124d93b9a369692e545c5b9378e08736dd)) - - -### Performance Improvements - -* avoid duplicate trigger effect to deps ([0e0c44e](https://github.com/vuejs/core/commit/0e0c44e74f51037253c02c7186adbffb49cc3a40)) -* calculate embedded `computed()` on-demand ([f485ea0](https://github.com/vuejs/core/commit/f485ea01a9d1d7aca3bdc61b12f768de91c8d583)) -* reduce `triggerRefValue()` triggered ([399f155](https://github.com/vuejs/core/commit/399f1553c889a8b7486191ee56ce961814f26b9d)) - - -### Reverts - -* Revert "chore: add test for effect() on-demand trigger" ([b5527a1](https://github.com/vuejs/core/commit/b5527a17fa0374de2e2f39175788af30db1f594f)) -* Revert "chore: remove unused args passed to ssrRender" ([b117b88](https://github.com/vuejs/core/commit/b117b8844881a732a021432066230ff2215049ea)) - - - # [3.3.0-alpha.8](https://github.com/vuejs/core/compare/v3.3.0-alpha.7...v3.3.0-alpha.8) (2023-04-04) diff --git a/package.json b/package.json index 7da3028e73a..2f6db271c76 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "3.3.0-alpha.9", + "version": "3.3.0-alpha.8", "packageManager": "pnpm@7.26.0", "type": "module", "scripts": { diff --git a/packages/compiler-core/package.json b/packages/compiler-core/package.json index a7c20e8e1b8..152fc9ce6cf 100644 --- a/packages/compiler-core/package.json +++ b/packages/compiler-core/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-core", - "version": "3.3.0-alpha.9", + "version": "3.3.0-alpha.8", "description": "@vue/compiler-core", "main": "index.js", "module": "dist/compiler-core.esm-bundler.js", @@ -33,7 +33,7 @@ "homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-core#readme", "dependencies": { "@babel/parser": "^7.21.3", - "@vue/shared": "3.3.0-alpha.9", + "@vue/shared": "3.3.0-alpha.8", "estree-walker": "^2.0.2", "source-map": "^0.6.1" }, diff --git a/packages/compiler-dom/package.json b/packages/compiler-dom/package.json index a73978cec7e..007568fec7c 100644 --- a/packages/compiler-dom/package.json +++ b/packages/compiler-dom/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-dom", - "version": "3.3.0-alpha.9", + "version": "3.3.0-alpha.8", "description": "@vue/compiler-dom", "main": "index.js", "module": "dist/compiler-dom.esm-bundler.js", @@ -37,7 +37,7 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-dom#readme", "dependencies": { - "@vue/shared": "3.3.0-alpha.9", - "@vue/compiler-core": "3.3.0-alpha.9" + "@vue/shared": "3.3.0-alpha.8", + "@vue/compiler-core": "3.3.0-alpha.8" } } diff --git a/packages/compiler-sfc/package.json b/packages/compiler-sfc/package.json index 8c97d3b3b9b..ce831788a9a 100644 --- a/packages/compiler-sfc/package.json +++ b/packages/compiler-sfc/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-sfc", - "version": "3.3.0-alpha.9", + "version": "3.3.0-alpha.8", "description": "@vue/compiler-sfc", "main": "dist/compiler-sfc.cjs.js", "module": "dist/compiler-sfc.esm-browser.js", @@ -33,11 +33,11 @@ "homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-sfc#readme", "dependencies": { "@babel/parser": "^7.20.15", - "@vue/compiler-core": "3.3.0-alpha.9", - "@vue/compiler-dom": "3.3.0-alpha.9", - "@vue/compiler-ssr": "3.3.0-alpha.9", - "@vue/reactivity-transform": "3.3.0-alpha.9", - "@vue/shared": "3.3.0-alpha.9", + "@vue/compiler-core": "3.3.0-alpha.8", + "@vue/compiler-dom": "3.3.0-alpha.8", + "@vue/compiler-ssr": "3.3.0-alpha.8", + "@vue/reactivity-transform": "3.3.0-alpha.8", + "@vue/shared": "3.3.0-alpha.8", "estree-walker": "^2.0.2", "magic-string": "^0.30.0", "postcss": "^8.1.10", diff --git a/packages/compiler-ssr/package.json b/packages/compiler-ssr/package.json index b1809422b1d..b692b9fdd0f 100644 --- a/packages/compiler-ssr/package.json +++ b/packages/compiler-ssr/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-ssr", - "version": "3.3.0-alpha.9", + "version": "3.3.0-alpha.8", "description": "@vue/compiler-ssr", "main": "dist/compiler-ssr.cjs.js", "types": "dist/compiler-ssr.d.ts", @@ -28,7 +28,7 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-ssr#readme", "dependencies": { - "@vue/shared": "3.3.0-alpha.9", - "@vue/compiler-dom": "3.3.0-alpha.9" + "@vue/shared": "3.3.0-alpha.8", + "@vue/compiler-dom": "3.3.0-alpha.8" } } diff --git a/packages/dts-test/package.json b/packages/dts-test/package.json index 833b92e5008..5d5acc0989d 100644 --- a/packages/dts-test/package.json +++ b/packages/dts-test/package.json @@ -4,5 +4,5 @@ "dependencies": { "vue": "workspace:*" }, - "version": "3.3.0-alpha.9" + "version": "3.3.0-alpha.8" } diff --git a/packages/reactivity-transform/package.json b/packages/reactivity-transform/package.json index 5045a40a818..29f37dd6a49 100644 --- a/packages/reactivity-transform/package.json +++ b/packages/reactivity-transform/package.json @@ -1,6 +1,6 @@ { "name": "@vue/reactivity-transform", - "version": "3.3.0-alpha.9", + "version": "3.3.0-alpha.8", "description": "@vue/reactivity-transform", "main": "dist/reactivity-transform.cjs.js", "files": [ @@ -29,8 +29,8 @@ "homepage": "https://github.com/vuejs/core/tree/dev/packages/reactivity-transform#readme", "dependencies": { "@babel/parser": "^7.20.15", - "@vue/compiler-core": "3.3.0-alpha.9", - "@vue/shared": "3.3.0-alpha.9", + "@vue/compiler-core": "3.3.0-alpha.8", + "@vue/shared": "3.3.0-alpha.8", "estree-walker": "^2.0.2", "magic-string": "^0.30.0" }, diff --git a/packages/reactivity/package.json b/packages/reactivity/package.json index 4c7988e82a6..e07312dce31 100644 --- a/packages/reactivity/package.json +++ b/packages/reactivity/package.json @@ -1,6 +1,6 @@ { "name": "@vue/reactivity", - "version": "3.3.0-alpha.9", + "version": "3.3.0-alpha.8", "description": "@vue/reactivity", "main": "index.js", "module": "dist/reactivity.esm-bundler.js", @@ -36,6 +36,6 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/reactivity#readme", "dependencies": { - "@vue/shared": "3.3.0-alpha.9" + "@vue/shared": "3.3.0-alpha.8" } } diff --git a/packages/runtime-core/package.json b/packages/runtime-core/package.json index 606fef5414a..a282b8d87b4 100644 --- a/packages/runtime-core/package.json +++ b/packages/runtime-core/package.json @@ -1,6 +1,6 @@ { "name": "@vue/runtime-core", - "version": "3.3.0-alpha.9", + "version": "3.3.0-alpha.8", "description": "@vue/runtime-core", "main": "index.js", "module": "dist/runtime-core.esm-bundler.js", @@ -32,7 +32,7 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/runtime-core#readme", "dependencies": { - "@vue/shared": "3.3.0-alpha.9", - "@vue/reactivity": "3.3.0-alpha.9" + "@vue/shared": "3.3.0-alpha.8", + "@vue/reactivity": "3.3.0-alpha.8" } } diff --git a/packages/runtime-dom/package.json b/packages/runtime-dom/package.json index 25da3f9657a..29cf32c048b 100644 --- a/packages/runtime-dom/package.json +++ b/packages/runtime-dom/package.json @@ -1,6 +1,6 @@ { "name": "@vue/runtime-dom", - "version": "3.3.0-alpha.9", + "version": "3.3.0-alpha.8", "description": "@vue/runtime-dom", "main": "index.js", "module": "dist/runtime-dom.esm-bundler.js", @@ -35,8 +35,8 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/runtime-dom#readme", "dependencies": { - "@vue/shared": "3.3.0-alpha.9", - "@vue/runtime-core": "3.3.0-alpha.9", + "@vue/shared": "3.3.0-alpha.8", + "@vue/runtime-core": "3.3.0-alpha.8", "csstype": "^3.1.1" } } diff --git a/packages/runtime-test/package.json b/packages/runtime-test/package.json index 202161c9474..4c778451a92 100644 --- a/packages/runtime-test/package.json +++ b/packages/runtime-test/package.json @@ -1,6 +1,6 @@ { "name": "@vue/runtime-test", - "version": "3.3.0-alpha.9", + "version": "3.3.0-alpha.8", "description": "@vue/runtime-test", "private": true, "main": "index.js", @@ -25,7 +25,7 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/runtime-test#readme", "dependencies": { - "@vue/shared": "3.3.0-alpha.9", - "@vue/runtime-core": "3.3.0-alpha.9" + "@vue/shared": "3.3.0-alpha.8", + "@vue/runtime-core": "3.3.0-alpha.8" } } diff --git a/packages/server-renderer/package.json b/packages/server-renderer/package.json index 9f8a2f8eafd..d6d43bf82f7 100644 --- a/packages/server-renderer/package.json +++ b/packages/server-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@vue/server-renderer", - "version": "3.3.0-alpha.9", + "version": "3.3.0-alpha.8", "description": "@vue/server-renderer", "main": "index.js", "module": "dist/server-renderer.esm-bundler.js", @@ -32,10 +32,10 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/server-renderer#readme", "peerDependencies": { - "vue": "3.3.0-alpha.9" + "vue": "3.3.0-alpha.8" }, "dependencies": { - "@vue/shared": "3.3.0-alpha.9", - "@vue/compiler-ssr": "3.3.0-alpha.9" + "@vue/shared": "3.3.0-alpha.8", + "@vue/compiler-ssr": "3.3.0-alpha.8" } } diff --git a/packages/sfc-playground/package.json b/packages/sfc-playground/package.json index e434dd9495b..214e81cce27 100644 --- a/packages/sfc-playground/package.json +++ b/packages/sfc-playground/package.json @@ -1,6 +1,6 @@ { "name": "@vue/sfc-playground", - "version": "3.3.0-alpha.9", + "version": "3.3.0-alpha.8", "private": true, "scripts": { "dev": "vite", diff --git a/packages/shared/package.json b/packages/shared/package.json index a7ceb93b4ae..727e24ee626 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@vue/shared", - "version": "3.3.0-alpha.9", + "version": "3.3.0-alpha.8", "description": "internal utils shared across @vue packages", "main": "index.js", "module": "dist/shared.esm-bundler.js", diff --git a/packages/size-check/package.json b/packages/size-check/package.json index 34c7df41c92..94d49cc4e6a 100644 --- a/packages/size-check/package.json +++ b/packages/size-check/package.json @@ -1,6 +1,6 @@ { "name": "@vue/size-check", - "version": "3.3.0-alpha.9", + "version": "3.3.0-alpha.8", "private": true, "scripts": { "build": "vite build" diff --git a/packages/template-explorer/package.json b/packages/template-explorer/package.json index 81852352839..7b42a4c3ea6 100644 --- a/packages/template-explorer/package.json +++ b/packages/template-explorer/package.json @@ -1,6 +1,6 @@ { "name": "@vue/template-explorer", - "version": "3.3.0-alpha.9", + "version": "3.3.0-alpha.8", "private": true, "buildOptions": { "formats": [ diff --git a/packages/vue-compat/package.json b/packages/vue-compat/package.json index 5032c134cf8..aa04139c247 100644 --- a/packages/vue-compat/package.json +++ b/packages/vue-compat/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compat", - "version": "3.3.0-alpha.9", + "version": "3.3.0-alpha.8", "description": "Vue 3 compatibility build for Vue 2", "main": "index.js", "module": "dist/vue.runtime.esm-bundler.js", @@ -43,6 +43,6 @@ "source-map": "^0.6.1" }, "peerDependencies": { - "vue": "3.3.0-alpha.9" + "vue": "3.3.0-alpha.8" } } diff --git a/packages/vue/package.json b/packages/vue/package.json index 5a2e32f0e5a..cb17e34700d 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "vue", - "version": "3.3.0-alpha.9", + "version": "3.3.0-alpha.8", "description": "The progressive JavaScript framework for building modern web UI.", "main": "index.js", "module": "dist/vue.runtime.esm-bundler.js", @@ -81,10 +81,10 @@ }, "homepage": "https://github.com/vuejs/core/tree/main/packages/vue#readme", "dependencies": { - "@vue/shared": "3.3.0-alpha.9", - "@vue/compiler-dom": "3.3.0-alpha.9", - "@vue/runtime-dom": "3.3.0-alpha.9", - "@vue/compiler-sfc": "3.3.0-alpha.9", - "@vue/server-renderer": "3.3.0-alpha.9" + "@vue/shared": "3.3.0-alpha.8", + "@vue/compiler-dom": "3.3.0-alpha.8", + "@vue/runtime-dom": "3.3.0-alpha.8", + "@vue/compiler-sfc": "3.3.0-alpha.8", + "@vue/server-renderer": "3.3.0-alpha.8" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 489f7892681..5206450c2af 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -102,7 +102,7 @@ importers: specifiers: '@babel/parser': ^7.21.3 '@babel/types': ^7.21.3 - '@vue/shared': 3.3.0-alpha.9 + '@vue/shared': 3.3.0-alpha.8 estree-walker: ^2.0.2 source-map: ^0.6.1 dependencies: @@ -115,8 +115,8 @@ importers: packages/compiler-dom: specifiers: - '@vue/compiler-core': 3.3.0-alpha.9 - '@vue/shared': 3.3.0-alpha.9 + '@vue/compiler-core': 3.3.0-alpha.8 + '@vue/shared': 3.3.0-alpha.8 dependencies: '@vue/compiler-core': link:../compiler-core '@vue/shared': link:../shared @@ -127,12 +127,12 @@ importers: '@babel/types': ^7.21.3 '@types/estree': ^0.0.48 '@types/lru-cache': ^5.1.0 - '@vue/compiler-core': 3.3.0-alpha.9 - '@vue/compiler-dom': 3.3.0-alpha.9 - '@vue/compiler-ssr': 3.3.0-alpha.9 + '@vue/compiler-core': 3.3.0-alpha.8 + '@vue/compiler-dom': 3.3.0-alpha.8 + '@vue/compiler-ssr': 3.3.0-alpha.8 '@vue/consolidate': ^0.17.3 - '@vue/reactivity-transform': 3.3.0-alpha.9 - '@vue/shared': 3.3.0-alpha.9 + '@vue/reactivity-transform': 3.3.0-alpha.8 + '@vue/shared': 3.3.0-alpha.8 estree-walker: ^2.0.2 hash-sum: ^2.0.0 lru-cache: ^5.1.1 @@ -170,8 +170,8 @@ importers: packages/compiler-ssr: specifiers: - '@vue/compiler-dom': 3.3.0-alpha.9 - '@vue/shared': 3.3.0-alpha.9 + '@vue/compiler-dom': 3.3.0-alpha.8 + '@vue/shared': 3.3.0-alpha.8 dependencies: '@vue/compiler-dom': link:../compiler-dom '@vue/shared': link:../shared @@ -184,7 +184,7 @@ importers: packages/reactivity: specifiers: - '@vue/shared': 3.3.0-alpha.9 + '@vue/shared': 3.3.0-alpha.8 dependencies: '@vue/shared': link:../shared @@ -193,8 +193,8 @@ importers: '@babel/core': ^7.21.3 '@babel/parser': ^7.20.15 '@babel/types': ^7.21.3 - '@vue/compiler-core': 3.3.0-alpha.9 - '@vue/shared': 3.3.0-alpha.9 + '@vue/compiler-core': 3.3.0-alpha.8 + '@vue/shared': 3.3.0-alpha.8 estree-walker: ^2.0.2 magic-string: ^0.30.0 dependencies: @@ -209,16 +209,16 @@ importers: packages/runtime-core: specifiers: - '@vue/reactivity': 3.3.0-alpha.9 - '@vue/shared': 3.3.0-alpha.9 + '@vue/reactivity': 3.3.0-alpha.8 + '@vue/shared': 3.3.0-alpha.8 dependencies: '@vue/reactivity': link:../reactivity '@vue/shared': link:../shared packages/runtime-dom: specifiers: - '@vue/runtime-core': 3.3.0-alpha.9 - '@vue/shared': 3.3.0-alpha.9 + '@vue/runtime-core': 3.3.0-alpha.8 + '@vue/shared': 3.3.0-alpha.8 csstype: ^3.1.1 dependencies: '@vue/runtime-core': link:../runtime-core @@ -227,16 +227,16 @@ importers: packages/runtime-test: specifiers: - '@vue/runtime-core': 3.3.0-alpha.9 - '@vue/shared': 3.3.0-alpha.9 + '@vue/runtime-core': 3.3.0-alpha.8 + '@vue/shared': 3.3.0-alpha.8 dependencies: '@vue/runtime-core': link:../runtime-core '@vue/shared': link:../shared packages/server-renderer: specifiers: - '@vue/compiler-ssr': 3.3.0-alpha.9 - '@vue/shared': 3.3.0-alpha.9 + '@vue/compiler-ssr': 3.3.0-alpha.8 + '@vue/shared': 3.3.0-alpha.8 dependencies: '@vue/compiler-ssr': link:../compiler-ssr '@vue/shared': link:../shared @@ -277,11 +277,11 @@ importers: packages/vue: specifiers: - '@vue/compiler-dom': 3.3.0-alpha.9 - '@vue/compiler-sfc': 3.3.0-alpha.9 - '@vue/runtime-dom': 3.3.0-alpha.9 - '@vue/server-renderer': 3.3.0-alpha.9 - '@vue/shared': 3.3.0-alpha.9 + '@vue/compiler-dom': 3.3.0-alpha.8 + '@vue/compiler-sfc': 3.3.0-alpha.8 + '@vue/runtime-dom': 3.3.0-alpha.8 + '@vue/server-renderer': 3.3.0-alpha.8 + '@vue/shared': 3.3.0-alpha.8 dependencies: '@vue/compiler-dom': link:../compiler-dom '@vue/compiler-sfc': link:../compiler-sfc From 6c9f058270b9be46a272a233b2ceafa25f6bc76e Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 1 Aug 2023 06:12:59 +0800 Subject: [PATCH 021/110] chore: naming --- packages/reactivity/src/computed.ts | 25 ++++++++++++------------- packages/reactivity/src/effect.ts | 15 +++++++++------ packages/reactivity/src/ref.ts | 6 +++--- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 5a92b42624e..f9750541f3c 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -38,11 +38,10 @@ export class ComputedRefImpl { public readonly [ReactiveFlags.IS_READONLY]: boolean = false public _dirty = true + public _scheduled = false + public _deferredComputed: ComputedRefImpl[] = [] public _cacheable: boolean - private _computedsToAskDirty: ComputedRefImpl[] = [] - private _triggeredAfterLastEffect = false - constructor( getter: ComputedGetter, private readonly _setter: ComputedSetter, @@ -52,12 +51,12 @@ export class ComputedRefImpl { this.effect = new ReactiveEffect(getter, _c => { if (!this._dirty) { if (_c) { - this._computedsToAskDirty.push(_c) + this._deferredComputed.push(_c) } else { this._dirty = true } - if (!this._triggeredAfterLastEffect) { - this._triggeredAfterLastEffect = true + if (!this._scheduled) { + this._scheduled = true triggerRefValue(this, this) } } @@ -70,17 +69,17 @@ export class ComputedRefImpl { get value() { // the computed ref may get wrapped by other proxies e.g. readonly() #3376 const self = toRaw(this) - if (!self._dirty && self._computedsToAskDirty.length) { + if (!self._dirty && self._deferredComputed.length) { pauseTracking() - if (self._computedsToAskDirty.length >= 2) { - self._computedsToAskDirty = self._computedsToAskDirty.sort((a, b) => { + if (self._deferredComputed.length >= 2) { + self._deferredComputed = self._deferredComputed.sort((a, b) => { const aIndex = self.effect.deps.indexOf(a.dep!) const bIndex = self.effect.deps.indexOf(b.dep!) return aIndex - bIndex }) } - for (const computedToAskDirty of self._computedsToAskDirty) { - computedToAskDirty.value + for (const deferredComputed of self._deferredComputed) { + deferredComputed.value if (self._dirty) { break } @@ -95,9 +94,9 @@ export class ComputedRefImpl { } self._value = newValue self._dirty = false - self._triggeredAfterLastEffect = false + self._scheduled = false } - self._computedsToAskDirty.length = 0 + self._deferredComputed.length = 0 return self._value } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 1ef95b31cd7..e3c500a5e24 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -30,7 +30,10 @@ export let trackOpBit = 1 */ const maxMarkerBits = 30 -export type EffectScheduler = (computedToAskDirty: ComputedRefImpl | undefined, ...args: any[]) => any +export type EffectScheduler = ( + deferredComputed: ComputedRefImpl | undefined, + ...args: any[] +) => any export type DebuggerEvent = { effect: ReactiveEffect @@ -392,26 +395,26 @@ export function trigger( export function triggerEffects( dep: Dep | ReactiveEffect[], - computedToAskDirty: ComputedRefImpl | undefined, + deferredComputed: ComputedRefImpl | undefined, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { // spread into array for stabilization const effects = isArray(dep) ? dep : [...dep] for (const effect of effects) { if (effect.computed) { - triggerEffect(effect, computedToAskDirty, debuggerEventExtraInfo) + triggerEffect(effect, deferredComputed, debuggerEventExtraInfo) } } for (const effect of effects) { if (!effect.computed) { - triggerEffect(effect, computedToAskDirty, debuggerEventExtraInfo) + triggerEffect(effect, deferredComputed, debuggerEventExtraInfo) } } } function triggerEffect( effect: ReactiveEffect, - computedToAskDirty: ComputedRefImpl | undefined, + deferredComputed: ComputedRefImpl | undefined, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { if (effect !== activeEffect || effect.allowRecurse) { @@ -419,7 +422,7 @@ function triggerEffect( effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) } if (effect.scheduler) { - effect.scheduler(computedToAskDirty) + effect.scheduler(deferredComputed) } else { effect.run() } diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 3fc594efbf8..d01058763a8 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -55,21 +55,21 @@ export function trackRefValue(ref: RefBase) { export function triggerRefValue( ref: RefBase, - computedToAskDirty: ComputedRefImpl | undefined, + deferredComputed: ComputedRefImpl | undefined, newVal?: any ) { ref = toRaw(ref) const dep = ref.dep if (dep) { if (__DEV__) { - triggerEffects(dep, computedToAskDirty, { + triggerEffects(dep, deferredComputed, { target: ref, type: TriggerOpTypes.SET, key: 'value', newValue: newVal }) } else { - triggerEffects(dep, computedToAskDirty) + triggerEffects(dep, deferredComputed) } } } From 66bcc3e05d8c7c60afc1fbc28e913bd1de49a9bf Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 1 Aug 2023 06:15:06 +0800 Subject: [PATCH 022/110] chore: with "s" --- packages/reactivity/src/computed.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index f9750541f3c..1b953e5870d 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -39,7 +39,7 @@ export class ComputedRefImpl { public _dirty = true public _scheduled = false - public _deferredComputed: ComputedRefImpl[] = [] + public _deferredComputeds: ComputedRefImpl[] = [] public _cacheable: boolean constructor( @@ -51,7 +51,7 @@ export class ComputedRefImpl { this.effect = new ReactiveEffect(getter, _c => { if (!this._dirty) { if (_c) { - this._deferredComputed.push(_c) + this._deferredComputeds.push(_c) } else { this._dirty = true } @@ -69,16 +69,16 @@ export class ComputedRefImpl { get value() { // the computed ref may get wrapped by other proxies e.g. readonly() #3376 const self = toRaw(this) - if (!self._dirty && self._deferredComputed.length) { + if (!self._dirty && self._deferredComputeds.length) { pauseTracking() - if (self._deferredComputed.length >= 2) { - self._deferredComputed = self._deferredComputed.sort((a, b) => { + if (self._deferredComputeds.length >= 2) { + self._deferredComputeds = self._deferredComputeds.sort((a, b) => { const aIndex = self.effect.deps.indexOf(a.dep!) const bIndex = self.effect.deps.indexOf(b.dep!) return aIndex - bIndex }) } - for (const deferredComputed of self._deferredComputed) { + for (const deferredComputed of self._deferredComputeds) { deferredComputed.value if (self._dirty) { break @@ -96,7 +96,7 @@ export class ComputedRefImpl { self._dirty = false self._scheduled = false } - self._deferredComputed.length = 0 + self._deferredComputeds.length = 0 return self._value } From debddde6ee056971a9e679800af05096cae795a5 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 1 Aug 2023 06:17:49 +0800 Subject: [PATCH 023/110] chore: naming --- packages/reactivity/src/effect.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 363740ad48d..434d1c621ff 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -189,18 +189,18 @@ export function effect( } let _dirty = false - let _triggeredAfterLastEffect = false - let _computedsToAskDirty: ComputedRefImpl[] = [] + let _scheduled = false + let _deferredComputeds: ComputedRefImpl[] = [] const _effect = new ReactiveEffect(fn, _c => { if (!_dirty) { if (_c) { - _computedsToAskDirty.push(_c) + _deferredComputeds.push(_c) } else { _dirty = true } - if (!_triggeredAfterLastEffect) { - _triggeredAfterLastEffect = true + if (!_scheduled) { + _scheduled = true schedulerCallbacks.push(cb) } } @@ -217,16 +217,16 @@ export function effect( return runner function cb() { - if (!_dirty && _computedsToAskDirty.length) { + if (!_dirty && _deferredComputeds.length) { pauseTracking() - if (_computedsToAskDirty.length >= 2) { - _computedsToAskDirty = _computedsToAskDirty.sort((a, b) => { + if (_deferredComputeds.length >= 2) { + _deferredComputeds = _deferredComputeds.sort((a, b) => { const aIndex = _effect.deps.indexOf(a.dep!) const bIndex = _effect.deps.indexOf(b.dep!) return aIndex - bIndex }) } - for (const computedToAskDirty of _computedsToAskDirty) { + for (const computedToAskDirty of _deferredComputeds) { computedToAskDirty.value if (_dirty) { break @@ -238,8 +238,8 @@ export function effect( _dirty = false _effect.run() } - _computedsToAskDirty.length = 0 - _triggeredAfterLastEffect = false + _deferredComputeds.length = 0 + _scheduled = false } } From ba6688019c39d4e3dd70df35b8d86b19b0c4c821 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 1 Aug 2023 06:41:11 +0800 Subject: [PATCH 024/110] chore: undo format --- packages/compiler-sfc/src/script/importUsageCheck.ts | 6 +----- packages/runtime-core/src/components/Teleport.ts | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/compiler-sfc/src/script/importUsageCheck.ts b/packages/compiler-sfc/src/script/importUsageCheck.ts index f3c3932d829..7019dcf2312 100644 --- a/packages/compiler-sfc/src/script/importUsageCheck.ts +++ b/packages/compiler-sfc/src/script/importUsageCheck.ts @@ -63,11 +63,7 @@ function resolveTemplateUsageCheckString(sfc: SFCDescriptor) { )}` } } - if ( - prop.type === NodeTypes.ATTRIBUTE && - prop.name === 'ref' && - prop.value?.content - ) { + if (prop.type === NodeTypes.ATTRIBUTE && prop.name === 'ref' && prop.value?.content) { code += `,${prop.value.content}` } } diff --git a/packages/runtime-core/src/components/Teleport.ts b/packages/runtime-core/src/components/Teleport.ts index 19ccbc5de27..4f7d16bc7d1 100644 --- a/packages/runtime-core/src/components/Teleport.ts +++ b/packages/runtime-core/src/components/Teleport.ts @@ -400,7 +400,7 @@ function hydrateTeleport( // Force-casted public typing for h and TSX props inference export const Teleport = TeleportImpl as unknown as { __isTeleport: true - new (): { + new(): { $props: VNodeProps & TeleportProps $slots: { default(): VNode[] From cb081f6878fc65d0166820b97ff11dcd8efd729f Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 1 Aug 2023 06:49:01 +0800 Subject: [PATCH 025/110] chore: `_c` -> `deferredComputed` --- packages/reactivity/src/computed.ts | 6 +++--- packages/reactivity/src/deferredComputed.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 1b953e5870d..f00f324a59f 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -48,10 +48,10 @@ export class ComputedRefImpl { isReadonly: boolean, isSSR: boolean ) { - this.effect = new ReactiveEffect(getter, _c => { + this.effect = new ReactiveEffect(getter, deferredComputed => { if (!this._dirty) { - if (_c) { - this._deferredComputeds.push(_c) + if (deferredComputed) { + this._deferredComputeds.push(deferredComputed) } else { this._dirty = true } diff --git a/packages/reactivity/src/deferredComputed.ts b/packages/reactivity/src/deferredComputed.ts index 07b4a496bf4..527da3911e5 100644 --- a/packages/reactivity/src/deferredComputed.ts +++ b/packages/reactivity/src/deferredComputed.ts @@ -38,7 +38,7 @@ class DeferredComputedRefImpl { let compareTarget: any let hasCompareTarget = false let scheduled = false - this.effect = new ReactiveEffect(getter, (_c, computedTrigger?: boolean) => { + this.effect = new ReactiveEffect(getter, (_deferredComputed, computedTrigger?: boolean) => { if (this.dep) { if (computedTrigger) { compareTarget = this._value From 76e2e6e2c512ad1af63e22a66af46022c628e964 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 1 Aug 2023 06:50:53 +0800 Subject: [PATCH 026/110] chore: `_c` -> `deferredComputed` --- packages/reactivity/src/effect.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 434d1c621ff..da4f4cf0897 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -192,10 +192,10 @@ export function effect( let _scheduled = false let _deferredComputeds: ComputedRefImpl[] = [] - const _effect = new ReactiveEffect(fn, _c => { + const _effect = new ReactiveEffect(fn, deferredComputed => { if (!_dirty) { - if (_c) { - _deferredComputeds.push(_c) + if (deferredComputed) { + _deferredComputeds.push(deferredComputed) } else { _dirty = true } From de4e436423c2971c4469a3407e7de44435ab6848 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 1 Aug 2023 07:12:47 +0800 Subject: [PATCH 027/110] chore: expressiveness --- packages/reactivity/src/effect.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index da4f4cf0897..f21de71df02 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -456,15 +456,15 @@ export function triggerEffects( const schedulerCallbacks: (() => void)[] = [] -let tracking = false +let triggeringEffect = false function triggerEffect( effect: ReactiveEffect, deferredComputed: ComputedRefImpl | undefined, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { - let isRootEffect = !tracking - tracking = true + const isRootTrigger = !triggeringEffect + triggeringEffect = true if (effect !== activeEffect || effect.allowRecurse) { if (__DEV__ && effect.onTrigger) { effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) @@ -475,11 +475,11 @@ function triggerEffect( effect.run() } } - if (isRootEffect) { + if (isRootTrigger) { while (schedulerCallbacks.length) { schedulerCallbacks.shift()!() } - tracking = false + triggeringEffect = false } } From 6c13a3021017f9631222b91ca79b2745eca6e4f7 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 1 Aug 2023 07:22:35 +0800 Subject: [PATCH 028/110] refactor: reuse `effectTrackDepth` --- packages/reactivity/src/effect.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index f21de71df02..656a5cbeefd 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -456,15 +456,12 @@ export function triggerEffects( const schedulerCallbacks: (() => void)[] = [] -let triggeringEffect = false - function triggerEffect( effect: ReactiveEffect, deferredComputed: ComputedRefImpl | undefined, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { - const isRootTrigger = !triggeringEffect - triggeringEffect = true + const isRootTrigger = effectTrackDepth === 0 if (effect !== activeEffect || effect.allowRecurse) { if (__DEV__ && effect.onTrigger) { effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) @@ -479,7 +476,6 @@ function triggerEffect( while (schedulerCallbacks.length) { schedulerCallbacks.shift()!() } - triggeringEffect = false } } From ef2a6e3e68f16dc10fce84c40089de41272538c1 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 1 Aug 2023 07:28:54 +0800 Subject: [PATCH 029/110] refactor: remove unneeded `pauseTracking()`, `resetTracking()` --- packages/reactivity/src/effect.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 656a5cbeefd..ce3b1ede9e4 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -201,7 +201,7 @@ export function effect( } if (!_scheduled) { _scheduled = true - schedulerCallbacks.push(cb) + pendingEffectRunners.push(run) } } }) @@ -216,9 +216,8 @@ export function effect( runner.effect = _effect return runner - function cb() { + function run() { if (!_dirty && _deferredComputeds.length) { - pauseTracking() if (_deferredComputeds.length >= 2) { _deferredComputeds = _deferredComputeds.sort((a, b) => { const aIndex = _effect.deps.indexOf(a.dep!) @@ -232,7 +231,6 @@ export function effect( break } } - resetTracking() } if (_dirty) { _dirty = false @@ -454,7 +452,7 @@ export function triggerEffects( } } -const schedulerCallbacks: (() => void)[] = [] +const pendingEffectRunners: (() => void)[] = [] function triggerEffect( effect: ReactiveEffect, @@ -473,8 +471,8 @@ function triggerEffect( } } if (isRootTrigger) { - while (schedulerCallbacks.length) { - schedulerCallbacks.shift()!() + while (pendingEffectRunners.length) { + pendingEffectRunners.shift()!() } } } From 7bc106991aafb25cb00d0d8a731f7c727154e379 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 1 Aug 2023 07:47:19 +0800 Subject: [PATCH 030/110] chore: sort in one line --- packages/reactivity/src/computed.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index f00f324a59f..065248c80b0 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -72,11 +72,10 @@ export class ComputedRefImpl { if (!self._dirty && self._deferredComputeds.length) { pauseTracking() if (self._deferredComputeds.length >= 2) { - self._deferredComputeds = self._deferredComputeds.sort((a, b) => { - const aIndex = self.effect.deps.indexOf(a.dep!) - const bIndex = self.effect.deps.indexOf(b.dep!) - return aIndex - bIndex - }) + self._deferredComputeds = self._deferredComputeds.sort( + (a, b) => + self.effect.deps.indexOf(a.dep!) - self.effect.deps.indexOf(b.dep!) + ) } for (const deferredComputed of self._deferredComputeds) { deferredComputed.value From f0512d607f102a3a1ab3c088c5e8ad3cc13fae26 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 1 Aug 2023 07:48:31 +0800 Subject: [PATCH 031/110] chore: sort in one line --- packages/reactivity/src/effect.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index ce3b1ede9e4..2659f9ab6e3 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -219,11 +219,9 @@ export function effect( function run() { if (!_dirty && _deferredComputeds.length) { if (_deferredComputeds.length >= 2) { - _deferredComputeds = _deferredComputeds.sort((a, b) => { - const aIndex = _effect.deps.indexOf(a.dep!) - const bIndex = _effect.deps.indexOf(b.dep!) - return aIndex - bIndex - }) + _deferredComputeds = _deferredComputeds.sort( + (a, b) => _effect.deps.indexOf(a.dep!) - _effect.deps.indexOf(b.dep!) + ) } for (const computedToAskDirty of _deferredComputeds) { computedToAskDirty.value From 7e2ac742caac191656fb3953d2e2ccbdadf62bf4 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 1 Aug 2023 07:49:35 +0800 Subject: [PATCH 032/110] chore: naming --- packages/reactivity/src/effect.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 2659f9ab6e3..4644138efad 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -223,8 +223,8 @@ export function effect( (a, b) => _effect.deps.indexOf(a.dep!) - _effect.deps.indexOf(b.dep!) ) } - for (const computedToAskDirty of _deferredComputeds) { - computedToAskDirty.value + for (const deferredComputed of _deferredComputeds) { + deferredComputed.value if (_dirty) { break } From e98d329c9395e9991e90f0222c7fd0916f0dfa86 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 1 Aug 2023 07:59:07 +0800 Subject: [PATCH 033/110] chore: import type --- packages/reactivity/src/ref.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index d01058763a8..95b1a270091 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -16,9 +16,9 @@ import { isShallow } from './reactive' import type { ShallowReactiveMarker } from './reactive' +import type { ComputedRefImpl } from './computed' import { CollectionTypes } from './collectionHandlers' import { createDep, Dep } from './dep' -import { ComputedRefImpl } from './computed' declare const RefSymbol: unique symbol export declare const RawSymbol: unique symbol From d5595cbab49c4f2096595cd136dae2be6669ca45 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 1 Aug 2023 08:33:34 +0800 Subject: [PATCH 034/110] chore: narrow `pauseTracking()` --- packages/reactivity/src/computed.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 065248c80b0..7c53e87fd38 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -70,13 +70,13 @@ export class ComputedRefImpl { // the computed ref may get wrapped by other proxies e.g. readonly() #3376 const self = toRaw(this) if (!self._dirty && self._deferredComputeds.length) { - pauseTracking() if (self._deferredComputeds.length >= 2) { self._deferredComputeds = self._deferredComputeds.sort( (a, b) => self.effect.deps.indexOf(a.dep!) - self.effect.deps.indexOf(b.dep!) ) } + pauseTracking() for (const deferredComputed of self._deferredComputeds) { deferredComputed.value if (self._dirty) { From 44cbb5eff970d09deef9a701488c2f51d12fe918 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 14 Aug 2023 13:35:37 +0800 Subject: [PATCH 035/110] perf: cache dep indexes --- packages/reactivity/src/computed.ts | 12 +++++++----- packages/reactivity/src/effect.ts | 11 +++++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 7c53e87fd38..cdf0032077b 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -41,6 +41,7 @@ export class ComputedRefImpl { public _scheduled = false public _deferredComputeds: ComputedRefImpl[] = [] public _cacheable: boolean + public _depIndexes = new Map() constructor( getter: ComputedGetter, @@ -70,12 +71,13 @@ export class ComputedRefImpl { // the computed ref may get wrapped by other proxies e.g. readonly() #3376 const self = toRaw(this) if (!self._dirty && self._deferredComputeds.length) { - if (self._deferredComputeds.length >= 2) { - self._deferredComputeds = self._deferredComputeds.sort( - (a, b) => - self.effect.deps.indexOf(a.dep!) - self.effect.deps.indexOf(b.dep!) - ) + self._depIndexes.clear() + for (const c of self._deferredComputeds) { + self._depIndexes.set(c.dep!, self.effect.deps.indexOf(c.dep!)) } + self._deferredComputeds = self._deferredComputeds.sort( + (a, b) => self._depIndexes.get(a.dep!)! - self._depIndexes.get(b.dep!)! + ) pauseTracking() for (const deferredComputed of self._deferredComputeds) { deferredComputed.value diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 4644138efad..52937a4c6bc 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -192,6 +192,7 @@ export function effect( let _scheduled = false let _deferredComputeds: ComputedRefImpl[] = [] + const _depIndexes = new Map() const _effect = new ReactiveEffect(fn, deferredComputed => { if (!_dirty) { if (deferredComputed) { @@ -218,11 +219,13 @@ export function effect( function run() { if (!_dirty && _deferredComputeds.length) { - if (_deferredComputeds.length >= 2) { - _deferredComputeds = _deferredComputeds.sort( - (a, b) => _effect.deps.indexOf(a.dep!) - _effect.deps.indexOf(b.dep!) - ) + _depIndexes.clear() + for (const c of _deferredComputeds) { + _depIndexes.set(c.dep!, _effect.deps.indexOf(c.dep!)) } + _deferredComputeds = _deferredComputeds.sort( + (a, b) => _depIndexes.get(a.dep!)! - _depIndexes.get(b.dep!)! + ) for (const deferredComputed of _deferredComputeds) { deferredComputed.value if (_dirty) { From 8a3c78605e888ed4b0d15972577d669ef1ab04e4 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 14 Aug 2023 13:44:50 +0800 Subject: [PATCH 036/110] perf: redo `length >= 2` --- packages/reactivity/src/computed.ts | 17 ++++++++++------- packages/reactivity/src/effect.ts | 14 ++++++++------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index cdf0032077b..26733142c8e 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -40,8 +40,8 @@ export class ComputedRefImpl { public _dirty = true public _scheduled = false public _deferredComputeds: ComputedRefImpl[] = [] - public _cacheable: boolean public _depIndexes = new Map() + public _cacheable: boolean constructor( getter: ComputedGetter, @@ -71,13 +71,16 @@ export class ComputedRefImpl { // the computed ref may get wrapped by other proxies e.g. readonly() #3376 const self = toRaw(this) if (!self._dirty && self._deferredComputeds.length) { - self._depIndexes.clear() - for (const c of self._deferredComputeds) { - self._depIndexes.set(c.dep!, self.effect.deps.indexOf(c.dep!)) + if (self._deferredComputeds.length >= 2) { + self._depIndexes.clear() + for (const { dep } of self._deferredComputeds) { + self._depIndexes.set(dep!, self.effect.deps.indexOf(dep!)) + } + self._deferredComputeds = self._deferredComputeds.sort( + (a, b) => + self._depIndexes.get(a.dep!)! - self._depIndexes.get(b.dep!)! + ) } - self._deferredComputeds = self._deferredComputeds.sort( - (a, b) => self._depIndexes.get(a.dep!)! - self._depIndexes.get(b.dep!)! - ) pauseTracking() for (const deferredComputed of self._deferredComputeds) { deferredComputed.value diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 52937a4c6bc..4253974ebef 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -219,13 +219,15 @@ export function effect( function run() { if (!_dirty && _deferredComputeds.length) { - _depIndexes.clear() - for (const c of _deferredComputeds) { - _depIndexes.set(c.dep!, _effect.deps.indexOf(c.dep!)) + if (_deferredComputeds.length >= 2) { + _depIndexes.clear() + for (const { dep } of _deferredComputeds) { + _depIndexes.set(dep!, _effect.deps.indexOf(dep!)) + } + _deferredComputeds = _deferredComputeds.sort( + (a, b) => _depIndexes.get(a.dep!)! - _depIndexes.get(b.dep!)! + ) } - _deferredComputeds = _deferredComputeds.sort( - (a, b) => _depIndexes.get(a.dep!)! - _depIndexes.get(b.dep!)! - ) for (const deferredComputed of _deferredComputeds) { deferredComputed.value if (_dirty) { From 76da3275f20469fc980ba784f4878e3607d795c6 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 14 Aug 2023 13:47:10 +0800 Subject: [PATCH 037/110] chore: hoist `triggerRefValue` for readability --- packages/reactivity/src/computed.ts | 8 ++++---- packages/reactivity/src/effect.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 26733142c8e..7cac02baf07 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -56,10 +56,10 @@ export class ComputedRefImpl { } else { this._dirty = true } - if (!this._scheduled) { - this._scheduled = true - triggerRefValue(this, this) - } + } + if (!this._scheduled) { + this._scheduled = true + triggerRefValue(this, this) } }) this.effect.computed = this diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 4253974ebef..1a8cd25562f 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -200,10 +200,10 @@ export function effect( } else { _dirty = true } - if (!_scheduled) { - _scheduled = true - pendingEffectRunners.push(run) - } + } + if (!_scheduled) { + _scheduled = true + pendingEffectRunners.push(run) } }) if (options) { From 9002b93b871786667a760ec860e9cdd428935fdd Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 14 Aug 2023 13:51:04 +0800 Subject: [PATCH 038/110] chore: always clear dep indexes cache to avoid memory leak --- packages/reactivity/src/computed.ts | 2 +- packages/reactivity/src/effect.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 7cac02baf07..831b8c0287b 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -72,7 +72,6 @@ export class ComputedRefImpl { const self = toRaw(this) if (!self._dirty && self._deferredComputeds.length) { if (self._deferredComputeds.length >= 2) { - self._depIndexes.clear() for (const { dep } of self._deferredComputeds) { self._depIndexes.set(dep!, self.effect.deps.indexOf(dep!)) } @@ -80,6 +79,7 @@ export class ComputedRefImpl { (a, b) => self._depIndexes.get(a.dep!)! - self._depIndexes.get(b.dep!)! ) + self._depIndexes.clear() } pauseTracking() for (const deferredComputed of self._deferredComputeds) { diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 1a8cd25562f..f5d8547765a 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -220,13 +220,13 @@ export function effect( function run() { if (!_dirty && _deferredComputeds.length) { if (_deferredComputeds.length >= 2) { - _depIndexes.clear() for (const { dep } of _deferredComputeds) { _depIndexes.set(dep!, _effect.deps.indexOf(dep!)) } _deferredComputeds = _deferredComputeds.sort( (a, b) => _depIndexes.get(a.dep!)! - _depIndexes.get(b.dep!)! ) + _depIndexes.clear() } for (const deferredComputed of _deferredComputeds) { deferredComputed.value From 0f2c388f30cdbcdb896fa140da3b3f05438b08e8 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 14 Aug 2023 13:54:12 +0800 Subject: [PATCH 039/110] chore: less type hack --- packages/reactivity/src/computed.ts | 7 +++---- packages/reactivity/src/effect.ts | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 831b8c0287b..fdd239e6360 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -40,7 +40,7 @@ export class ComputedRefImpl { public _dirty = true public _scheduled = false public _deferredComputeds: ComputedRefImpl[] = [] - public _depIndexes = new Map() + public _depIndexes = new Map() public _cacheable: boolean constructor( @@ -73,11 +73,10 @@ export class ComputedRefImpl { if (!self._dirty && self._deferredComputeds.length) { if (self._deferredComputeds.length >= 2) { for (const { dep } of self._deferredComputeds) { - self._depIndexes.set(dep!, self.effect.deps.indexOf(dep!)) + self._depIndexes.set(dep, self.effect.deps.indexOf(dep!)) } self._deferredComputeds = self._deferredComputeds.sort( - (a, b) => - self._depIndexes.get(a.dep!)! - self._depIndexes.get(b.dep!)! + (a, b) => self._depIndexes.get(a.dep)! - self._depIndexes.get(b.dep)! ) self._depIndexes.clear() } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index f5d8547765a..829318d4f12 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -192,7 +192,7 @@ export function effect( let _scheduled = false let _deferredComputeds: ComputedRefImpl[] = [] - const _depIndexes = new Map() + const _depIndexes = new Map() const _effect = new ReactiveEffect(fn, deferredComputed => { if (!_dirty) { if (deferredComputed) { @@ -221,10 +221,10 @@ export function effect( if (!_dirty && _deferredComputeds.length) { if (_deferredComputeds.length >= 2) { for (const { dep } of _deferredComputeds) { - _depIndexes.set(dep!, _effect.deps.indexOf(dep!)) + _depIndexes.set(dep, _effect.deps.indexOf(dep!)) } _deferredComputeds = _deferredComputeds.sort( - (a, b) => _depIndexes.get(a.dep!)! - _depIndexes.get(b.dep!)! + (a, b) => _depIndexes.get(a.dep)! - _depIndexes.get(b.dep)! ) _depIndexes.clear() } From f8ff93a5d04bd9cd65c96d88abea62e4764d8fd3 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 27 Aug 2023 03:25:35 +0800 Subject: [PATCH 040/110] chore: format --- packages/reactivity/src/deferredComputed.ts | 51 +++++++++++---------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/packages/reactivity/src/deferredComputed.ts b/packages/reactivity/src/deferredComputed.ts index 527da3911e5..0830fe7b5c8 100644 --- a/packages/reactivity/src/deferredComputed.ts +++ b/packages/reactivity/src/deferredComputed.ts @@ -38,33 +38,38 @@ class DeferredComputedRefImpl { let compareTarget: any let hasCompareTarget = false let scheduled = false - this.effect = new ReactiveEffect(getter, (_deferredComputed, computedTrigger?: boolean) => { - if (this.dep) { - if (computedTrigger) { - compareTarget = this._value - hasCompareTarget = true - } else if (!scheduled) { - const valueToCompare = hasCompareTarget ? compareTarget : this._value - scheduled = true - hasCompareTarget = false - scheduler(() => { - if (this.effect.active && this._get() !== valueToCompare) { - triggerRefValue(this, undefined) + this.effect = new ReactiveEffect( + getter, + (_deferredComputed, computedTrigger?: boolean) => { + if (this.dep) { + if (computedTrigger) { + compareTarget = this._value + hasCompareTarget = true + } else if (!scheduled) { + const valueToCompare = hasCompareTarget + ? compareTarget + : this._value + scheduled = true + hasCompareTarget = false + scheduler(() => { + if (this.effect.active && this._get() !== valueToCompare) { + triggerRefValue(this, undefined) + } + scheduled = false + }) + } + // chained upstream computeds are notified synchronously to ensure + // value invalidation in case of sync access; normal effects are + // deferred to be triggered in scheduler. + for (const e of this.dep) { + if (e.computed instanceof DeferredComputedRefImpl) { + e.scheduler!(undefined, true /* computedTrigger */) } - scheduled = false - }) - } - // chained upstream computeds are notified synchronously to ensure - // value invalidation in case of sync access; normal effects are - // deferred to be triggered in scheduler. - for (const e of this.dep) { - if (e.computed instanceof DeferredComputedRefImpl) { - e.scheduler!(undefined, true /* computedTrigger */) } } + this._dirty = true } - this._dirty = true - }) + ) this.effect.computed = this as any } From 17342d50ddc16c4ba1666e81d668baf34a46be68 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 27 Aug 2023 05:20:09 +0800 Subject: [PATCH 041/110] refactor: abstract deferred computeds into ReactiveEffect --- packages/reactivity/src/computed.ts | 53 ++---------- packages/reactivity/src/deferredComputed.ts | 58 ++++++-------- packages/reactivity/src/effect.ts | 89 +++++++++++---------- 3 files changed, 80 insertions(+), 120 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index fdd239e6360..c1ae41ba48f 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -1,9 +1,4 @@ -import { - DebuggerOptions, - pauseTracking, - ReactiveEffect, - resetTracking -} from './effect' +import { DebuggerOptions, ReactiveEffect } from './effect' import { Ref, trackRefValue, triggerRefValue } from './ref' import { hasChanged, isFunction, NOOP } from '@vue/shared' import { ReactiveFlags, toRaw } from './reactive' @@ -37,10 +32,6 @@ export class ComputedRefImpl { public readonly __v_isRef = true public readonly [ReactiveFlags.IS_READONLY]: boolean = false - public _dirty = true - public _scheduled = false - public _deferredComputeds: ComputedRefImpl[] = [] - public _depIndexes = new Map() public _cacheable: boolean constructor( @@ -49,19 +40,11 @@ export class ComputedRefImpl { isReadonly: boolean, isSSR: boolean ) { - this.effect = new ReactiveEffect(getter, deferredComputed => { - if (!this._dirty) { - if (deferredComputed) { - this._deferredComputeds.push(deferredComputed) - } else { - this._dirty = true - } - } - if (!this._scheduled) { - this._scheduled = true - triggerRefValue(this, this) - } + this.effect = new ReactiveEffect(getter, () => { + this.effect._scheduled = true + triggerRefValue(this, this) }) + this.effect._dirty = true this.effect.computed = this this.effect.active = this._cacheable = !isSSR this[ReactiveFlags.IS_READONLY] = isReadonly @@ -70,36 +53,16 @@ export class ComputedRefImpl { get value() { // the computed ref may get wrapped by other proxies e.g. readonly() #3376 const self = toRaw(this) - if (!self._dirty && self._deferredComputeds.length) { - if (self._deferredComputeds.length >= 2) { - for (const { dep } of self._deferredComputeds) { - self._depIndexes.set(dep, self.effect.deps.indexOf(dep!)) - } - self._deferredComputeds = self._deferredComputeds.sort( - (a, b) => self._depIndexes.get(a.dep)! - self._depIndexes.get(b.dep)! - ) - self._depIndexes.clear() - } - pauseTracking() - for (const deferredComputed of self._deferredComputeds) { - deferredComputed.value - if (self._dirty) { - break - } - } - resetTracking() - } trackRefValue(self) - if (self._dirty || !self._cacheable) { + if (!self._cacheable || self.effect.dirty) { const newValue = self.effect.run()! if (hasChanged(self._value, newValue)) { triggerRefValue(self, undefined) } self._value = newValue - self._dirty = false - self._scheduled = false + self.effect._dirty = false + self.effect._scheduled = false } - self._deferredComputeds.length = 0 return self._value } diff --git a/packages/reactivity/src/deferredComputed.ts b/packages/reactivity/src/deferredComputed.ts index 0830fe7b5c8..4d88744b1db 100644 --- a/packages/reactivity/src/deferredComputed.ts +++ b/packages/reactivity/src/deferredComputed.ts @@ -28,7 +28,6 @@ class DeferredComputedRefImpl { public dep?: Dep = undefined private _value!: T - private _dirty = true public readonly effect: ReactiveEffect public readonly __v_isRef = true @@ -37,45 +36,40 @@ class DeferredComputedRefImpl { constructor(getter: ComputedGetter) { let compareTarget: any let hasCompareTarget = false - let scheduled = false - this.effect = new ReactiveEffect( - getter, - (_deferredComputed, computedTrigger?: boolean) => { - if (this.dep) { - if (computedTrigger) { - compareTarget = this._value - hasCompareTarget = true - } else if (!scheduled) { - const valueToCompare = hasCompareTarget - ? compareTarget - : this._value - scheduled = true - hasCompareTarget = false - scheduler(() => { - if (this.effect.active && this._get() !== valueToCompare) { - triggerRefValue(this, undefined) - } - scheduled = false - }) - } - // chained upstream computeds are notified synchronously to ensure - // value invalidation in case of sync access; normal effects are - // deferred to be triggered in scheduler. - for (const e of this.dep) { - if (e.computed instanceof DeferredComputedRefImpl) { - e.scheduler!(undefined, true /* computedTrigger */) + this.effect = new ReactiveEffect(getter, (computedTrigger?: boolean) => { + if (this.dep) { + if (computedTrigger) { + compareTarget = this._value + hasCompareTarget = true + } else if (!this.effect._scheduled) { + const valueToCompare = hasCompareTarget ? compareTarget : this._value + this.effect._scheduled = true + hasCompareTarget = false + scheduler(() => { + if (this.effect.active && this._get() !== valueToCompare) { + triggerRefValue(this, undefined) } + this.effect._scheduled = false + }) + } + // chained upstream computeds are notified synchronously to ensure + // value invalidation in case of sync access; normal effects are + // deferred to be triggered in scheduler. + for (const e of this.dep) { + if (e.computed instanceof DeferredComputedRefImpl) { + e.scheduler!(true /* computedTrigger */) } } - this._dirty = true + this.effect._dirty = true } - ) + }) + this.effect._dirty = true this.effect.computed = this as any } private _get() { - if (this._dirty) { - this._dirty = false + if (this.effect.dirty) { + this.effect._dirty = false return (this._value = this.effect.run()!) } return this._value diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 829318d4f12..38336e5fd66 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -30,10 +30,7 @@ export let trackOpBit = 1 */ const maxMarkerBits = 30 -export type EffectScheduler = ( - deferredComputed: ComputedRefImpl | undefined, - ...args: any[] -) => any +export type EffectScheduler = (...args: any[]) => any export type DebuggerEvent = { effect: ReactiveEffect @@ -78,6 +75,35 @@ export class ReactiveEffect { // dev only onTrigger?: (event: DebuggerEvent) => void + public _dirty = false + public _scheduled = false + public _deferredComputeds: ComputedRefImpl[] = [] + private _depIndexes = new Map() + + public get dirty() { + if (!this._dirty && this._deferredComputeds.length) { + if (this._deferredComputeds.length >= 2) { + for (const { dep } of this._deferredComputeds) { + this._depIndexes.set(dep, this.deps.indexOf(dep!)) + } + this._deferredComputeds = this._deferredComputeds.sort( + (a, b) => this._depIndexes.get(a.dep)! - this._depIndexes.get(b.dep)! + ) + this._depIndexes.clear() + } + pauseTracking() + for (const deferredComputed of this._deferredComputeds) { + deferredComputed.value + if (this._dirty) { + break + } + } + resetTracking() + } + this._deferredComputeds.length = 0 + return this._dirty + } + constructor( public fn: () => T, public scheduler: EffectScheduler | null = null, @@ -188,23 +214,9 @@ export function effect( fn = (fn as ReactiveEffectRunner).effect.fn } - let _dirty = false - let _scheduled = false - let _deferredComputeds: ComputedRefImpl[] = [] - - const _depIndexes = new Map() - const _effect = new ReactiveEffect(fn, deferredComputed => { - if (!_dirty) { - if (deferredComputed) { - _deferredComputeds.push(deferredComputed) - } else { - _dirty = true - } - } - if (!_scheduled) { - _scheduled = true - pendingEffectRunners.push(run) - } + const _effect = new ReactiveEffect(fn, () => { + _effect._scheduled = true + pendingEffectRunners.push(run) }) if (options) { extend(_effect, options) @@ -218,29 +230,11 @@ export function effect( return runner function run() { - if (!_dirty && _deferredComputeds.length) { - if (_deferredComputeds.length >= 2) { - for (const { dep } of _deferredComputeds) { - _depIndexes.set(dep, _effect.deps.indexOf(dep!)) - } - _deferredComputeds = _deferredComputeds.sort( - (a, b) => _depIndexes.get(a.dep)! - _depIndexes.get(b.dep)! - ) - _depIndexes.clear() - } - for (const deferredComputed of _deferredComputeds) { - deferredComputed.value - if (_dirty) { - break - } - } - } - if (_dirty) { - _dirty = false + if (_effect.dirty) { _effect.run() + _effect._dirty = false } - _deferredComputeds.length = 0 - _scheduled = false + _effect._scheduled = false } } @@ -468,7 +462,16 @@ function triggerEffect( effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) } if (effect.scheduler) { - effect.scheduler(deferredComputed) + if (!effect._dirty) { + if (deferredComputed) { + effect._deferredComputeds.push(deferredComputed) + } else { + effect._dirty = true + } + } + if (!effect._scheduled) { + effect.scheduler() + } } else { effect.run() } From 0194f0249127ee7ef004881a875e95546fc00ffb Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 27 Aug 2023 05:23:33 +0800 Subject: [PATCH 042/110] fix: fixes _dirty updating for deferredComputed --- packages/reactivity/src/deferredComputed.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reactivity/src/deferredComputed.ts b/packages/reactivity/src/deferredComputed.ts index 4d88744b1db..263d3b82502 100644 --- a/packages/reactivity/src/deferredComputed.ts +++ b/packages/reactivity/src/deferredComputed.ts @@ -60,8 +60,8 @@ class DeferredComputedRefImpl { e.scheduler!(true /* computedTrigger */) } } - this.effect._dirty = true } + this.effect._dirty = true }) this.effect._dirty = true this.effect.computed = this as any From 16dc2b88dedc9d0a997d0742b906edce1cdffbd9 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 27 Aug 2023 05:26:05 +0800 Subject: [PATCH 043/110] chore: revert deferredComputed --- packages/reactivity/src/deferredComputed.ts | 17 +++++++++-------- packages/reactivity/src/ref.ts | 7 ++----- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/reactivity/src/deferredComputed.ts b/packages/reactivity/src/deferredComputed.ts index 263d3b82502..a23122046a4 100644 --- a/packages/reactivity/src/deferredComputed.ts +++ b/packages/reactivity/src/deferredComputed.ts @@ -28,6 +28,7 @@ class DeferredComputedRefImpl { public dep?: Dep = undefined private _value!: T + private _dirty = true public readonly effect: ReactiveEffect public readonly __v_isRef = true @@ -36,20 +37,21 @@ class DeferredComputedRefImpl { constructor(getter: ComputedGetter) { let compareTarget: any let hasCompareTarget = false + let scheduled = false this.effect = new ReactiveEffect(getter, (computedTrigger?: boolean) => { if (this.dep) { if (computedTrigger) { compareTarget = this._value hasCompareTarget = true - } else if (!this.effect._scheduled) { + } else if (!scheduled) { const valueToCompare = hasCompareTarget ? compareTarget : this._value - this.effect._scheduled = true + scheduled = true hasCompareTarget = false scheduler(() => { if (this.effect.active && this._get() !== valueToCompare) { - triggerRefValue(this, undefined) + triggerRefValue(this) } - this.effect._scheduled = false + scheduled = false }) } // chained upstream computeds are notified synchronously to ensure @@ -61,15 +63,14 @@ class DeferredComputedRefImpl { } } } - this.effect._dirty = true + this._dirty = true }) - this.effect._dirty = true this.effect.computed = this as any } private _get() { - if (this.effect.dirty) { - this.effect._dirty = false + if (this._dirty) { + this._dirty = false return (this._value = this.effect.run()!) } return this._value diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 448995e736d..6dd10ad503e 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -55,7 +55,7 @@ export function trackRefValue(ref: RefBase) { export function triggerRefValue( ref: RefBase, - deferredComputed: ComputedRefImpl | undefined, + deferredComputed?: ComputedRefImpl, newVal?: any ) { ref = toRaw(ref) @@ -143,10 +143,7 @@ class RefImpl { public dep?: Dep = undefined public readonly __v_isRef = true - constructor( - value: T, - public readonly __v_isShallow: boolean - ) { + constructor(value: T, public readonly __v_isShallow: boolean) { this._rawValue = __v_isShallow ? value : toRaw(value) this._value = __v_isShallow ? value : toReactive(value) } From 1b666b9c9271723b18c6b05e460b859b5fa084db Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 27 Aug 2023 05:31:34 +0800 Subject: [PATCH 044/110] chore: format --- packages/reactivity/src/ref.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 6dd10ad503e..66431acb812 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -143,7 +143,10 @@ class RefImpl { public dep?: Dep = undefined public readonly __v_isRef = true - constructor(value: T, public readonly __v_isShallow: boolean) { + constructor( + value: T, + public readonly __v_isShallow: boolean + ) { this._rawValue = __v_isShallow ? value : toRaw(value) this._value = __v_isShallow ? value : toReactive(value) } From bf89e57b83cb9b3eb77587bb53b8ffe3ea33c361 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 27 Aug 2023 05:56:26 +0800 Subject: [PATCH 045/110] perf: more efficient `watch()` --- .../runtime-core/__tests__/apiWatch.spec.ts | 25 +++++++++++++++++++ packages/runtime-core/src/apiWatch.ts | 13 +++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/__tests__/apiWatch.spec.ts b/packages/runtime-core/__tests__/apiWatch.spec.ts index f24ce80b9df..7ead1011e25 100644 --- a/packages/runtime-core/__tests__/apiWatch.spec.ts +++ b/packages/runtime-core/__tests__/apiWatch.spec.ts @@ -1205,4 +1205,29 @@ describe('api: watch', () => { expect(countWE).toBe(3) expect(countW).toBe(2) }) + + it('watch callback on-demand trigger', () => { + const effectSpy = vi.fn() + + const sec = ref(0) + const min = computed(() => { + return Math.floor(sec.value / 60) + }) + const hour = computed(() => { + return Math.floor(min.value / 60) + }) + + watchEffect( + () => { + effectSpy() + min.value + hour.value + }, + { flush: 'sync' } + ) + + for (sec.value = 0; sec.value < 1000; sec.value++) {} + + expect(effectSpy).toHaveBeenCalledTimes(17) + }) }) diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts index 1b85ba12d19..991c7573ffb 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -310,6 +310,11 @@ function doWatch( if (!effect.active) { return } + effect._scheduled = false + if (!effect.dirty) { + return + } + effect._dirty = false if (cb) { // watch(source, cb) const newValue = effect.run() @@ -361,7 +366,13 @@ function doWatch( scheduler = () => queueJob(job) } - const effect = new ReactiveEffect(getter, scheduler) + const effect = new ReactiveEffect(getter, () => { + effect._scheduled = true + scheduler() + }) + if (immediate) { + effect._dirty = true + } if (__DEV__) { effect.onTrack = onTrack From 09e35124dac32af39b8acd174f33dba66db17e56 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 27 Aug 2023 06:32:25 +0800 Subject: [PATCH 046/110] chore(effect): use allow function --- packages/reactivity/src/effect.ts | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 38336e5fd66..77915de50b0 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -216,7 +216,13 @@ export function effect( const _effect = new ReactiveEffect(fn, () => { _effect._scheduled = true - pendingEffectRunners.push(run) + queueEffectCbs.push(() => { + if (_effect.dirty) { + _effect.run() + _effect._dirty = false + } + _effect._scheduled = false + }) }) if (options) { extend(_effect, options) @@ -228,14 +234,6 @@ export function effect( const runner = _effect.run.bind(_effect) as ReactiveEffectRunner runner.effect = _effect return runner - - function run() { - if (_effect.dirty) { - _effect.run() - _effect._dirty = false - } - _effect._scheduled = false - } } /** @@ -449,7 +447,7 @@ export function triggerEffects( } } -const pendingEffectRunners: (() => void)[] = [] +const queueEffectCbs: (() => void)[] = [] function triggerEffect( effect: ReactiveEffect, @@ -477,8 +475,8 @@ function triggerEffect( } } if (isRootTrigger) { - while (pendingEffectRunners.length) { - pendingEffectRunners.shift()!() + while (queueEffectCbs.length) { + queueEffectCbs.shift()!() } } } From 25578fdceaf7fc9c1132358d1ebc9563cabe32c5 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 27 Aug 2023 06:35:09 +0800 Subject: [PATCH 047/110] chore(effect): release _deferredComputeds earlier --- packages/reactivity/src/effect.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 77915de50b0..a1d3ec53405 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -465,6 +465,7 @@ function triggerEffect( effect._deferredComputeds.push(deferredComputed) } else { effect._dirty = true + effect._deferredComputeds.length = 0 } } if (!effect._scheduled) { From 4b72c94d03e76f75702f146804da2a072bcb14a8 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 27 Aug 2023 07:35:02 +0800 Subject: [PATCH 048/110] refactor(effect): make `scheduler` required --- packages/reactivity/src/deferredComputed.ts | 2 +- packages/reactivity/src/effect.ts | 24 +++++++++------------ 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/packages/reactivity/src/deferredComputed.ts b/packages/reactivity/src/deferredComputed.ts index a23122046a4..16f708c2b75 100644 --- a/packages/reactivity/src/deferredComputed.ts +++ b/packages/reactivity/src/deferredComputed.ts @@ -59,7 +59,7 @@ class DeferredComputedRefImpl { // deferred to be triggered in scheduler. for (const e of this.dep) { if (e.computed instanceof DeferredComputedRefImpl) { - e.scheduler!(true /* computedTrigger */) + e.scheduler(true /* computedTrigger */) } } } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index a1d3ec53405..04a1a24b50d 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -106,7 +106,7 @@ export class ReactiveEffect { constructor( public fn: () => T, - public scheduler: EffectScheduler | null = null, + public scheduler: EffectScheduler, scope?: EffectScope ) { recordEffectScope(this, scope) @@ -459,20 +459,16 @@ function triggerEffect( if (__DEV__ && effect.onTrigger) { effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) } - if (effect.scheduler) { - if (!effect._dirty) { - if (deferredComputed) { - effect._deferredComputeds.push(deferredComputed) - } else { - effect._dirty = true - effect._deferredComputeds.length = 0 - } - } - if (!effect._scheduled) { - effect.scheduler() + if (!effect._dirty) { + if (deferredComputed) { + effect._deferredComputeds.push(deferredComputed) + } else { + effect._dirty = true + effect._deferredComputeds.length = 0 } - } else { - effect.run() + } + if (!effect._scheduled) { + effect.scheduler() } } if (isRootTrigger) { From 0257e69abcc572390f8ce666b7ab55759c8cfd9c Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 27 Aug 2023 10:00:44 +0800 Subject: [PATCH 049/110] refactor(effect): dirty -> applyDirty() --- packages/reactivity/src/computed.ts | 10 +++---- packages/reactivity/src/effect.ts | 42 +++++++++++++-------------- packages/runtime-core/src/apiWatch.ts | 10 +++---- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index c1ae41ba48f..186e8d51266 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -41,10 +41,10 @@ export class ComputedRefImpl { isSSR: boolean ) { this.effect = new ReactiveEffect(getter, () => { - this.effect._scheduled = true + this.effect.scheduled = true triggerRefValue(this, this) }) - this.effect._dirty = true + this.effect.dirty = true this.effect.computed = this this.effect.active = this._cacheable = !isSSR this[ReactiveFlags.IS_READONLY] = isReadonly @@ -54,14 +54,14 @@ export class ComputedRefImpl { // the computed ref may get wrapped by other proxies e.g. readonly() #3376 const self = toRaw(this) trackRefValue(self) - if (!self._cacheable || self.effect.dirty) { + if (!self._cacheable || self.effect.applyDirty()) { const newValue = self.effect.run()! if (hasChanged(self._value, newValue)) { triggerRefValue(self, undefined) } self._value = newValue - self.effect._dirty = false - self.effect._scheduled = false + self.effect.dirty = false + self.effect.scheduled = false } return self._value } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 04a1a24b50d..f9466ec5bcc 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -75,13 +75,21 @@ export class ReactiveEffect { // dev only onTrigger?: (event: DebuggerEvent) => void - public _dirty = false - public _scheduled = false + public dirty = false + public scheduled = false public _deferredComputeds: ComputedRefImpl[] = [] private _depIndexes = new Map() - public get dirty() { - if (!this._dirty && this._deferredComputeds.length) { + constructor( + public fn: () => T, + public scheduler: EffectScheduler, + scope?: EffectScope + ) { + recordEffectScope(this, scope) + } + + public applyDirty() { + if (!this.dirty && this._deferredComputeds.length) { if (this._deferredComputeds.length >= 2) { for (const { dep } of this._deferredComputeds) { this._depIndexes.set(dep, this.deps.indexOf(dep!)) @@ -94,22 +102,14 @@ export class ReactiveEffect { pauseTracking() for (const deferredComputed of this._deferredComputeds) { deferredComputed.value - if (this._dirty) { + if (this.dirty) { break } } resetTracking() } this._deferredComputeds.length = 0 - return this._dirty - } - - constructor( - public fn: () => T, - public scheduler: EffectScheduler, - scope?: EffectScope - ) { - recordEffectScope(this, scope) + return this.dirty } run() { @@ -215,13 +215,13 @@ export function effect( } const _effect = new ReactiveEffect(fn, () => { - _effect._scheduled = true + _effect.scheduled = true queueEffectCbs.push(() => { - if (_effect.dirty) { + if (_effect.applyDirty()) { _effect.run() - _effect._dirty = false + _effect.dirty = false } - _effect._scheduled = false + _effect.scheduled = false }) }) if (options) { @@ -459,15 +459,15 @@ function triggerEffect( if (__DEV__ && effect.onTrigger) { effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) } - if (!effect._dirty) { + if (!effect.dirty) { if (deferredComputed) { effect._deferredComputeds.push(deferredComputed) } else { - effect._dirty = true + effect.dirty = true effect._deferredComputeds.length = 0 } } - if (!effect._scheduled) { + if (!effect.scheduled) { effect.scheduler() } } diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts index 991c7573ffb..b7530d3cb31 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -310,11 +310,11 @@ function doWatch( if (!effect.active) { return } - effect._scheduled = false - if (!effect.dirty) { + effect.scheduled = false + if (!effect.applyDirty()) { return } - effect._dirty = false + effect.dirty = false if (cb) { // watch(source, cb) const newValue = effect.run() @@ -367,11 +367,11 @@ function doWatch( } const effect = new ReactiveEffect(getter, () => { - effect._scheduled = true + effect.scheduled = true scheduler() }) if (immediate) { - effect._dirty = true + effect.dirty = true } if (__DEV__) { From 37d5c031a001bd3eb608b4e6b5b82f4993ec1e49 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 27 Aug 2023 10:04:23 +0800 Subject: [PATCH 050/110] chore(effect): applyDirty() -> dirty, _dirty --- packages/reactivity/src/computed.ts | 2 +- packages/reactivity/src/effect.ts | 20 ++++++++++++-------- packages/runtime-core/src/apiWatch.ts | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 186e8d51266..fb979e85a08 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -54,7 +54,7 @@ export class ComputedRefImpl { // the computed ref may get wrapped by other proxies e.g. readonly() #3376 const self = toRaw(this) trackRefValue(self) - if (!self._cacheable || self.effect.applyDirty()) { + if (!self._cacheable || self.effect.dirty) { const newValue = self.effect.run()! if (hasChanged(self._value, newValue)) { triggerRefValue(self, undefined) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index f9466ec5bcc..c73938970b2 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -75,7 +75,7 @@ export class ReactiveEffect { // dev only onTrigger?: (event: DebuggerEvent) => void - public dirty = false + public _dirty = false public scheduled = false public _deferredComputeds: ComputedRefImpl[] = [] private _depIndexes = new Map() @@ -88,8 +88,8 @@ export class ReactiveEffect { recordEffectScope(this, scope) } - public applyDirty() { - if (!this.dirty && this._deferredComputeds.length) { + public get dirty() { + if (!this._dirty && this._deferredComputeds.length) { if (this._deferredComputeds.length >= 2) { for (const { dep } of this._deferredComputeds) { this._depIndexes.set(dep, this.deps.indexOf(dep!)) @@ -102,14 +102,18 @@ export class ReactiveEffect { pauseTracking() for (const deferredComputed of this._deferredComputeds) { deferredComputed.value - if (this.dirty) { + if (this._dirty) { break } } resetTracking() } this._deferredComputeds.length = 0 - return this.dirty + return this._dirty + } + + public set dirty(value) { + this._dirty = value } run() { @@ -217,7 +221,7 @@ export function effect( const _effect = new ReactiveEffect(fn, () => { _effect.scheduled = true queueEffectCbs.push(() => { - if (_effect.applyDirty()) { + if (_effect.dirty) { _effect.run() _effect.dirty = false } @@ -459,11 +463,11 @@ function triggerEffect( if (__DEV__ && effect.onTrigger) { effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) } - if (!effect.dirty) { + if (!effect._dirty) { if (deferredComputed) { effect._deferredComputeds.push(deferredComputed) } else { - effect.dirty = true + effect._dirty = true effect._deferredComputeds.length = 0 } } diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts index b7530d3cb31..3c46ae64d3b 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -311,7 +311,7 @@ function doWatch( return } effect.scheduled = false - if (!effect.applyDirty()) { + if (!effect.dirty) { return } effect.dirty = false From 1269644006cef9cb505eb0208fa2715e3da34593 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 27 Aug 2023 20:52:21 +0800 Subject: [PATCH 051/110] chore(effect): add triggerEffectCallbacks() --- packages/reactivity/src/effect.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index c73938970b2..3890f585ddc 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -458,7 +458,6 @@ function triggerEffect( deferredComputed: ComputedRefImpl | undefined, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { - const isRootTrigger = effectTrackDepth === 0 if (effect !== activeEffect || effect.allowRecurse) { if (__DEV__ && effect.onTrigger) { effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) @@ -475,7 +474,11 @@ function triggerEffect( effect.scheduler() } } - if (isRootTrigger) { + triggerEffectCallbacks() +} + +function triggerEffectCallbacks() { + if (effectTrackDepth === 0) { while (queueEffectCbs.length) { queueEffectCbs.shift()!() } From 07da2ddf800e8200ca707343dd7c840de1cdae4a Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 27 Aug 2023 21:07:07 +0800 Subject: [PATCH 052/110] chore(effect): triggerEffectCallbacks -> scheduleEffectCallbacks --- packages/reactivity/src/effect.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 3890f585ddc..852a19f78d9 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -474,11 +474,11 @@ function triggerEffect( effect.scheduler() } } - triggerEffectCallbacks() + scheduleEffectCallbacks() } -function triggerEffectCallbacks() { if (effectTrackDepth === 0) { +function scheduleEffectCallbacks() { while (queueEffectCbs.length) { queueEffectCbs.shift()!() } From 891d937ee3e81d32c60425f80a99f5ff61ef667a Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 27 Aug 2023 21:11:53 +0800 Subject: [PATCH 053/110] chore(effect): fix syntax --- packages/reactivity/src/effect.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 852a19f78d9..349c364355f 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -477,8 +477,8 @@ function triggerEffect( scheduleEffectCallbacks() } - if (effectTrackDepth === 0) { function scheduleEffectCallbacks() { + if (effectTrackDepth === 0) { while (queueEffectCbs.length) { queueEffectCbs.shift()!() } From 3350c24b1fe53e2d71e2f52967abf09c4cd768df Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 27 Aug 2023 21:27:36 +0800 Subject: [PATCH 054/110] perf(reactivity): Array `shift()` should only trigger effect once --- .../reactivity/__tests__/reactiveArray.spec.ts | 14 ++++++++++++++ packages/reactivity/src/baseHandlers.ts | 6 +++++- packages/reactivity/src/effect.ts | 16 +++++++++++++++- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/__tests__/reactiveArray.spec.ts b/packages/reactivity/__tests__/reactiveArray.spec.ts index 808c5aa5529..8bc7ba1433a 100644 --- a/packages/reactivity/__tests__/reactiveArray.spec.ts +++ b/packages/reactivity/__tests__/reactiveArray.spec.ts @@ -99,6 +99,20 @@ describe('reactivity/reactive/Array', () => { expect(fn).toHaveBeenCalledTimes(1) }) + test('shift on Array should only trigger dependency once', () => { + const arr = reactive([1, 2, 3]) + const fn = vi.fn() + effect(() => { + for (let i = 0; i < arr.length; i++) { + arr[i] + } + fn() + }) + expect(fn).toHaveBeenCalledTimes(1) + arr.shift() + expect(fn).toHaveBeenCalledTimes(2) + }) + test('add existing index on Array should not trigger length dependency', () => { const array = new Array(3) const observed = reactive(array) diff --git a/packages/reactivity/src/baseHandlers.ts b/packages/reactivity/src/baseHandlers.ts index 259b44a1edc..264ba36cf83 100644 --- a/packages/reactivity/src/baseHandlers.ts +++ b/packages/reactivity/src/baseHandlers.ts @@ -17,7 +17,9 @@ import { trigger, ITERATE_KEY, pauseTracking, - resetTracking + resetTracking, + pauseScheduling, + resetScheduling } from './effect' import { isObject, @@ -71,7 +73,9 @@ function createArrayInstrumentations() { ;(['push', 'pop', 'shift', 'unshift', 'splice'] as const).forEach(key => { instrumentations[key] = function (this: unknown[], ...args: unknown[]) { pauseTracking() + pauseScheduling() const res = (toRaw(this) as any)[key].apply(this, args) + resetScheduling() resetTracking() return res } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 349c364355f..2804e905941 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -250,7 +250,10 @@ export function stop(runner: ReactiveEffectRunner) { } export let shouldTrack = true +export let shouldSchedule = true + const trackStack: boolean[] = [] +const scheduleStack: boolean[] = [] /** * Temporarily pauses tracking. @@ -276,6 +279,17 @@ export function resetTracking() { shouldTrack = last === undefined ? true : last } +export function pauseScheduling() { + scheduleStack.push(shouldSchedule) + shouldSchedule = false +} + +export function resetScheduling() { + const last = scheduleStack.pop() + shouldSchedule = last === undefined ? true : last + scheduleEffectCallbacks() +} + /** * Tracks access to a reactive property. * @@ -478,7 +492,7 @@ function triggerEffect( } function scheduleEffectCallbacks() { - if (effectTrackDepth === 0) { + if (effectTrackDepth === 0 && shouldSchedule) { while (queueEffectCbs.length) { queueEffectCbs.shift()!() } From c187177e9427e308f7fe7ac41907ea2d1eb88b7f Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 28 Aug 2023 17:52:46 +0800 Subject: [PATCH 055/110] refactor(effect): de-abstract `scheduled` --- packages/reactivity/src/computed.ts | 10 +++++++--- packages/reactivity/src/effect.ts | 25 +++++++++++++------------ packages/runtime-core/src/apiWatch.ts | 15 ++++++++------- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index fb979e85a08..f4fd188179f 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -34,6 +34,8 @@ export class ComputedRefImpl { public _cacheable: boolean + private scheduled = false + constructor( getter: ComputedGetter, private readonly _setter: ComputedSetter, @@ -41,8 +43,10 @@ export class ComputedRefImpl { isSSR: boolean ) { this.effect = new ReactiveEffect(getter, () => { - this.effect.scheduled = true - triggerRefValue(this, this) + if (!this.scheduled) { + this.scheduled = true + triggerRefValue(this, this) + } }) this.effect.dirty = true this.effect.computed = this @@ -60,8 +64,8 @@ export class ComputedRefImpl { triggerRefValue(self, undefined) } self._value = newValue + self.scheduled = false self.effect.dirty = false - self.effect.scheduled = false } return self._value } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 349c364355f..17eac5598ab 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -76,7 +76,6 @@ export class ReactiveEffect { onTrigger?: (event: DebuggerEvent) => void public _dirty = false - public scheduled = false public _deferredComputeds: ComputedRefImpl[] = [] private _depIndexes = new Map() @@ -218,15 +217,19 @@ export function effect( fn = (fn as ReactiveEffectRunner).effect.fn } + let scheduled = false + const _effect = new ReactiveEffect(fn, () => { - _effect.scheduled = true - queueEffectCbs.push(() => { - if (_effect.dirty) { - _effect.run() - _effect.dirty = false - } - _effect.scheduled = false - }) + if (!scheduled) { + scheduled = true + queueEffectCbs.push(() => { + if (_effect.dirty) { + _effect.run() + _effect.dirty = false + } + scheduled = false + }) + } }) if (options) { extend(_effect, options) @@ -470,9 +473,7 @@ function triggerEffect( effect._deferredComputeds.length = 0 } } - if (!effect.scheduled) { - effect.scheduler() - } + effect.scheduler() } scheduleEffectCallbacks() } diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts index 3c46ae64d3b..50ec7c310b0 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -307,11 +307,8 @@ function doWatch( ? new Array((source as []).length).fill(INITIAL_WATCHER_VALUE) : INITIAL_WATCHER_VALUE const job: SchedulerJob = () => { - if (!effect.active) { - return - } - effect.scheduled = false - if (!effect.dirty) { + scheduled = false + if (!effect.active || !effect.dirty) { return } effect.dirty = false @@ -366,9 +363,13 @@ function doWatch( scheduler = () => queueJob(job) } + let scheduled = false + const effect = new ReactiveEffect(getter, () => { - effect.scheduled = true - scheduler() + if (!scheduled) { + scheduled = true + scheduler() + } }) if (immediate) { effect.dirty = true From 8f58ea33d795888fcef0fec77cce11f7bb3f3bde Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 28 Aug 2023 18:23:17 +0800 Subject: [PATCH 056/110] chore(effect): mark dirty by default --- packages/reactivity/src/computed.ts | 1 - packages/reactivity/src/effect.ts | 3 ++- packages/runtime-core/src/apiWatch.ts | 10 ++++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index f4fd188179f..e33c23bff04 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -48,7 +48,6 @@ export class ComputedRefImpl { triggerRefValue(this, this) } }) - this.effect.dirty = true this.effect.computed = this this.effect.active = this._cacheable = !isSSR this[ReactiveFlags.IS_READONLY] = isReadonly diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 17eac5598ab..633e4f160c1 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -75,7 +75,7 @@ export class ReactiveEffect { // dev only onTrigger?: (event: DebuggerEvent) => void - public _dirty = false + public _dirty = true public _deferredComputeds: ComputedRefImpl[] = [] private _depIndexes = new Map() @@ -237,6 +237,7 @@ export function effect( } if (!options || !options.lazy) { _effect.run() + _effect.dirty = false } const runner = _effect.run.bind(_effect) as ReactiveEffectRunner runner.effect = _effect diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts index 50ec7c310b0..61afd9f9dee 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -371,9 +371,6 @@ function doWatch( scheduler() } }) - if (immediate) { - effect.dirty = true - } if (__DEV__) { effect.onTrack = onTrack @@ -386,14 +383,19 @@ function doWatch( job() } else { oldValue = effect.run() + effect.dirty = false } } else if (flush === 'post') { queuePostRenderEffect( - effect.run.bind(effect), + () => { + effect.run() + effect.dirty = false + }, instance && instance.suspense ) } else { effect.run() + effect.dirty = false } const unwatch = () => { From 770b2de4e72595a5788248fc24ca27b718aeb863 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 28 Aug 2023 18:30:14 +0800 Subject: [PATCH 057/110] refactor(effect): abstract `dirty` setter --- packages/reactivity/src/computed.ts | 1 - packages/reactivity/src/effect.ts | 7 +------ packages/runtime-core/src/apiWatch.ts | 8 +------- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index e33c23bff04..f44e29b0f34 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -64,7 +64,6 @@ export class ComputedRefImpl { } self._value = newValue self.scheduled = false - self.effect.dirty = false } return self._value } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 633e4f160c1..f20e3d3fbb1 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -111,11 +111,8 @@ export class ReactiveEffect { return this._dirty } - public set dirty(value) { - this._dirty = value - } - run() { + this._dirty = false if (!this.active) { return this.fn() } @@ -225,7 +222,6 @@ export function effect( queueEffectCbs.push(() => { if (_effect.dirty) { _effect.run() - _effect.dirty = false } scheduled = false }) @@ -237,7 +233,6 @@ export function effect( } if (!options || !options.lazy) { _effect.run() - _effect.dirty = false } const runner = _effect.run.bind(_effect) as ReactiveEffectRunner runner.effect = _effect diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts index 61afd9f9dee..8023fdc4331 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -311,7 +311,6 @@ function doWatch( if (!effect.active || !effect.dirty) { return } - effect.dirty = false if (cb) { // watch(source, cb) const newValue = effect.run() @@ -383,19 +382,14 @@ function doWatch( job() } else { oldValue = effect.run() - effect.dirty = false } } else if (flush === 'post') { queuePostRenderEffect( - () => { - effect.run() - effect.dirty = false - }, + effect.run.bind(effect), instance && instance.suspense ) } else { effect.run() - effect.dirty = false } const unwatch = () => { From e5e895d16a7bc1fdbab10d0644f918dd230ee37d Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 28 Aug 2023 20:21:21 +0800 Subject: [PATCH 058/110] fix(effect): revert updates for watch apis --- .../runtime-core/__tests__/apiWatch.spec.ts | 25 ------------------- packages/runtime-core/src/apiWatch.ts | 12 ++------- 2 files changed, 2 insertions(+), 35 deletions(-) diff --git a/packages/runtime-core/__tests__/apiWatch.spec.ts b/packages/runtime-core/__tests__/apiWatch.spec.ts index 7ead1011e25..f24ce80b9df 100644 --- a/packages/runtime-core/__tests__/apiWatch.spec.ts +++ b/packages/runtime-core/__tests__/apiWatch.spec.ts @@ -1205,29 +1205,4 @@ describe('api: watch', () => { expect(countWE).toBe(3) expect(countW).toBe(2) }) - - it('watch callback on-demand trigger', () => { - const effectSpy = vi.fn() - - const sec = ref(0) - const min = computed(() => { - return Math.floor(sec.value / 60) - }) - const hour = computed(() => { - return Math.floor(min.value / 60) - }) - - watchEffect( - () => { - effectSpy() - min.value - hour.value - }, - { flush: 'sync' } - ) - - for (sec.value = 0; sec.value < 1000; sec.value++) {} - - expect(effectSpy).toHaveBeenCalledTimes(17) - }) }) diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts index 8023fdc4331..1b85ba12d19 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -307,8 +307,7 @@ function doWatch( ? new Array((source as []).length).fill(INITIAL_WATCHER_VALUE) : INITIAL_WATCHER_VALUE const job: SchedulerJob = () => { - scheduled = false - if (!effect.active || !effect.dirty) { + if (!effect.active) { return } if (cb) { @@ -362,14 +361,7 @@ function doWatch( scheduler = () => queueJob(job) } - let scheduled = false - - const effect = new ReactiveEffect(getter, () => { - if (!scheduled) { - scheduled = true - scheduler() - } - }) + const effect = new ReactiveEffect(getter, scheduler) if (__DEV__) { effect.onTrack = onTrack From 3fc6349d4f9ca7a7d32aea0b2b687b579caf8745 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 28 Aug 2023 21:44:21 +0800 Subject: [PATCH 059/110] chore: remove unneeded change --- packages/reactivity/src/ref.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 66431acb812..91826f35600 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -290,7 +290,7 @@ class CustomRefImpl { constructor(factory: CustomRefFactory) { const { get, set } = factory( () => trackRefValue(this), - () => triggerRefValue(this, undefined) + () => triggerRefValue(this) ) this._get = get this._set = set From 382168787e63d9cbe6b5d30973044e3636d6a5c7 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 28 Aug 2023 22:15:14 +0800 Subject: [PATCH 060/110] fix(computed): polyfill `_dirty` --- packages/reactivity/src/computed.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index f44e29b0f34..6fc752f3ccc 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -71,6 +71,16 @@ export class ComputedRefImpl { set value(newValue: T) { this._setter(newValue) } + + // #region polyfill _dirty to backward compatibility for Vue <= 3.3 + get _dirty() { + return this.effect.dirty + } + + set _dirty(v) { + this.effect._dirty = v + } + // #endregion } /** From eb360e6cb76eef5178ca910ef42274bb7c420dfd Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 29 Aug 2023 03:56:23 +0800 Subject: [PATCH 061/110] perf(runtime-core): re-render on deps changed --- packages/runtime-core/src/apiAsyncComponent.ts | 1 + packages/runtime-core/src/componentPublicInstance.ts | 5 ++++- packages/runtime-core/src/components/BaseTransition.ts | 1 + packages/runtime-core/src/hmr.ts | 2 ++ packages/runtime-core/src/renderer.ts | 7 ++++++- 5 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/src/apiAsyncComponent.ts b/packages/runtime-core/src/apiAsyncComponent.ts index 342339042ef..0dd785a1862 100644 --- a/packages/runtime-core/src/apiAsyncComponent.ts +++ b/packages/runtime-core/src/apiAsyncComponent.ts @@ -187,6 +187,7 @@ export function defineAsyncComponent< if (instance.parent && isKeepAlive(instance.parent.vnode)) { // parent is keep-alive, force update so the loaded component's // name is taken into account + instance.parent.effect._dirty = true queueJob(instance.parent.update) } }) diff --git a/packages/runtime-core/src/componentPublicInstance.ts b/packages/runtime-core/src/componentPublicInstance.ts index dc575aafff9..01d946e4d71 100644 --- a/packages/runtime-core/src/componentPublicInstance.ts +++ b/packages/runtime-core/src/componentPublicInstance.ts @@ -269,7 +269,10 @@ export const publicPropertiesMap: PublicPropertiesMap = $root: i => getPublicInstance(i.root), $emit: i => i.emit, $options: i => (__FEATURE_OPTIONS_API__ ? resolveMergedOptions(i) : i.type), - $forceUpdate: i => i.f || (i.f = () => queueJob(i.update)), + $forceUpdate: i => i.f || (i.f = () => { + i.effect._dirty = true + queueJob(i.update) + }), $nextTick: i => i.n || (i.n = nextTick.bind(i.proxy!)), $watch: i => (__FEATURE_OPTIONS_API__ ? instanceWatch.bind(i) : NOOP) } as PublicPropertiesMap) diff --git a/packages/runtime-core/src/components/BaseTransition.ts b/packages/runtime-core/src/components/BaseTransition.ts index 9cb80b94ef0..6d03edf0936 100644 --- a/packages/runtime-core/src/components/BaseTransition.ts +++ b/packages/runtime-core/src/components/BaseTransition.ts @@ -246,6 +246,7 @@ const BaseTransitionImpl: ComponentOptions = { // #6835 // it also needs to be updated when active is undefined if (instance.update.active !== false) { + instance.effect._dirty = true instance.update() } } diff --git a/packages/runtime-core/src/hmr.ts b/packages/runtime-core/src/hmr.ts index 1ce66a3da1e..f95b2414eb4 100644 --- a/packages/runtime-core/src/hmr.ts +++ b/packages/runtime-core/src/hmr.ts @@ -93,6 +93,7 @@ function rerender(id: string, newRender?: Function) { instance.renderCache = [] // this flag forces child components with slot content to update isHmrUpdating = true + instance.effect._dirty = true instance.update() isHmrUpdating = false }) @@ -137,6 +138,7 @@ function reload(id: string, newComp: HMRComponent) { // 4. Force the parent instance to re-render. This will cause all updated // components to be unmounted and re-mounted. Queue the update so that we // don't end up forcing the same parent to re-render multiple times. + instance.parent.effect._dirty = true queueJob(instance.parent.update) } else if (instance.appContext.reload) { // root instance mounted via createApp() has a reload method diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 383e17fb0f5..0f036dade99 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -1282,6 +1282,7 @@ function baseCreateRenderer( // double updating the same child component in the same flush. invalidateJob(instance.update) // instance.update is the reactive effect. + instance.effect._dirty = true instance.update() } } else { @@ -1550,7 +1551,11 @@ function baseCreateRenderer( instance.scope // track it in component's effect scope )) - const update: SchedulerJob = (instance.update = () => effect.run()) + const update: SchedulerJob = (instance.update = () => { + if (effect.dirty) { + effect.run() + } + }) update.id = instance.uid // allowRecurse // #1801, #2043 component render effects should allow recursive updates From 128b1eb0ae3ae1086b4194e2099ac6de44ab0cff Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 29 Aug 2023 04:06:13 +0800 Subject: [PATCH 062/110] chore: format --- packages/runtime-core/src/componentPublicInstance.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/runtime-core/src/componentPublicInstance.ts b/packages/runtime-core/src/componentPublicInstance.ts index 01d946e4d71..26a2eb21def 100644 --- a/packages/runtime-core/src/componentPublicInstance.ts +++ b/packages/runtime-core/src/componentPublicInstance.ts @@ -269,10 +269,12 @@ export const publicPropertiesMap: PublicPropertiesMap = $root: i => getPublicInstance(i.root), $emit: i => i.emit, $options: i => (__FEATURE_OPTIONS_API__ ? resolveMergedOptions(i) : i.type), - $forceUpdate: i => i.f || (i.f = () => { - i.effect._dirty = true - queueJob(i.update) - }), + $forceUpdate: i => + i.f || + (i.f = () => { + i.effect._dirty = true + queueJob(i.update) + }), $nextTick: i => i.n || (i.n = nextTick.bind(i.proxy!)), $watch: i => (__FEATURE_OPTIONS_API__ ? instanceWatch.bind(i) : NOOP) } as PublicPropertiesMap) From a8f2bbafb0a1ee42f1423e41beae05fb0fde476b Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 29 Aug 2023 06:10:23 +0800 Subject: [PATCH 063/110] fix: computed causes render twice at the beginning --- packages/reactivity/src/computed.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 6fc752f3ccc..d7e4c26f4d7 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -34,6 +34,7 @@ export class ComputedRefImpl { public _cacheable: boolean + private init = false private scheduled = false constructor( @@ -45,7 +46,12 @@ export class ComputedRefImpl { this.effect = new ReactiveEffect(getter, () => { if (!this.scheduled) { this.scheduled = true - triggerRefValue(this, this) + if (!this.init) { + triggerRefValue(this, undefined) + } + else { + triggerRefValue(this, this) + } } }) this.effect.computed = this @@ -59,7 +65,10 @@ export class ComputedRefImpl { trackRefValue(self) if (!self._cacheable || self.effect.dirty) { const newValue = self.effect.run()! - if (hasChanged(self._value, newValue)) { + if (!self.init) { + self.init = true + } + else if (hasChanged(self._value, newValue)) { triggerRefValue(self, undefined) } self._value = newValue From 3aaa3af802a9d6ad6a393593303e732e23a52e94 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 29 Aug 2023 06:11:50 +0800 Subject: [PATCH 064/110] chore: format --- packages/reactivity/src/computed.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index d7e4c26f4d7..6de0cfa3d97 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -48,8 +48,7 @@ export class ComputedRefImpl { this.scheduled = true if (!this.init) { triggerRefValue(this, undefined) - } - else { + } else { triggerRefValue(this, this) } } @@ -67,8 +66,7 @@ export class ComputedRefImpl { const newValue = self.effect.run()! if (!self.init) { self.init = true - } - else if (hasChanged(self._value, newValue)) { + } else if (hasChanged(self._value, newValue)) { triggerRefValue(self, undefined) } self._value = newValue From 1be6c50e603b422b46f9dcfff3e4a217af7633b0 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 29 Aug 2023 09:04:34 +0800 Subject: [PATCH 065/110] fix: multiple computed triggers render multiple times --- packages/runtime-core/src/renderer.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 0f036dade99..5f4538a0ace 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -1544,10 +1544,17 @@ function baseCreateRenderer( } } + let scheduled = false + // create reactive effect for rendering const effect = (instance.effect = new ReactiveEffect( componentUpdateFn, - () => queueJob(update), + () => { + if (!scheduled) { + scheduled = true + queueJob(update) + } + }, instance.scope // track it in component's effect scope )) @@ -1555,6 +1562,7 @@ function baseCreateRenderer( if (effect.dirty) { effect.run() } + scheduled = false }) update.id = instance.uid // allowRecurse From d2a57068f0074d1fb40fbaea1776e03e392432f5 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 29 Aug 2023 11:24:47 +0800 Subject: [PATCH 066/110] fix: computed getter trigger effect repeatedly --- packages/reactivity/src/computed.ts | 8 +-- packages/reactivity/src/deferredComputed.ts | 60 ++++++++++++--------- packages/reactivity/src/effect.ts | 52 ++++++++++++++---- packages/reactivity/src/index.ts | 1 + packages/reactivity/src/ref.ts | 24 ++++++--- packages/runtime-core/src/renderer.ts | 11 ++-- 6 files changed, 109 insertions(+), 47 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 6de0cfa3d97..fd57976de4f 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -1,4 +1,4 @@ -import { DebuggerOptions, ReactiveEffect } from './effect' +import { DebuggerOptions, ReactiveEffect, TriggerReason } from './effect' import { Ref, trackRefValue, triggerRefValue } from './ref' import { hasChanged, isFunction, NOOP } from '@vue/shared' import { ReactiveFlags, toRaw } from './reactive' @@ -47,9 +47,9 @@ export class ComputedRefImpl { if (!this.scheduled) { this.scheduled = true if (!this.init) { - triggerRefValue(this, undefined) + triggerRefValue(this, TriggerReason.ComputedDepsUpdated, undefined) } else { - triggerRefValue(this, this) + triggerRefValue(this, TriggerReason.ValueUpdatedByGetter, this) } } }) @@ -67,7 +67,7 @@ export class ComputedRefImpl { if (!self.init) { self.init = true } else if (hasChanged(self._value, newValue)) { - triggerRefValue(self, undefined) + triggerRefValue(self, TriggerReason.ValueUpdatedByGetter, undefined) } self._value = newValue self.scheduled = false diff --git a/packages/reactivity/src/deferredComputed.ts b/packages/reactivity/src/deferredComputed.ts index 16f708c2b75..02b0073d3c8 100644 --- a/packages/reactivity/src/deferredComputed.ts +++ b/packages/reactivity/src/deferredComputed.ts @@ -1,5 +1,5 @@ import { Dep } from './dep' -import { ReactiveEffect } from './effect' +import { ReactiveEffect, TriggerReason } from './effect' import { ComputedGetter, ComputedRef } from './computed' import { ReactiveFlags, toRaw } from './reactive' import { trackRefValue, triggerRefValue } from './ref' @@ -38,33 +38,45 @@ class DeferredComputedRefImpl { let compareTarget: any let hasCompareTarget = false let scheduled = false - this.effect = new ReactiveEffect(getter, (computedTrigger?: boolean) => { - if (this.dep) { - if (computedTrigger) { - compareTarget = this._value - hasCompareTarget = true - } else if (!scheduled) { - const valueToCompare = hasCompareTarget ? compareTarget : this._value - scheduled = true - hasCompareTarget = false - scheduler(() => { - if (this.effect.active && this._get() !== valueToCompare) { - triggerRefValue(this) + this.effect = new ReactiveEffect( + getter, + (_mode, computedTrigger?: boolean) => { + if (this.dep) { + if (computedTrigger) { + compareTarget = this._value + hasCompareTarget = true + } else if (!scheduled) { + const valueToCompare = hasCompareTarget + ? compareTarget + : this._value + scheduled = true + hasCompareTarget = false + scheduler(() => { + if (this.effect.active && this._get() !== valueToCompare) { + triggerRefValue( + this, + TriggerReason.ValueUpdatedByGetter, + undefined + ) + } + scheduled = false + }) + } + // chained upstream computeds are notified synchronously to ensure + // value invalidation in case of sync access; normal effects are + // deferred to be triggered in scheduler. + for (const e of this.dep) { + if (e.computed instanceof DeferredComputedRefImpl) { + e.scheduler( + TriggerReason.ValueUpdatedByGetter, + true /* computedTrigger */ + ) } - scheduled = false - }) - } - // chained upstream computeds are notified synchronously to ensure - // value invalidation in case of sync access; normal effects are - // deferred to be triggered in scheduler. - for (const e of this.dep) { - if (e.computed instanceof DeferredComputedRefImpl) { - e.scheduler(true /* computedTrigger */) } } + this._dirty = true } - this._dirty = true - }) + ) this.effect.computed = this as any } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index f20e3d3fbb1..b66fe35b0fd 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -30,7 +30,13 @@ export let trackOpBit = 1 */ const maxMarkerBits = 30 -export type EffectScheduler = (...args: any[]) => any +export enum TriggerReason { + ValueUpdatedBySetter = 1, + ValueUpdatedByGetter = 2, + ComputedDepsUpdated = 3 +} + +export type EffectScheduler = (reason: TriggerReason, ...args: any[]) => any export type DebuggerEvent = { effect: ReactiveEffect @@ -216,8 +222,8 @@ export function effect( let scheduled = false - const _effect = new ReactiveEffect(fn, () => { - if (!scheduled) { + const _effect = new ReactiveEffect(fn, reason => { + if (reason === TriggerReason.ValueUpdatedBySetter || !scheduled) { scheduled = true queueEffectCbs.push(() => { if (_effect.dirty) { @@ -411,9 +417,14 @@ export function trigger( if (deps.length === 1) { if (deps[0]) { if (__DEV__) { - triggerEffects(deps[0], undefined, eventInfo) + triggerEffects( + deps[0], + TriggerReason.ValueUpdatedBySetter, + undefined, + eventInfo + ) } else { - triggerEffects(deps[0], undefined) + triggerEffects(deps[0], TriggerReason.ValueUpdatedBySetter, undefined) } } } else { @@ -424,15 +435,25 @@ export function trigger( } } if (__DEV__) { - triggerEffects(createDep(effects), undefined, eventInfo) + triggerEffects( + createDep(effects), + TriggerReason.ValueUpdatedBySetter, + undefined, + eventInfo + ) } else { - triggerEffects(createDep(effects), undefined) + triggerEffects( + createDep(effects), + TriggerReason.ValueUpdatedBySetter, + undefined + ) } } } export function triggerEffects( dep: Dep | ReactiveEffect[], + triggerMode: TriggerReason, deferredComputed: ComputedRefImpl | undefined, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { @@ -440,12 +461,22 @@ export function triggerEffects( const effects = isArray(dep) ? dep : [...dep] for (const effect of effects) { if (effect.computed) { - triggerEffect(effect, deferredComputed, debuggerEventExtraInfo) + triggerEffect( + effect, + triggerMode, + deferredComputed, + debuggerEventExtraInfo + ) } } for (const effect of effects) { if (!effect.computed) { - triggerEffect(effect, deferredComputed, debuggerEventExtraInfo) + triggerEffect( + effect, + triggerMode, + deferredComputed, + debuggerEventExtraInfo + ) } } } @@ -454,6 +485,7 @@ const queueEffectCbs: (() => void)[] = [] function triggerEffect( effect: ReactiveEffect, + triggerMode: TriggerReason, deferredComputed: ComputedRefImpl | undefined, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { @@ -469,7 +501,7 @@ function triggerEffect( effect._deferredComputeds.length = 0 } } - effect.scheduler() + effect.scheduler(triggerMode) } scheduleEffectCallbacks() } diff --git a/packages/reactivity/src/index.ts b/packages/reactivity/src/index.ts index ee4da5b1935..f20605886bd 100644 --- a/packages/reactivity/src/index.ts +++ b/packages/reactivity/src/index.ts @@ -56,6 +56,7 @@ export { resetTracking, ITERATE_KEY, ReactiveEffect, + TriggerReason, type ReactiveEffectRunner, type ReactiveEffectOptions, type EffectScheduler, diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 91826f35600..62800766ef1 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -1,4 +1,5 @@ import { + TriggerReason, activeEffect, getDepFromReactive, shouldTrack, @@ -55,21 +56,22 @@ export function trackRefValue(ref: RefBase) { export function triggerRefValue( ref: RefBase, - deferredComputed?: ComputedRefImpl, + triggerMode: TriggerReason, + deferredComputed: ComputedRefImpl | undefined, newVal?: any ) { ref = toRaw(ref) const dep = ref.dep if (dep) { if (__DEV__) { - triggerEffects(dep, deferredComputed, { + triggerEffects(dep, triggerMode, deferredComputed, { target: ref, type: TriggerOpTypes.SET, key: 'value', newValue: newVal }) } else { - triggerEffects(dep, deferredComputed) + triggerEffects(dep, triggerMode, deferredComputed) } } } @@ -163,7 +165,12 @@ class RefImpl { if (hasChanged(newVal, this._rawValue)) { this._rawValue = newVal this._value = useDirectValue ? newVal : toReactive(newVal) - triggerRefValue(this, undefined, newVal) + triggerRefValue( + this, + TriggerReason.ValueUpdatedBySetter, + undefined, + newVal + ) } } } @@ -194,7 +201,12 @@ class RefImpl { * @see {@link https://vuejs.org/api/reactivity-advanced.html#triggerref} */ export function triggerRef(ref: Ref) { - triggerRefValue(ref, undefined, __DEV__ ? ref.value : void 0) + triggerRefValue( + ref, + TriggerReason.ValueUpdatedBySetter, + undefined, + __DEV__ ? ref.value : void 0 + ) } export type MaybeRef = T | Ref @@ -290,7 +302,7 @@ class CustomRefImpl { constructor(factory: CustomRefFactory) { const { get, set } = factory( () => trackRefValue(this), - () => triggerRefValue(this) + () => triggerRefValue(this, TriggerReason.ValueUpdatedBySetter, undefined) ) this._get = get this._set = set diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 5f4538a0ace..478b59db909 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -45,7 +45,12 @@ import { flushPreFlushCbs, SchedulerJob } from './scheduler' -import { pauseTracking, resetTracking, ReactiveEffect } from '@vue/reactivity' +import { + pauseTracking, + resetTracking, + ReactiveEffect, + TriggerReason +} from '@vue/reactivity' import { updateProps } from './componentProps' import { updateSlots } from './componentSlots' import { pushWarningContext, popWarningContext, warn } from './warning' @@ -1549,8 +1554,8 @@ function baseCreateRenderer( // create reactive effect for rendering const effect = (instance.effect = new ReactiveEffect( componentUpdateFn, - () => { - if (!scheduled) { + reason => { + if (reason === TriggerReason.ValueUpdatedBySetter || !scheduled) { scheduled = true queueJob(update) } From 13c9a2c638e9bb400959bb1d04df0bc592134fde Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 29 Aug 2023 12:39:34 +0800 Subject: [PATCH 067/110] fix: avoid computed side effects causing re-rendering --- packages/reactivity/src/effect.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index b66fe35b0fd..16fb39c9a4e 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -82,6 +82,7 @@ export class ReactiveEffect { onTrigger?: (event: DebuggerEvent) => void public _dirty = true + public _drityTriggerReason = TriggerReason.ValueUpdatedBySetter public _deferredComputeds: ComputedRefImpl[] = [] private _depIndexes = new Map() @@ -118,7 +119,22 @@ export class ReactiveEffect { } run() { + this.resetDirty() + const r = this._run() + if (this._drityTriggerReason !== TriggerReason.ValueUpdatedBySetter) { + this._dirty = false + this._drityTriggerReason = TriggerReason.ValueUpdatedBySetter + } + return r + } + + resetDirty() { this._dirty = false + this._drityTriggerReason = TriggerReason.ValueUpdatedBySetter + this._deferredComputeds.length = 0 + } + + _run() { if (!this.active) { return this.fn() } @@ -500,6 +516,7 @@ function triggerEffect( effect._dirty = true effect._deferredComputeds.length = 0 } + effect._drityTriggerReason = triggerMode } effect.scheduler(triggerMode) } From 53db76c2a7b8935c53375f1dce091382a82a1d4c Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 29 Aug 2023 12:54:53 +0800 Subject: [PATCH 068/110] chore: _mode -> _ --- packages/reactivity/src/deferredComputed.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reactivity/src/deferredComputed.ts b/packages/reactivity/src/deferredComputed.ts index 02b0073d3c8..8da127ce7a5 100644 --- a/packages/reactivity/src/deferredComputed.ts +++ b/packages/reactivity/src/deferredComputed.ts @@ -40,7 +40,7 @@ class DeferredComputedRefImpl { let scheduled = false this.effect = new ReactiveEffect( getter, - (_mode, computedTrigger?: boolean) => { + (_, computedTrigger?: boolean) => { if (this.dep) { if (computedTrigger) { compareTarget = this._value From e76b42c8c2797a783ff95d74e8b61bdadbc4b1bc Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 29 Aug 2023 12:56:13 +0800 Subject: [PATCH 069/110] chore: format --- packages/reactivity/src/deferredComputed.ts | 61 ++++++++++----------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/packages/reactivity/src/deferredComputed.ts b/packages/reactivity/src/deferredComputed.ts index 8da127ce7a5..8fe7f7dde46 100644 --- a/packages/reactivity/src/deferredComputed.ts +++ b/packages/reactivity/src/deferredComputed.ts @@ -38,45 +38,40 @@ class DeferredComputedRefImpl { let compareTarget: any let hasCompareTarget = false let scheduled = false - this.effect = new ReactiveEffect( - getter, - (_, computedTrigger?: boolean) => { - if (this.dep) { - if (computedTrigger) { - compareTarget = this._value - hasCompareTarget = true - } else if (!scheduled) { - const valueToCompare = hasCompareTarget - ? compareTarget - : this._value - scheduled = true - hasCompareTarget = false - scheduler(() => { - if (this.effect.active && this._get() !== valueToCompare) { - triggerRefValue( - this, - TriggerReason.ValueUpdatedByGetter, - undefined - ) - } - scheduled = false - }) - } - // chained upstream computeds are notified synchronously to ensure - // value invalidation in case of sync access; normal effects are - // deferred to be triggered in scheduler. - for (const e of this.dep) { - if (e.computed instanceof DeferredComputedRefImpl) { - e.scheduler( + this.effect = new ReactiveEffect(getter, (_, computedTrigger?: boolean) => { + if (this.dep) { + if (computedTrigger) { + compareTarget = this._value + hasCompareTarget = true + } else if (!scheduled) { + const valueToCompare = hasCompareTarget ? compareTarget : this._value + scheduled = true + hasCompareTarget = false + scheduler(() => { + if (this.effect.active && this._get() !== valueToCompare) { + triggerRefValue( + this, TriggerReason.ValueUpdatedByGetter, - true /* computedTrigger */ + undefined ) } + scheduled = false + }) + } + // chained upstream computeds are notified synchronously to ensure + // value invalidation in case of sync access; normal effects are + // deferred to be triggered in scheduler. + for (const e of this.dep) { + if (e.computed instanceof DeferredComputedRefImpl) { + e.scheduler( + TriggerReason.ValueUpdatedByGetter, + true /* computedTrigger */ + ) } } - this._dirty = true } - ) + this._dirty = true + }) this.effect.computed = this as any } From 35f0ddd979df69ac6f7060b5adac4463d3668ad9 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 29 Aug 2023 13:04:21 +0800 Subject: [PATCH 070/110] chore: remove computed.init --- packages/reactivity/src/computed.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 6de0cfa3d97..6fc752f3ccc 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -34,7 +34,6 @@ export class ComputedRefImpl { public _cacheable: boolean - private init = false private scheduled = false constructor( @@ -46,11 +45,7 @@ export class ComputedRefImpl { this.effect = new ReactiveEffect(getter, () => { if (!this.scheduled) { this.scheduled = true - if (!this.init) { - triggerRefValue(this, undefined) - } else { - triggerRefValue(this, this) - } + triggerRefValue(this, this) } }) this.effect.computed = this @@ -64,9 +59,7 @@ export class ComputedRefImpl { trackRefValue(self) if (!self._cacheable || self.effect.dirty) { const newValue = self.effect.run()! - if (!self.init) { - self.init = true - } else if (hasChanged(self._value, newValue)) { + if (hasChanged(self._value, newValue)) { triggerRefValue(self, undefined) } self._value = newValue From e605560cb2dc3f5e0fc35c8238a3fad0b00b4bcb Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 29 Aug 2023 13:14:31 +0800 Subject: [PATCH 071/110] refactor: TriggerReason -> TriggerType --- packages/reactivity/src/computed.ts | 6 +-- packages/reactivity/src/deferredComputed.ts | 6 +-- packages/reactivity/src/effect.ts | 42 ++++++++------------- packages/reactivity/src/index.ts | 2 +- packages/reactivity/src/ref.ts | 10 ++--- packages/runtime-core/src/renderer.ts | 6 +-- 6 files changed, 31 insertions(+), 41 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index c6817f5fec9..5371d967145 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -1,4 +1,4 @@ -import { DebuggerOptions, ReactiveEffect, TriggerReason } from './effect' +import { DebuggerOptions, ReactiveEffect, TriggerType } from './effect' import { Ref, trackRefValue, triggerRefValue } from './ref' import { hasChanged, isFunction, NOOP } from '@vue/shared' import { ReactiveFlags, toRaw } from './reactive' @@ -45,7 +45,7 @@ export class ComputedRefImpl { this.effect = new ReactiveEffect(getter, () => { if (!this.scheduled) { this.scheduled = true - triggerRefValue(this, TriggerReason.ComputedDepsUpdated, this) + triggerRefValue(this, TriggerType.SideEffect, this) } }) this.effect.computed = this @@ -60,7 +60,7 @@ export class ComputedRefImpl { if (!self._cacheable || self.effect.dirty) { const newValue = self.effect.run()! if (hasChanged(self._value, newValue)) { - triggerRefValue(self, TriggerReason.ValueUpdatedByGetter, undefined) + triggerRefValue(self, TriggerType.SideEffect, undefined) } self._value = newValue self.scheduled = false diff --git a/packages/reactivity/src/deferredComputed.ts b/packages/reactivity/src/deferredComputed.ts index 8fe7f7dde46..34c91994e82 100644 --- a/packages/reactivity/src/deferredComputed.ts +++ b/packages/reactivity/src/deferredComputed.ts @@ -1,5 +1,5 @@ import { Dep } from './dep' -import { ReactiveEffect, TriggerReason } from './effect' +import { ReactiveEffect, TriggerType } from './effect' import { ComputedGetter, ComputedRef } from './computed' import { ReactiveFlags, toRaw } from './reactive' import { trackRefValue, triggerRefValue } from './ref' @@ -51,7 +51,7 @@ class DeferredComputedRefImpl { if (this.effect.active && this._get() !== valueToCompare) { triggerRefValue( this, - TriggerReason.ValueUpdatedByGetter, + TriggerType.SideEffect, undefined ) } @@ -64,7 +64,7 @@ class DeferredComputedRefImpl { for (const e of this.dep) { if (e.computed instanceof DeferredComputedRefImpl) { e.scheduler( - TriggerReason.ValueUpdatedByGetter, + TriggerType.SideEffect, true /* computedTrigger */ ) } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 16fb39c9a4e..a1162ca8417 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -30,13 +30,12 @@ export let trackOpBit = 1 */ const maxMarkerBits = 30 -export enum TriggerReason { - ValueUpdatedBySetter = 1, - ValueUpdatedByGetter = 2, - ComputedDepsUpdated = 3 +export enum TriggerType { + Operate = 1, + SideEffect = 2 } -export type EffectScheduler = (reason: TriggerReason, ...args: any[]) => any +export type EffectScheduler = (triggerType: TriggerType, ...args: any[]) => any export type DebuggerEvent = { effect: ReactiveEffect @@ -82,7 +81,7 @@ export class ReactiveEffect { onTrigger?: (event: DebuggerEvent) => void public _dirty = true - public _drityTriggerReason = TriggerReason.ValueUpdatedBySetter + public _drityTriggerReason = TriggerType.Operate public _deferredComputeds: ComputedRefImpl[] = [] private _depIndexes = new Map() @@ -121,16 +120,16 @@ export class ReactiveEffect { run() { this.resetDirty() const r = this._run() - if (this._drityTriggerReason !== TriggerReason.ValueUpdatedBySetter) { + if (this._drityTriggerReason !== TriggerType.Operate) { this._dirty = false - this._drityTriggerReason = TriggerReason.ValueUpdatedBySetter + this._drityTriggerReason = TriggerType.Operate } return r } resetDirty() { this._dirty = false - this._drityTriggerReason = TriggerReason.ValueUpdatedBySetter + this._drityTriggerReason = TriggerType.Operate this._deferredComputeds.length = 0 } @@ -238,8 +237,8 @@ export function effect( let scheduled = false - const _effect = new ReactiveEffect(fn, reason => { - if (reason === TriggerReason.ValueUpdatedBySetter || !scheduled) { + const _effect = new ReactiveEffect(fn, triggerType => { + if (triggerType === TriggerType.Operate || !scheduled) { scheduled = true queueEffectCbs.push(() => { if (_effect.dirty) { @@ -433,14 +432,9 @@ export function trigger( if (deps.length === 1) { if (deps[0]) { if (__DEV__) { - triggerEffects( - deps[0], - TriggerReason.ValueUpdatedBySetter, - undefined, - eventInfo - ) + triggerEffects(deps[0], TriggerType.Operate, undefined, eventInfo) } else { - triggerEffects(deps[0], TriggerReason.ValueUpdatedBySetter, undefined) + triggerEffects(deps[0], TriggerType.Operate, undefined) } } } else { @@ -453,23 +447,19 @@ export function trigger( if (__DEV__) { triggerEffects( createDep(effects), - TriggerReason.ValueUpdatedBySetter, + TriggerType.Operate, undefined, eventInfo ) } else { - triggerEffects( - createDep(effects), - TriggerReason.ValueUpdatedBySetter, - undefined - ) + triggerEffects(createDep(effects), TriggerType.Operate, undefined) } } } export function triggerEffects( dep: Dep | ReactiveEffect[], - triggerMode: TriggerReason, + triggerMode: TriggerType, deferredComputed: ComputedRefImpl | undefined, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { @@ -501,7 +491,7 @@ const queueEffectCbs: (() => void)[] = [] function triggerEffect( effect: ReactiveEffect, - triggerMode: TriggerReason, + triggerMode: TriggerType, deferredComputed: ComputedRefImpl | undefined, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { diff --git a/packages/reactivity/src/index.ts b/packages/reactivity/src/index.ts index f20605886bd..05c3ac6ab56 100644 --- a/packages/reactivity/src/index.ts +++ b/packages/reactivity/src/index.ts @@ -56,7 +56,7 @@ export { resetTracking, ITERATE_KEY, ReactiveEffect, - TriggerReason, + TriggerType, type ReactiveEffectRunner, type ReactiveEffectOptions, type EffectScheduler, diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 62800766ef1..fbf0dbd3e87 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -1,5 +1,5 @@ import { - TriggerReason, + TriggerType, activeEffect, getDepFromReactive, shouldTrack, @@ -56,7 +56,7 @@ export function trackRefValue(ref: RefBase) { export function triggerRefValue( ref: RefBase, - triggerMode: TriggerReason, + triggerMode: TriggerType, deferredComputed: ComputedRefImpl | undefined, newVal?: any ) { @@ -167,7 +167,7 @@ class RefImpl { this._value = useDirectValue ? newVal : toReactive(newVal) triggerRefValue( this, - TriggerReason.ValueUpdatedBySetter, + TriggerType.Operate, undefined, newVal ) @@ -203,7 +203,7 @@ class RefImpl { export function triggerRef(ref: Ref) { triggerRefValue( ref, - TriggerReason.ValueUpdatedBySetter, + TriggerType.Operate, undefined, __DEV__ ? ref.value : void 0 ) @@ -302,7 +302,7 @@ class CustomRefImpl { constructor(factory: CustomRefFactory) { const { get, set } = factory( () => trackRefValue(this), - () => triggerRefValue(this, TriggerReason.ValueUpdatedBySetter, undefined) + () => triggerRefValue(this, TriggerType.Operate, undefined) ) this._get = get this._set = set diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 478b59db909..144f0b5205b 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -49,7 +49,7 @@ import { pauseTracking, resetTracking, ReactiveEffect, - TriggerReason + TriggerType } from '@vue/reactivity' import { updateProps } from './componentProps' import { updateSlots } from './componentSlots' @@ -1554,8 +1554,8 @@ function baseCreateRenderer( // create reactive effect for rendering const effect = (instance.effect = new ReactiveEffect( componentUpdateFn, - reason => { - if (reason === TriggerReason.ValueUpdatedBySetter || !scheduled) { + triggerType => { + if (triggerType === TriggerType.Operate || !scheduled) { scheduled = true queueJob(update) } From fe8d996221784f38e3ae5cd5106ee4fe8f46d763 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 29 Aug 2023 13:17:22 +0800 Subject: [PATCH 072/110] chore: use resetDirty --- packages/reactivity/src/effect.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index a1162ca8417..69d00383388 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -121,8 +121,7 @@ export class ReactiveEffect { this.resetDirty() const r = this._run() if (this._drityTriggerReason !== TriggerType.Operate) { - this._dirty = false - this._drityTriggerReason = TriggerType.Operate + this.resetDirty() } return r } From 9ab4eb1e26688d18b1617e03068b3b4e06b47628 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 29 Aug 2023 13:17:56 +0800 Subject: [PATCH 073/110] chore: format --- packages/reactivity/src/deferredComputed.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/reactivity/src/deferredComputed.ts b/packages/reactivity/src/deferredComputed.ts index 34c91994e82..603c097e94a 100644 --- a/packages/reactivity/src/deferredComputed.ts +++ b/packages/reactivity/src/deferredComputed.ts @@ -49,11 +49,7 @@ class DeferredComputedRefImpl { hasCompareTarget = false scheduler(() => { if (this.effect.active && this._get() !== valueToCompare) { - triggerRefValue( - this, - TriggerType.SideEffect, - undefined - ) + triggerRefValue(this, TriggerType.SideEffect, undefined) } scheduled = false }) @@ -63,10 +59,7 @@ class DeferredComputedRefImpl { // deferred to be triggered in scheduler. for (const e of this.dep) { if (e.computed instanceof DeferredComputedRefImpl) { - e.scheduler( - TriggerType.SideEffect, - true /* computedTrigger */ - ) + e.scheduler(TriggerType.SideEffect, true /* computedTrigger */) } } } From 942be5db14e18e3c85e25f3fa42e115756480ca9 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 29 Aug 2023 13:19:46 +0800 Subject: [PATCH 074/110] chore: format --- packages/reactivity/src/ref.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index fbf0dbd3e87..eb128dd7540 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -165,12 +165,7 @@ class RefImpl { if (hasChanged(newVal, this._rawValue)) { this._rawValue = newVal this._value = useDirectValue ? newVal : toReactive(newVal) - triggerRefValue( - this, - TriggerType.Operate, - undefined, - newVal - ) + triggerRefValue(this, TriggerType.Operate, undefined, newVal) } } } From b1bd1de06071a34c966f76852381f76f8164c426 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 29 Aug 2023 13:40:48 +0800 Subject: [PATCH 075/110] fix: don't reset hard reset dirty for computed --- packages/reactivity/src/effect.ts | 21 ++++++++------------- packages/runtime-core/src/renderer.ts | 3 +++ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 69d00383388..e0c47fa5aa5 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -81,7 +81,7 @@ export class ReactiveEffect { onTrigger?: (event: DebuggerEvent) => void public _dirty = true - public _drityTriggerReason = TriggerType.Operate + public _drityTriggerType = TriggerType.Operate public _deferredComputeds: ComputedRefImpl[] = [] private _depIndexes = new Map() @@ -117,22 +117,14 @@ export class ReactiveEffect { return this._dirty } - run() { - this.resetDirty() - const r = this._run() - if (this._drityTriggerReason !== TriggerType.Operate) { - this.resetDirty() - } - return r - } - resetDirty() { this._dirty = false - this._drityTriggerReason = TriggerType.Operate + this._drityTriggerType = TriggerType.Operate this._deferredComputeds.length = 0 } - _run() { + run() { + this.resetDirty() if (!this.active) { return this.fn() } @@ -243,6 +235,9 @@ export function effect( if (_effect.dirty) { _effect.run() } + if (_effect._drityTriggerType !== TriggerType.Operate) { + _effect.resetDirty() + } scheduled = false }) } @@ -505,7 +500,7 @@ function triggerEffect( effect._dirty = true effect._deferredComputeds.length = 0 } - effect._drityTriggerReason = triggerMode + effect._drityTriggerType = triggerMode } effect.scheduler(triggerMode) } diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 144f0b5205b..d2d4eeeef8a 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -1567,6 +1567,9 @@ function baseCreateRenderer( if (effect.dirty) { effect.run() } + if (effect._drityTriggerType !== TriggerType.Operate) { + effect.resetDirty() + } scheduled = false }) update.id = instance.uid From ee05427fbe4548c16a635a2ab49906e4ca1aff15 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 29 Aug 2023 22:37:31 +0800 Subject: [PATCH 076/110] fix: computed deps update should re-trigger effects --- packages/reactivity/src/computed.ts | 4 +- packages/reactivity/src/deferredComputed.ts | 4 +- packages/reactivity/src/effect.ts | 46 ++++++++++++--------- packages/reactivity/src/ref.ts | 12 +++--- packages/runtime-core/src/renderer.ts | 9 ++-- 5 files changed, 41 insertions(+), 34 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 5371d967145..f0d1b469ad2 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -45,7 +45,7 @@ export class ComputedRefImpl { this.effect = new ReactiveEffect(getter, () => { if (!this.scheduled) { this.scheduled = true - triggerRefValue(this, TriggerType.SideEffect, this) + triggerRefValue(this, TriggerType.ComputedDepsUpdated, this) } }) this.effect.computed = this @@ -60,7 +60,7 @@ export class ComputedRefImpl { if (!self._cacheable || self.effect.dirty) { const newValue = self.effect.run()! if (hasChanged(self._value, newValue)) { - triggerRefValue(self, TriggerType.SideEffect, undefined) + triggerRefValue(self, TriggerType.ComputedValueUpdated, this) } self._value = newValue self.scheduled = false diff --git a/packages/reactivity/src/deferredComputed.ts b/packages/reactivity/src/deferredComputed.ts index 603c097e94a..f4b18372dcf 100644 --- a/packages/reactivity/src/deferredComputed.ts +++ b/packages/reactivity/src/deferredComputed.ts @@ -49,7 +49,7 @@ class DeferredComputedRefImpl { hasCompareTarget = false scheduler(() => { if (this.effect.active && this._get() !== valueToCompare) { - triggerRefValue(this, TriggerType.SideEffect, undefined) + triggerRefValue(this, TriggerType.ForceDirty, undefined) } scheduled = false }) @@ -59,7 +59,7 @@ class DeferredComputedRefImpl { // deferred to be triggered in scheduler. for (const e of this.dep) { if (e.computed instanceof DeferredComputedRefImpl) { - e.scheduler(TriggerType.SideEffect, true /* computedTrigger */) + e.scheduler(TriggerType.ForceDirty, true /* computedTrigger */) } } } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index e0c47fa5aa5..d41e32ed440 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -31,8 +31,9 @@ export let trackOpBit = 1 const maxMarkerBits = 30 export enum TriggerType { - Operate = 1, - SideEffect = 2 + ForceDirty = 1, + ComputedDepsUpdated = 2, + ComputedValueUpdated = 3 } export type EffectScheduler = (triggerType: TriggerType, ...args: any[]) => any @@ -81,7 +82,6 @@ export class ReactiveEffect { onTrigger?: (event: DebuggerEvent) => void public _dirty = true - public _drityTriggerType = TriggerType.Operate public _deferredComputeds: ComputedRefImpl[] = [] private _depIndexes = new Map() @@ -119,7 +119,6 @@ export class ReactiveEffect { resetDirty() { this._dirty = false - this._drityTriggerType = TriggerType.Operate this._deferredComputeds.length = 0 } @@ -229,15 +228,16 @@ export function effect( let scheduled = false const _effect = new ReactiveEffect(fn, triggerType => { - if (triggerType === TriggerType.Operate || !scheduled) { + if ( + triggerType === TriggerType.ForceDirty || + triggerType === TriggerType.ComputedDepsUpdated || + !scheduled + ) { scheduled = true queueEffectCbs.push(() => { if (_effect.dirty) { _effect.run() } - if (_effect._drityTriggerType !== TriggerType.Operate) { - _effect.resetDirty() - } scheduled = false }) } @@ -426,9 +426,9 @@ export function trigger( if (deps.length === 1) { if (deps[0]) { if (__DEV__) { - triggerEffects(deps[0], TriggerType.Operate, undefined, eventInfo) + triggerEffects(deps[0], TriggerType.ForceDirty, undefined, eventInfo) } else { - triggerEffects(deps[0], TriggerType.Operate, undefined) + triggerEffects(deps[0], TriggerType.ForceDirty, undefined) } } } else { @@ -441,19 +441,19 @@ export function trigger( if (__DEV__) { triggerEffects( createDep(effects), - TriggerType.Operate, + TriggerType.ForceDirty, undefined, eventInfo ) } else { - triggerEffects(createDep(effects), TriggerType.Operate, undefined) + triggerEffects(createDep(effects), TriggerType.ForceDirty, undefined) } } } export function triggerEffects( dep: Dep | ReactiveEffect[], - triggerMode: TriggerType, + triggerType: TriggerType, deferredComputed: ComputedRefImpl | undefined, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { @@ -463,7 +463,7 @@ export function triggerEffects( if (effect.computed) { triggerEffect( effect, - triggerMode, + triggerType, deferredComputed, debuggerEventExtraInfo ) @@ -473,7 +473,7 @@ export function triggerEffects( if (!effect.computed) { triggerEffect( effect, - triggerMode, + triggerType, deferredComputed, debuggerEventExtraInfo ) @@ -485,7 +485,7 @@ const queueEffectCbs: (() => void)[] = [] function triggerEffect( effect: ReactiveEffect, - triggerMode: TriggerType, + triggerType: TriggerType, deferredComputed: ComputedRefImpl | undefined, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { @@ -494,15 +494,21 @@ function triggerEffect( effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) } if (!effect._dirty) { - if (deferredComputed) { + if (triggerType === TriggerType.ComputedDepsUpdated && deferredComputed) { effect._deferredComputeds.push(deferredComputed) - } else { + } else if ( + triggerType === TriggerType.ComputedValueUpdated && + deferredComputed && + effect._deferredComputeds.includes(deferredComputed) + ) { + effect._dirty = true + effect._deferredComputeds.length = 0 + } else if (triggerType === TriggerType.ForceDirty) { effect._dirty = true effect._deferredComputeds.length = 0 } - effect._drityTriggerType = triggerMode } - effect.scheduler(triggerMode) + effect.scheduler(triggerType) } scheduleEffectCallbacks() } diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index eb128dd7540..81215da6e36 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -56,7 +56,7 @@ export function trackRefValue(ref: RefBase) { export function triggerRefValue( ref: RefBase, - triggerMode: TriggerType, + triggerType: TriggerType, deferredComputed: ComputedRefImpl | undefined, newVal?: any ) { @@ -64,14 +64,14 @@ export function triggerRefValue( const dep = ref.dep if (dep) { if (__DEV__) { - triggerEffects(dep, triggerMode, deferredComputed, { + triggerEffects(dep, triggerType, deferredComputed, { target: ref, type: TriggerOpTypes.SET, key: 'value', newValue: newVal }) } else { - triggerEffects(dep, triggerMode, deferredComputed) + triggerEffects(dep, triggerType, deferredComputed) } } } @@ -165,7 +165,7 @@ class RefImpl { if (hasChanged(newVal, this._rawValue)) { this._rawValue = newVal this._value = useDirectValue ? newVal : toReactive(newVal) - triggerRefValue(this, TriggerType.Operate, undefined, newVal) + triggerRefValue(this, TriggerType.ForceDirty, undefined, newVal) } } } @@ -198,7 +198,7 @@ class RefImpl { export function triggerRef(ref: Ref) { triggerRefValue( ref, - TriggerType.Operate, + TriggerType.ForceDirty, undefined, __DEV__ ? ref.value : void 0 ) @@ -297,7 +297,7 @@ class CustomRefImpl { constructor(factory: CustomRefFactory) { const { get, set } = factory( () => trackRefValue(this), - () => triggerRefValue(this, TriggerType.Operate, undefined) + () => triggerRefValue(this, TriggerType.ForceDirty, undefined) ) this._get = get this._set = set diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index d2d4eeeef8a..72a78667f78 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -1555,7 +1555,11 @@ function baseCreateRenderer( const effect = (instance.effect = new ReactiveEffect( componentUpdateFn, triggerType => { - if (triggerType === TriggerType.Operate || !scheduled) { + if ( + triggerType === TriggerType.ForceDirty || + triggerType === TriggerType.ComputedDepsUpdated || + !scheduled + ) { scheduled = true queueJob(update) } @@ -1567,9 +1571,6 @@ function baseCreateRenderer( if (effect.dirty) { effect.run() } - if (effect._drityTriggerType !== TriggerType.Operate) { - effect.resetDirty() - } scheduled = false }) update.id = instance.uid From 99af87b961e79ac887236358a32cf83d57c1efb8 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 29 Aug 2023 22:44:14 +0800 Subject: [PATCH 077/110] refactor(effect): add dirty setter --- packages/reactivity/src/computed.ts | 2 +- packages/reactivity/src/effect.ts | 12 +++++------- packages/runtime-core/src/apiAsyncComponent.ts | 2 +- packages/runtime-core/src/componentPublicInstance.ts | 2 +- .../runtime-core/src/components/BaseTransition.ts | 2 +- packages/runtime-core/src/hmr.ts | 4 ++-- packages/runtime-core/src/renderer.ts | 2 +- 7 files changed, 12 insertions(+), 14 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index f0d1b469ad2..ca347e2015d 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -78,7 +78,7 @@ export class ComputedRefImpl { } set _dirty(v) { - this.effect._dirty = v + this.effect.dirty = v } // #endregion } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index d41e32ed440..e9e9fddb846 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -117,13 +117,13 @@ export class ReactiveEffect { return this._dirty } - resetDirty() { - this._dirty = false + public set dirty(v) { + this._dirty = v this._deferredComputeds.length = 0 } run() { - this.resetDirty() + this.dirty = false if (!this.active) { return this.fn() } @@ -501,11 +501,9 @@ function triggerEffect( deferredComputed && effect._deferredComputeds.includes(deferredComputed) ) { - effect._dirty = true - effect._deferredComputeds.length = 0 + effect.dirty = true } else if (triggerType === TriggerType.ForceDirty) { - effect._dirty = true - effect._deferredComputeds.length = 0 + effect.dirty = true } } effect.scheduler(triggerType) diff --git a/packages/runtime-core/src/apiAsyncComponent.ts b/packages/runtime-core/src/apiAsyncComponent.ts index 0dd785a1862..535cb83fb5d 100644 --- a/packages/runtime-core/src/apiAsyncComponent.ts +++ b/packages/runtime-core/src/apiAsyncComponent.ts @@ -187,7 +187,7 @@ export function defineAsyncComponent< if (instance.parent && isKeepAlive(instance.parent.vnode)) { // parent is keep-alive, force update so the loaded component's // name is taken into account - instance.parent.effect._dirty = true + instance.parent.effect.dirty = true queueJob(instance.parent.update) } }) diff --git a/packages/runtime-core/src/componentPublicInstance.ts b/packages/runtime-core/src/componentPublicInstance.ts index 26a2eb21def..3d2b0dd912f 100644 --- a/packages/runtime-core/src/componentPublicInstance.ts +++ b/packages/runtime-core/src/componentPublicInstance.ts @@ -272,7 +272,7 @@ export const publicPropertiesMap: PublicPropertiesMap = $forceUpdate: i => i.f || (i.f = () => { - i.effect._dirty = true + i.effect.dirty = true queueJob(i.update) }), $nextTick: i => i.n || (i.n = nextTick.bind(i.proxy!)), diff --git a/packages/runtime-core/src/components/BaseTransition.ts b/packages/runtime-core/src/components/BaseTransition.ts index 6d03edf0936..ef0632384d6 100644 --- a/packages/runtime-core/src/components/BaseTransition.ts +++ b/packages/runtime-core/src/components/BaseTransition.ts @@ -246,7 +246,7 @@ const BaseTransitionImpl: ComponentOptions = { // #6835 // it also needs to be updated when active is undefined if (instance.update.active !== false) { - instance.effect._dirty = true + instance.effect.dirty = true instance.update() } } diff --git a/packages/runtime-core/src/hmr.ts b/packages/runtime-core/src/hmr.ts index f95b2414eb4..cdf291989bd 100644 --- a/packages/runtime-core/src/hmr.ts +++ b/packages/runtime-core/src/hmr.ts @@ -93,7 +93,7 @@ function rerender(id: string, newRender?: Function) { instance.renderCache = [] // this flag forces child components with slot content to update isHmrUpdating = true - instance.effect._dirty = true + instance.effect.dirty = true instance.update() isHmrUpdating = false }) @@ -138,7 +138,7 @@ function reload(id: string, newComp: HMRComponent) { // 4. Force the parent instance to re-render. This will cause all updated // components to be unmounted and re-mounted. Queue the update so that we // don't end up forcing the same parent to re-render multiple times. - instance.parent.effect._dirty = true + instance.parent.effect.dirty = true queueJob(instance.parent.update) } else if (instance.appContext.reload) { // root instance mounted via createApp() has a reload method diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 72a78667f78..003a8e77fa3 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -1287,7 +1287,7 @@ function baseCreateRenderer( // double updating the same child component in the same flush. invalidateJob(instance.update) // instance.update is the reactive effect. - instance.effect._dirty = true + instance.effect.dirty = true instance.update() } } else { From a181339057bc93d4b4581f01148367080348f4b7 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 30 Aug 2023 02:05:31 +0800 Subject: [PATCH 078/110] fix: computed should allow recursion dirty trigger --- packages/reactivity/src/computed.ts | 10 ++++++++-- packages/reactivity/src/effect.ts | 11 ++++++++++- packages/reactivity/src/ref.ts | 1 + 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index ca347e2015d..52e4db34ae4 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -1,4 +1,9 @@ -import { DebuggerOptions, ReactiveEffect, TriggerType } from './effect' +import { + DebuggerOptions, + DeferredComputedAcceptMode, + ReactiveEffect, + TriggerType +} from './effect' import { Ref, trackRefValue, triggerRefValue } from './ref' import { hasChanged, isFunction, NOOP } from '@vue/shared' import { ReactiveFlags, toRaw } from './reactive' @@ -48,6 +53,7 @@ export class ComputedRefImpl { triggerRefValue(this, TriggerType.ComputedDepsUpdated, this) } }) + this.effect._deferredComputedAcceptMode = DeferredComputedAcceptMode.Always this.effect.computed = this this.effect.active = this._cacheable = !isSSR this[ReactiveFlags.IS_READONLY] = isReadonly @@ -60,7 +66,7 @@ export class ComputedRefImpl { if (!self._cacheable || self.effect.dirty) { const newValue = self.effect.run()! if (hasChanged(self._value, newValue)) { - triggerRefValue(self, TriggerType.ComputedValueUpdated, this) + triggerRefValue(self, TriggerType.ComputedValueUpdated, self) } self._value = newValue self.scheduled = false diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index e9e9fddb846..2f6a46691cd 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -36,6 +36,11 @@ export enum TriggerType { ComputedValueUpdated = 3 } +export enum DeferredComputedAcceptMode { + Always = 1, + OnlyWhenQuerying = 2 +} + export type EffectScheduler = (triggerType: TriggerType, ...args: any[]) => any export type DebuggerEvent = { @@ -83,6 +88,8 @@ export class ReactiveEffect { public _dirty = true public _deferredComputeds: ComputedRefImpl[] = [] + public _deferredComputedAcceptMode = + DeferredComputedAcceptMode.OnlyWhenQuerying private _depIndexes = new Map() constructor( @@ -499,7 +506,9 @@ function triggerEffect( } else if ( triggerType === TriggerType.ComputedValueUpdated && deferredComputed && - effect._deferredComputeds.includes(deferredComputed) + (effect._deferredComputedAcceptMode === + DeferredComputedAcceptMode.Always || + effect._deferredComputeds.includes(deferredComputed)) ) { effect.dirty = true } else if (triggerType === TriggerType.ForceDirty) { diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 81215da6e36..c5ec136dfd4 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -61,6 +61,7 @@ export function triggerRefValue( newVal?: any ) { ref = toRaw(ref) + deferredComputed = toRaw(deferredComputed) const dep = ref.dep if (dep) { if (__DEV__) { From 8084d74cf059c452556fb904ca67a14733db5162 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 30 Aug 2023 19:06:52 +0800 Subject: [PATCH 079/110] fix: avoid tree shaking `deferredComputed.value` --- packages/reactivity/src/effect.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index f20e3d3fbb1..c6d01c9e200 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -50,6 +50,10 @@ export let activeEffect: ReactiveEffect | undefined export const ITERATE_KEY = Symbol(__DEV__ ? 'iterate' : '') export const MAP_KEY_ITERATE_KEY = Symbol(__DEV__ ? 'Map key iterate' : '') +function triggerComputedGetter(computed: ComputedRefImpl) { + return computed.value +} + export class ReactiveEffect { active = true deps: Dep[] = [] @@ -100,7 +104,7 @@ export class ReactiveEffect { } pauseTracking() for (const deferredComputed of this._deferredComputeds) { - deferredComputed.value + triggerComputedGetter(deferredComputed.value) // wrap with function to avoid tree shaking if (this._dirty) { break } From 194f8a8cdec2fba307a120608d200e9125a83725 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 30 Aug 2023 19:07:35 +0800 Subject: [PATCH 080/110] perf: reuse `_depIndexes` --- packages/reactivity/src/effect.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index c6d01c9e200..5fdaea9a37b 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -50,6 +50,8 @@ export let activeEffect: ReactiveEffect | undefined export const ITERATE_KEY = Symbol(__DEV__ ? 'iterate' : '') export const MAP_KEY_ITERATE_KEY = Symbol(__DEV__ ? 'Map key iterate' : '') +const _depIndexes = new Map() + function triggerComputedGetter(computed: ComputedRefImpl) { return computed.value } @@ -79,9 +81,8 @@ export class ReactiveEffect { // dev only onTrigger?: (event: DebuggerEvent) => void - public _dirty = true - public _deferredComputeds: ComputedRefImpl[] = [] - private _depIndexes = new Map() + _dirty = true + _deferredComputeds: ComputedRefImpl[] = [] constructor( public fn: () => T, @@ -95,12 +96,12 @@ export class ReactiveEffect { if (!this._dirty && this._deferredComputeds.length) { if (this._deferredComputeds.length >= 2) { for (const { dep } of this._deferredComputeds) { - this._depIndexes.set(dep, this.deps.indexOf(dep!)) + _depIndexes.set(dep, this.deps.indexOf(dep!)) } this._deferredComputeds = this._deferredComputeds.sort( - (a, b) => this._depIndexes.get(a.dep)! - this._depIndexes.get(b.dep)! + (a, b) => _depIndexes.get(a.dep)! - _depIndexes.get(b.dep)! ) - this._depIndexes.clear() + _depIndexes.clear() } pauseTracking() for (const deferredComputed of this._deferredComputeds) { From f620aa96aa72d9477aa87b53ad93f3147a1ea9cf Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 30 Aug 2023 19:11:21 +0800 Subject: [PATCH 081/110] chore: fix tests --- packages/reactivity/src/effect.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 5fdaea9a37b..a889735490b 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -105,7 +105,7 @@ export class ReactiveEffect { } pauseTracking() for (const deferredComputed of this._deferredComputeds) { - triggerComputedGetter(deferredComputed.value) // wrap with function to avoid tree shaking + triggerComputedGetter(deferredComputed) // wrap with function to avoid tree shaking if (this._dirty) { break } From 8f8f3847437a996720e83d11f358af62625af4f7 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 30 Aug 2023 19:11:53 +0800 Subject: [PATCH 082/110] Squashed commit of the following: commit f620aa96aa72d9477aa87b53ad93f3147a1ea9cf Author: Johnson Chu Date: Wed Aug 30 19:11:21 2023 +0800 chore: fix tests --- packages/reactivity/src/effect.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 7e716d110cd..2ae1bf8ec15 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -117,7 +117,7 @@ export class ReactiveEffect { } pauseTracking() for (const deferredComputed of this._deferredComputeds) { - triggerComputedGetter(deferredComputed.value) // wrap with function to avoid tree shaking + triggerComputedGetter(deferredComputed) // wrap with function to avoid tree shaking if (this._dirty) { break } From c02f650ccefbe2eed28712118b4229adcc38f1d4 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Fri, 1 Sep 2023 08:28:27 +0800 Subject: [PATCH 083/110] perf: faster computed effect spread --- packages/reactivity/src/computed.ts | 15 ++++---- packages/reactivity/src/dep.ts | 5 ++- packages/reactivity/src/effect.ts | 55 +++++++++++++---------------- packages/reactivity/src/ref.ts | 23 ++++++------ 4 files changed, 48 insertions(+), 50 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 6fc752f3ccc..12d43137050 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -33,8 +33,7 @@ export class ComputedRefImpl { public readonly [ReactiveFlags.IS_READONLY]: boolean = false public _cacheable: boolean - - private scheduled = false + public _scheduled = false constructor( getter: ComputedGetter, @@ -43,9 +42,9 @@ export class ComputedRefImpl { isSSR: boolean ) { this.effect = new ReactiveEffect(getter, () => { - if (!this.scheduled) { - this.scheduled = true - triggerRefValue(this, this) + if (!this._scheduled) { + this._scheduled = true + triggerRefValue(this, true) } }) this.effect.computed = this @@ -56,14 +55,14 @@ export class ComputedRefImpl { get value() { // the computed ref may get wrapped by other proxies e.g. readonly() #3376 const self = toRaw(this) - trackRefValue(self) + trackRefValue(self, self) if (!self._cacheable || self.effect.dirty) { const newValue = self.effect.run()! if (hasChanged(self._value, newValue)) { - triggerRefValue(self, undefined) + triggerRefValue(self, false) } self._value = newValue - self.scheduled = false + self._scheduled = false } return self._value } diff --git a/packages/reactivity/src/dep.ts b/packages/reactivity/src/dep.ts index 8677f575756..e470bc07c5c 100644 --- a/packages/reactivity/src/dep.ts +++ b/packages/reactivity/src/dep.ts @@ -1,3 +1,4 @@ +import type { ComputedRefImpl } from './computed' import { ReactiveEffect, trackOpBit } from './effect' export type Dep = Set & TrackedMarkers @@ -16,12 +17,14 @@ type TrackedMarkers = { * newTracked */ n: number + computed?: ComputedRefImpl } -export const createDep = (effects?: ReactiveEffect[]): Dep => { +export const createDep = (effects?: ReactiveEffect[], computed?: ComputedRefImpl): Dep => { const dep = new Set(effects) as Dep dep.w = 0 dep.n = 0 + dep.computed = computed return dep } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index a889735490b..eb28a0c1814 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -9,7 +9,8 @@ import { newTracked, wasTracked } from './dep' -import { ComputedRefImpl } from './computed' +import type { ComputedRefImpl } from './computed' +import type { RefBase } from './ref' // The main WeakMap that stores {target -> key -> dep} connections. // Conceptually, it's easier to think of a dependency as a Dep class @@ -50,9 +51,7 @@ export let activeEffect: ReactiveEffect | undefined export const ITERATE_KEY = Symbol(__DEV__ ? 'iterate' : '') export const MAP_KEY_ITERATE_KEY = Symbol(__DEV__ ? 'Map key iterate' : '') -const _depIndexes = new Map() - -function triggerComputedGetter(computed: ComputedRefImpl) { +function triggerComputedGetter(computed: RefBase) { return computed.value } @@ -82,7 +81,7 @@ export class ReactiveEffect { onTrigger?: (event: DebuggerEvent) => void _dirty = true - _deferredComputeds: ComputedRefImpl[] = [] + _depsMaybeDirty = false constructor( public fn: () => T, @@ -93,31 +92,25 @@ export class ReactiveEffect { } public get dirty() { - if (!this._dirty && this._deferredComputeds.length) { - if (this._deferredComputeds.length >= 2) { - for (const { dep } of this._deferredComputeds) { - _depIndexes.set(dep, this.deps.indexOf(dep!)) - } - this._deferredComputeds = this._deferredComputeds.sort( - (a, b) => _depIndexes.get(a.dep)! - _depIndexes.get(b.dep)! - ) - _depIndexes.clear() - } + if (!this._dirty && this._depsMaybeDirty) { pauseTracking() - for (const deferredComputed of this._deferredComputeds) { - triggerComputedGetter(deferredComputed) // wrap with function to avoid tree shaking - if (this._dirty) { - break + for (const dep of this.deps) { + if (dep.computed?._scheduled) { + triggerComputedGetter(dep.computed) // wrap with function call to avoid tree shaking + if (this._dirty) { + break + } } } resetTracking() } - this._deferredComputeds.length = 0 + this._depsMaybeDirty = false return this._dirty } run() { this._dirty = false + this._depsMaybeDirty = false if (!this.active) { return this.fn() } @@ -416,9 +409,9 @@ export function trigger( if (deps.length === 1) { if (deps[0]) { if (__DEV__) { - triggerEffects(deps[0], undefined, eventInfo) + triggerEffects(deps[0], false, eventInfo) } else { - triggerEffects(deps[0], undefined) + triggerEffects(deps[0], false) } } } else { @@ -429,28 +422,28 @@ export function trigger( } } if (__DEV__) { - triggerEffects(createDep(effects), undefined, eventInfo) + triggerEffects(createDep(effects), false, eventInfo) } else { - triggerEffects(createDep(effects), undefined) + triggerEffects(createDep(effects), false) } } } export function triggerEffects( dep: Dep | ReactiveEffect[], - deferredComputed: ComputedRefImpl | undefined, + isDepMaybeDirtyTrigger: boolean, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { // spread into array for stabilization const effects = isArray(dep) ? dep : [...dep] for (const effect of effects) { if (effect.computed) { - triggerEffect(effect, deferredComputed, debuggerEventExtraInfo) + triggerEffect(effect, isDepMaybeDirtyTrigger, debuggerEventExtraInfo) } } for (const effect of effects) { if (!effect.computed) { - triggerEffect(effect, deferredComputed, debuggerEventExtraInfo) + triggerEffect(effect, isDepMaybeDirtyTrigger, debuggerEventExtraInfo) } } } @@ -459,7 +452,7 @@ const queueEffectCbs: (() => void)[] = [] function triggerEffect( effect: ReactiveEffect, - deferredComputed: ComputedRefImpl | undefined, + isDepMaybeDirtyTrigger: boolean, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { if (effect !== activeEffect || effect.allowRecurse) { @@ -467,11 +460,11 @@ function triggerEffect( effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) } if (!effect._dirty) { - if (deferredComputed) { - effect._deferredComputeds.push(deferredComputed) + if (isDepMaybeDirtyTrigger) { + effect._depsMaybeDirty = true } else { effect._dirty = true - effect._deferredComputeds.length = 0 + effect._depsMaybeDirty = false } } effect.scheduler() diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 91826f35600..f9c29bf620a 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -16,9 +16,9 @@ import { isShallow } from './reactive' import type { ShallowReactiveMarker } from './reactive' -import type { ComputedRefImpl } from './computed' import { CollectionTypes } from './collectionHandlers' import { createDep, Dep } from './dep' +import type { ComputedRefImpl } from './computed' declare const RefSymbol: unique symbol export declare const RawSymbol: unique symbol @@ -33,43 +33,46 @@ export interface Ref { [RefSymbol]: true } -type RefBase = { +export type RefBase = { dep?: Dep value: T } -export function trackRefValue(ref: RefBase) { +export function trackRefValue( + ref: RefBase, + computed?: ComputedRefImpl +) { if (shouldTrack && activeEffect) { ref = toRaw(ref) if (__DEV__) { - trackEffects(ref.dep || (ref.dep = createDep()), { + trackEffects(ref.dep || (ref.dep = createDep(undefined, computed)), { target: ref, type: TrackOpTypes.GET, key: 'value' }) } else { - trackEffects(ref.dep || (ref.dep = createDep())) + trackEffects(ref.dep || (ref.dep = createDep(undefined, computed))) } } } export function triggerRefValue( ref: RefBase, - deferredComputed?: ComputedRefImpl, + isDepMaybeDirtyTrigger: boolean = false, newVal?: any ) { ref = toRaw(ref) const dep = ref.dep if (dep) { if (__DEV__) { - triggerEffects(dep, deferredComputed, { + triggerEffects(dep, isDepMaybeDirtyTrigger, { target: ref, type: TriggerOpTypes.SET, key: 'value', newValue: newVal }) } else { - triggerEffects(dep, deferredComputed) + triggerEffects(dep, isDepMaybeDirtyTrigger) } } } @@ -163,7 +166,7 @@ class RefImpl { if (hasChanged(newVal, this._rawValue)) { this._rawValue = newVal this._value = useDirectValue ? newVal : toReactive(newVal) - triggerRefValue(this, undefined, newVal) + triggerRefValue(this, false, newVal) } } } @@ -194,7 +197,7 @@ class RefImpl { * @see {@link https://vuejs.org/api/reactivity-advanced.html#triggerref} */ export function triggerRef(ref: Ref) { - triggerRefValue(ref, undefined, __DEV__ ? ref.value : void 0) + triggerRefValue(ref, false, __DEV__ ? ref.value : void 0) } export type MaybeRef = T | Ref From e74cd43672ecf051d3dc98d6d121789ce980c614 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Fri, 1 Sep 2023 08:35:21 +0800 Subject: [PATCH 084/110] chore: format --- packages/reactivity/src/dep.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/reactivity/src/dep.ts b/packages/reactivity/src/dep.ts index e470bc07c5c..4693a0cb057 100644 --- a/packages/reactivity/src/dep.ts +++ b/packages/reactivity/src/dep.ts @@ -20,7 +20,10 @@ type TrackedMarkers = { computed?: ComputedRefImpl } -export const createDep = (effects?: ReactiveEffect[], computed?: ComputedRefImpl): Dep => { +export const createDep = ( + effects?: ReactiveEffect[], + computed?: ComputedRefImpl +): Dep => { const dep = new Set(effects) as Dep dep.w = 0 dep.n = 0 From f5b03318d3499832e149ce7174828f445b453e61 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Fri, 1 Sep 2023 08:55:42 +0800 Subject: [PATCH 085/110] refactor: remove deferredComputed arg --- packages/reactivity/src/effect.ts | 38 +++++++++---------------------- packages/reactivity/src/ref.ts | 15 ++++-------- 2 files changed, 15 insertions(+), 38 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 82f2d8f3ada..6f53fbb2d81 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -430,9 +430,9 @@ export function trigger( if (deps.length === 1) { if (deps[0]) { if (__DEV__) { - triggerEffects(deps[0], TriggerType.ForceDirty, undefined, eventInfo) + triggerEffects(TriggerType.ForceDirty, deps[0], eventInfo) } else { - triggerEffects(deps[0], TriggerType.ForceDirty, undefined) + triggerEffects(TriggerType.ForceDirty, deps[0]) } } } else { @@ -443,44 +443,28 @@ export function trigger( } } if (__DEV__) { - triggerEffects( - createDep(effects), - TriggerType.ForceDirty, - undefined, - eventInfo - ) + triggerEffects(TriggerType.ForceDirty, createDep(effects), eventInfo) } else { - triggerEffects(createDep(effects), TriggerType.ForceDirty, undefined) + triggerEffects(TriggerType.ForceDirty, createDep(effects)) } } } export function triggerEffects( - dep: Dep | ReactiveEffect[], triggerType: TriggerType, - deferredComputed: ComputedRefImpl | undefined, + dep: Dep, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { // spread into array for stabilization - const effects = isArray(dep) ? dep : [...dep] + const effects = [...dep] for (const effect of effects) { if (effect.computed) { - triggerEffect( - effect, - triggerType, - deferredComputed, - debuggerEventExtraInfo - ) + triggerEffect(triggerType, dep, effect, debuggerEventExtraInfo) } } for (const effect of effects) { if (!effect.computed) { - triggerEffect( - effect, - triggerType, - deferredComputed, - debuggerEventExtraInfo - ) + triggerEffect(triggerType, dep, effect, debuggerEventExtraInfo) } } } @@ -488,9 +472,9 @@ export function triggerEffects( const queueEffectCbs: (() => void)[] = [] function triggerEffect( - effect: ReactiveEffect, triggerType: TriggerType, - deferredComputed: ComputedRefImpl | undefined, + triggerDep: Dep, + effect: ReactiveEffect, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { if (effect !== activeEffect || effect.allowRecurse) { @@ -504,7 +488,7 @@ function triggerEffect( triggerType === TriggerType.ComputedValueUpdated && (effect._deferredComputedAcceptMode === DeferredComputedAcceptMode.Always || - effect.deps.some(dep => dep.computed === deferredComputed)) + effect.deps.includes(triggerDep)) ) { effect.dirty = true } else if (triggerType === TriggerType.ForceDirty) { diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 30ca5ac1ae5..2055e26293c 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -60,22 +60,20 @@ export function trackRefValue( export function triggerRefValue( ref: RefBase, triggerType: TriggerType, - deferredComputed: ComputedRefImpl | undefined, newVal?: any ) { ref = toRaw(ref) - deferredComputed = toRaw(deferredComputed) const dep = ref.dep if (dep) { if (__DEV__) { - triggerEffects(dep, triggerType, deferredComputed, { + triggerEffects(triggerType, dep, { target: ref, type: TriggerOpTypes.SET, key: 'value', newValue: newVal }) } else { - triggerEffects(dep, triggerType, deferredComputed) + triggerEffects(triggerType, dep) } } } @@ -169,7 +167,7 @@ class RefImpl { if (hasChanged(newVal, this._rawValue)) { this._rawValue = newVal this._value = useDirectValue ? newVal : toReactive(newVal) - triggerRefValue(this, TriggerType.ForceDirty, undefined, newVal) + triggerRefValue(this, TriggerType.ForceDirty, newVal) } } } @@ -200,12 +198,7 @@ class RefImpl { * @see {@link https://vuejs.org/api/reactivity-advanced.html#triggerref} */ export function triggerRef(ref: Ref) { - triggerRefValue( - ref, - TriggerType.ForceDirty, - undefined, - __DEV__ ? ref.value : void 0 - ) + triggerRefValue(ref, TriggerType.ForceDirty, __DEV__ ? ref.value : void 0) } export type MaybeRef = T | Ref From 9e642190a46e42151e9300d86b53b6708b0407d0 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Fri, 1 Sep 2023 09:04:36 +0800 Subject: [PATCH 086/110] fix: fix vant test --- packages/reactivity/src/computed.ts | 9 ++------- packages/reactivity/src/effect.ts | 14 +++++--------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index c5a353c34ff..16bedf55bd2 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -1,9 +1,4 @@ -import { - DebuggerOptions, - DeferredComputedAcceptMode, - ReactiveEffect, - TriggerType -} from './effect' +import { DebuggerOptions, ReactiveEffect, TriggerType } from './effect' import { Ref, trackRefValue, triggerRefValue } from './ref' import { hasChanged, isFunction, NOOP } from '@vue/shared' import { ReactiveFlags, toRaw } from './reactive' @@ -52,7 +47,7 @@ export class ComputedRefImpl { triggerRefValue(this, TriggerType.ComputedDepsUpdated, this) } }) - this.effect._deferredComputedAcceptMode = DeferredComputedAcceptMode.Always + this.effect._alwaysAcceptComputedValueUpdated = true this.effect.computed = this this.effect.active = this._cacheable = !isSSR this[ReactiveFlags.IS_READONLY] = isReadonly diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 6f53fbb2d81..9aa9d112f2a 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -37,11 +37,6 @@ export enum TriggerType { ComputedValueUpdated = 3 } -export enum DeferredComputedAcceptMode { - Always = 1, - OnlyWhenQuerying = 2 -} - export type EffectScheduler = (triggerType: TriggerType, ...args: any[]) => any export type DebuggerEvent = { @@ -93,7 +88,7 @@ export class ReactiveEffect { _dirty = true _depsMaybeDirty = false - _deferredComputedAcceptMode = DeferredComputedAcceptMode.OnlyWhenQuerying + _alwaysAcceptComputedValueUpdated = false constructor( public fn: () => T, @@ -486,9 +481,10 @@ function triggerEffect( effect._depsMaybeDirty = true } else if ( triggerType === TriggerType.ComputedValueUpdated && - (effect._deferredComputedAcceptMode === - DeferredComputedAcceptMode.Always || - effect.deps.includes(triggerDep)) + (effect._alwaysAcceptComputedValueUpdated || + (effect._depsMaybeDirty && + triggerDep.computed?._scheduled && + effect.deps.includes(triggerDep))) ) { effect.dirty = true } else if (triggerType === TriggerType.ForceDirty) { From b43c4946777ea19423ad0ec31df6288eefae8c40 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Fri, 1 Sep 2023 09:24:15 +0800 Subject: [PATCH 087/110] chore: fix triggerRefValue newValue for dev --- packages/reactivity/src/computed.ts | 4 ++-- packages/reactivity/src/ref.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 16bedf55bd2..f62d87f444a 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -44,7 +44,7 @@ export class ComputedRefImpl { this.effect = new ReactiveEffect(getter, () => { if (!this._scheduled) { this._scheduled = true - triggerRefValue(this, TriggerType.ComputedDepsUpdated, this) + triggerRefValue(this, TriggerType.ComputedDepsUpdated) } }) this.effect._alwaysAcceptComputedValueUpdated = true @@ -60,7 +60,7 @@ export class ComputedRefImpl { if (!self._cacheable || self.effect.dirty) { const newValue = self.effect.run()! if (hasChanged(self._value, newValue)) { - triggerRefValue(self, TriggerType.ComputedValueUpdated, self) + triggerRefValue(self, TriggerType.ComputedValueUpdated) } self._value = newValue self._scheduled = false diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 2055e26293c..a0feb5e4ee1 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -294,7 +294,7 @@ class CustomRefImpl { constructor(factory: CustomRefFactory) { const { get, set } = factory( () => trackRefValue(this), - () => triggerRefValue(this, TriggerType.ForceDirty, undefined) + () => triggerRefValue(this, TriggerType.ForceDirty) ) this._get = get this._set = set From c522871fdc3603a152cd3e58528f4c1a45ff7679 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Fri, 1 Sep 2023 09:45:54 +0800 Subject: [PATCH 088/110] chore: remove _alwaysAcceptComputedValueUpdated --- packages/reactivity/src/computed.ts | 1 - packages/reactivity/src/effect.ts | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index f62d87f444a..38d69977ed6 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -47,7 +47,6 @@ export class ComputedRefImpl { triggerRefValue(this, TriggerType.ComputedDepsUpdated) } }) - this.effect._alwaysAcceptComputedValueUpdated = true this.effect.computed = this this.effect.active = this._cacheable = !isSSR this[ReactiveFlags.IS_READONLY] = isReadonly diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 9aa9d112f2a..9a7ab4a1116 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -88,7 +88,6 @@ export class ReactiveEffect { _dirty = true _depsMaybeDirty = false - _alwaysAcceptComputedValueUpdated = false constructor( public fn: () => T, @@ -481,7 +480,7 @@ function triggerEffect( effect._depsMaybeDirty = true } else if ( triggerType === TriggerType.ComputedValueUpdated && - (effect._alwaysAcceptComputedValueUpdated || + (effect.computed || (effect._depsMaybeDirty && triggerDep.computed?._scheduled && effect.deps.includes(triggerDep))) From 5bd18efd6c5ea02fed88a5b687ed5be42b732cbb Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 2 Sep 2023 05:05:07 +0800 Subject: [PATCH 089/110] chore: less changes --- packages/reactivity/src/effect.ts | 3 +-- packages/reactivity/src/ref.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 9a7ab4a1116..64e21283d60 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -10,7 +10,6 @@ import { wasTracked } from './dep' import type { ComputedRefImpl } from './computed' -import type { RefBase } from './ref' // The main WeakMap that stores {target -> key -> dep} connections. // Conceptually, it's easier to think of a dependency as a Dep class @@ -57,7 +56,7 @@ export let activeEffect: ReactiveEffect | undefined export const ITERATE_KEY = Symbol(__DEV__ ? 'iterate' : '') export const MAP_KEY_ITERATE_KEY = Symbol(__DEV__ ? 'Map key iterate' : '') -function triggerComputedGetter(computed: RefBase) { +function triggerComputedGetter(computed: ComputedRefImpl) { return computed.value } diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index a0feb5e4ee1..d5bf0f6da49 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -34,7 +34,7 @@ export interface Ref { [RefSymbol]: true } -export type RefBase = { +type RefBase = { dep?: Dep value: T } From b8c0a72bc8cb1a496e5455f15bc97be29e3de988 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 2 Sep 2023 05:08:47 +0800 Subject: [PATCH 090/110] chore: less changes --- packages/reactivity/src/deferredComputed.ts | 2 +- packages/reactivity/src/ref.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/reactivity/src/deferredComputed.ts b/packages/reactivity/src/deferredComputed.ts index f4b18372dcf..67893cd36ab 100644 --- a/packages/reactivity/src/deferredComputed.ts +++ b/packages/reactivity/src/deferredComputed.ts @@ -49,7 +49,7 @@ class DeferredComputedRefImpl { hasCompareTarget = false scheduler(() => { if (this.effect.active && this._get() !== valueToCompare) { - triggerRefValue(this, TriggerType.ForceDirty, undefined) + triggerRefValue(this) } scheduled = false }) diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index d5bf0f6da49..c38b777c738 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -59,7 +59,7 @@ export function trackRefValue( export function triggerRefValue( ref: RefBase, - triggerType: TriggerType, + triggerType: TriggerType = TriggerType.ForceDirty, newVal?: any ) { ref = toRaw(ref) @@ -294,7 +294,7 @@ class CustomRefImpl { constructor(factory: CustomRefFactory) { const { get, set } = factory( () => trackRefValue(this), - () => triggerRefValue(this, TriggerType.ForceDirty) + () => triggerRefValue(this) ) this._get = get this._set = set From 857f60e4beecb4a52190dfe862a8b6fc54ca3b94 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 2 Sep 2023 05:36:43 +0800 Subject: [PATCH 091/110] chore: remove unneeded `deps.includes` --- packages/reactivity/src/computed.ts | 4 +++- packages/reactivity/src/effect.ts | 7 +++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 38d69977ed6..203139971f1 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -32,8 +32,9 @@ export class ComputedRefImpl { public readonly __v_isRef = true public readonly [ReactiveFlags.IS_READONLY]: boolean = false - public _cacheable: boolean public _scheduled = false + public _valueMaybeDirty = false + public _cacheable: boolean constructor( getter: ComputedGetter, @@ -44,6 +45,7 @@ export class ComputedRefImpl { this.effect = new ReactiveEffect(getter, () => { if (!this._scheduled) { this._scheduled = true + this._valueMaybeDirty = true triggerRefValue(this, TriggerType.ComputedDepsUpdated) } }) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 64e21283d60..9c63f0276f5 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -100,8 +100,9 @@ export class ReactiveEffect { if (!this._dirty && this._depsMaybeDirty) { pauseTracking() for (const dep of this.deps) { - if (dep.computed?._scheduled) { + if (dep.computed?._valueMaybeDirty) { triggerComputedGetter(dep.computed) // wrap with function call to avoid tree shaking + dep.computed._valueMaybeDirty = false if (this._dirty) { break } @@ -480,9 +481,7 @@ function triggerEffect( } else if ( triggerType === TriggerType.ComputedValueUpdated && (effect.computed || - (effect._depsMaybeDirty && - triggerDep.computed?._scheduled && - effect.deps.includes(triggerDep))) + (effect._depsMaybeDirty && triggerDep.computed?._valueMaybeDirty)) ) { effect.dirty = true } else if (triggerType === TriggerType.ForceDirty) { From 174626c64c951aae6652fa3e4a30774d6ea8b2ff Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 2 Sep 2023 05:41:10 +0800 Subject: [PATCH 092/110] refactor: reduce `dep` arg for triggerEffect() --- packages/reactivity/src/effect.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 9c63f0276f5..35e8cce56a8 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -453,12 +453,12 @@ export function triggerEffects( const effects = [...dep] for (const effect of effects) { if (effect.computed) { - triggerEffect(triggerType, dep, effect, debuggerEventExtraInfo) + triggerEffect(triggerType, effect, debuggerEventExtraInfo) } } for (const effect of effects) { if (!effect.computed) { - triggerEffect(triggerType, dep, effect, debuggerEventExtraInfo) + triggerEffect(triggerType, effect, debuggerEventExtraInfo) } } } @@ -467,7 +467,6 @@ const queueEffectCbs: (() => void)[] = [] function triggerEffect( triggerType: TriggerType, - triggerDep: Dep, effect: ReactiveEffect, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { @@ -480,8 +479,7 @@ function triggerEffect( effect._depsMaybeDirty = true } else if ( triggerType === TriggerType.ComputedValueUpdated && - (effect.computed || - (effect._depsMaybeDirty && triggerDep.computed?._valueMaybeDirty)) + (effect.computed || effect._depsMaybeDirty) ) { effect.dirty = true } else if (triggerType === TriggerType.ForceDirty) { From d73ba7ab7aca5599832c974d61f2376972ef3f12 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 2 Sep 2023 05:49:17 +0800 Subject: [PATCH 093/110] chore: move `_valueMaybeDirty = false` --- packages/reactivity/src/effect.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 35e8cce56a8..6dcec3d6245 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -101,8 +101,8 @@ export class ReactiveEffect { pauseTracking() for (const dep of this.deps) { if (dep.computed?._valueMaybeDirty) { - triggerComputedGetter(dep.computed) // wrap with function call to avoid tree shaking dep.computed._valueMaybeDirty = false + triggerComputedGetter(dep.computed) // wrap with function call to avoid tree shaking if (this._dirty) { break } From 80ea2507673b00f640567b64328c6fcef73e235c Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 2 Sep 2023 06:25:29 +0800 Subject: [PATCH 094/110] fix: remove `_valueMaybeDirty` for fix vuetify test --- packages/reactivity/src/computed.ts | 2 -- packages/reactivity/src/effect.ts | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 203139971f1..3354b36aaeb 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -33,7 +33,6 @@ export class ComputedRefImpl { public readonly [ReactiveFlags.IS_READONLY]: boolean = false public _scheduled = false - public _valueMaybeDirty = false public _cacheable: boolean constructor( @@ -45,7 +44,6 @@ export class ComputedRefImpl { this.effect = new ReactiveEffect(getter, () => { if (!this._scheduled) { this._scheduled = true - this._valueMaybeDirty = true triggerRefValue(this, TriggerType.ComputedDepsUpdated) } }) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 6dcec3d6245..6466cc72849 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -100,8 +100,7 @@ export class ReactiveEffect { if (!this._dirty && this._depsMaybeDirty) { pauseTracking() for (const dep of this.deps) { - if (dep.computed?._valueMaybeDirty) { - dep.computed._valueMaybeDirty = false + if (dep.computed?._scheduled) { triggerComputedGetter(dep.computed) // wrap with function call to avoid tree shaking if (this._dirty) { break From e339def29c63d698ad8adf36f5ace2de960b2152 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 2 Sep 2023 07:05:38 +0800 Subject: [PATCH 095/110] refactor: abstract triggerType --- packages/reactivity/src/computed.ts | 8 +-- packages/reactivity/src/deferredComputed.ts | 6 +-- packages/reactivity/src/effect.ts | 55 ++++++++------------- packages/reactivity/src/index.ts | 1 - packages/reactivity/src/operations.ts | 6 +++ packages/reactivity/src/ref.ts | 9 ++-- packages/runtime-core/src/renderer.ts | 21 +------- 7 files changed, 41 insertions(+), 65 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 3354b36aaeb..aab3098fe30 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -1,8 +1,9 @@ -import { DebuggerOptions, ReactiveEffect, TriggerType } from './effect' +import { DebuggerOptions, ReactiveEffect } from './effect' import { Ref, trackRefValue, triggerRefValue } from './ref' import { hasChanged, isFunction, NOOP } from '@vue/shared' import { ReactiveFlags, toRaw } from './reactive' import { Dep } from './dep' +import { TriggerTypes } from './operations' declare const ComputedRefSymbol: unique symbol @@ -44,9 +45,10 @@ export class ComputedRefImpl { this.effect = new ReactiveEffect(getter, () => { if (!this._scheduled) { this._scheduled = true - triggerRefValue(this, TriggerType.ComputedDepsUpdated) + triggerRefValue(this, TriggerTypes.ComputedDepsUpdated) } }) + this.effect._triggerFlags |= TriggerTypes.ComputedValueUpdated this.effect.computed = this this.effect.active = this._cacheable = !isSSR this[ReactiveFlags.IS_READONLY] = isReadonly @@ -59,7 +61,7 @@ export class ComputedRefImpl { if (!self._cacheable || self.effect.dirty) { const newValue = self.effect.run()! if (hasChanged(self._value, newValue)) { - triggerRefValue(self, TriggerType.ComputedValueUpdated) + triggerRefValue(self, TriggerTypes.ComputedValueUpdated) } self._value = newValue self._scheduled = false diff --git a/packages/reactivity/src/deferredComputed.ts b/packages/reactivity/src/deferredComputed.ts index 67893cd36ab..16f708c2b75 100644 --- a/packages/reactivity/src/deferredComputed.ts +++ b/packages/reactivity/src/deferredComputed.ts @@ -1,5 +1,5 @@ import { Dep } from './dep' -import { ReactiveEffect, TriggerType } from './effect' +import { ReactiveEffect } from './effect' import { ComputedGetter, ComputedRef } from './computed' import { ReactiveFlags, toRaw } from './reactive' import { trackRefValue, triggerRefValue } from './ref' @@ -38,7 +38,7 @@ class DeferredComputedRefImpl { let compareTarget: any let hasCompareTarget = false let scheduled = false - this.effect = new ReactiveEffect(getter, (_, computedTrigger?: boolean) => { + this.effect = new ReactiveEffect(getter, (computedTrigger?: boolean) => { if (this.dep) { if (computedTrigger) { compareTarget = this._value @@ -59,7 +59,7 @@ class DeferredComputedRefImpl { // deferred to be triggered in scheduler. for (const e of this.dep) { if (e.computed instanceof DeferredComputedRefImpl) { - e.scheduler(TriggerType.ForceDirty, true /* computedTrigger */) + e.scheduler(true /* computedTrigger */) } } } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 6466cc72849..75c3f44cb94 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -1,4 +1,4 @@ -import { TrackOpTypes, TriggerOpTypes } from './operations' +import { TrackOpTypes, TriggerOpTypes, TriggerTypes } from './operations' import { extend, isArray, isIntegerKey, isMap } from '@vue/shared' import { EffectScope, recordEffectScope } from './effectScope' import { @@ -30,13 +30,7 @@ export let trackOpBit = 1 */ const maxMarkerBits = 30 -export enum TriggerType { - ForceDirty = 1, - ComputedDepsUpdated = 2, - ComputedValueUpdated = 3 -} - -export type EffectScheduler = (triggerType: TriggerType, ...args: any[]) => any +export type EffectScheduler = (...args: any[]) => any export type DebuggerEvent = { effect: ReactiveEffect @@ -87,6 +81,7 @@ export class ReactiveEffect { _dirty = true _depsMaybeDirty = false + _triggerFlags = TriggerTypes.ForceDirty | TriggerTypes.ComputedDepsUpdated constructor( public fn: () => T, @@ -222,22 +217,12 @@ export function effect( fn = (fn as ReactiveEffectRunner).effect.fn } - let scheduled = false - - const _effect = new ReactiveEffect(fn, triggerType => { - if ( - triggerType === TriggerType.ForceDirty || - triggerType === TriggerType.ComputedDepsUpdated || - !scheduled - ) { - scheduled = true - queueEffectCbs.push(() => { - if (_effect.dirty) { - _effect.run() - } - scheduled = false - }) - } + const _effect = new ReactiveEffect(fn, () => { + queueEffectCbs.push(() => { + if (_effect.dirty) { + _effect.run() + } + }) }) if (options) { extend(_effect, options) @@ -423,9 +408,9 @@ export function trigger( if (deps.length === 1) { if (deps[0]) { if (__DEV__) { - triggerEffects(TriggerType.ForceDirty, deps[0], eventInfo) + triggerEffects(TriggerTypes.ForceDirty, deps[0], eventInfo) } else { - triggerEffects(TriggerType.ForceDirty, deps[0]) + triggerEffects(TriggerTypes.ForceDirty, deps[0]) } } } else { @@ -436,15 +421,15 @@ export function trigger( } } if (__DEV__) { - triggerEffects(TriggerType.ForceDirty, createDep(effects), eventInfo) + triggerEffects(TriggerTypes.ForceDirty, createDep(effects), eventInfo) } else { - triggerEffects(TriggerType.ForceDirty, createDep(effects)) + triggerEffects(TriggerTypes.ForceDirty, createDep(effects)) } } } export function triggerEffects( - triggerType: TriggerType, + triggerType: TriggerTypes, dep: Dep, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { @@ -465,7 +450,7 @@ export function triggerEffects( const queueEffectCbs: (() => void)[] = [] function triggerEffect( - triggerType: TriggerType, + triggerType: TriggerTypes, effect: ReactiveEffect, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { @@ -474,18 +459,20 @@ function triggerEffect( effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) } if (!effect._dirty) { - if (triggerType === TriggerType.ComputedDepsUpdated) { + if (triggerType === TriggerTypes.ComputedDepsUpdated) { effect._depsMaybeDirty = true } else if ( - triggerType === TriggerType.ComputedValueUpdated && + triggerType === TriggerTypes.ComputedValueUpdated && (effect.computed || effect._depsMaybeDirty) ) { effect.dirty = true - } else if (triggerType === TriggerType.ForceDirty) { + } else if (triggerType === TriggerTypes.ForceDirty) { effect.dirty = true } } - effect.scheduler(triggerType) + if (effect._triggerFlags & triggerType) { + effect.scheduler(triggerType) + } } scheduleEffectCallbacks() } diff --git a/packages/reactivity/src/index.ts b/packages/reactivity/src/index.ts index 05c3ac6ab56..ee4da5b1935 100644 --- a/packages/reactivity/src/index.ts +++ b/packages/reactivity/src/index.ts @@ -56,7 +56,6 @@ export { resetTracking, ITERATE_KEY, ReactiveEffect, - TriggerType, type ReactiveEffectRunner, type ReactiveEffectOptions, type EffectScheduler, diff --git a/packages/reactivity/src/operations.ts b/packages/reactivity/src/operations.ts index 1b96e982571..024aa4f267b 100644 --- a/packages/reactivity/src/operations.ts +++ b/packages/reactivity/src/operations.ts @@ -13,3 +13,9 @@ export const enum TriggerOpTypes { DELETE = 'delete', CLEAR = 'clear' } + +export const enum TriggerTypes { + ForceDirty = 1 << 0, + ComputedDepsUpdated = 1 << 1, + ComputedValueUpdated = 1 << 2 +} diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index c38b777c738..8b2c2d8bcd3 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -1,12 +1,11 @@ import { - TriggerType, activeEffect, getDepFromReactive, shouldTrack, trackEffects, triggerEffects } from './effect' -import { TrackOpTypes, TriggerOpTypes } from './operations' +import { TrackOpTypes, TriggerOpTypes, TriggerTypes } from './operations' import { isArray, hasChanged, IfAny, isFunction, isObject } from '@vue/shared' import { isProxy, @@ -59,7 +58,7 @@ export function trackRefValue( export function triggerRefValue( ref: RefBase, - triggerType: TriggerType = TriggerType.ForceDirty, + triggerType: TriggerTypes = TriggerTypes.ForceDirty, newVal?: any ) { ref = toRaw(ref) @@ -167,7 +166,7 @@ class RefImpl { if (hasChanged(newVal, this._rawValue)) { this._rawValue = newVal this._value = useDirectValue ? newVal : toReactive(newVal) - triggerRefValue(this, TriggerType.ForceDirty, newVal) + triggerRefValue(this, TriggerTypes.ForceDirty, newVal) } } } @@ -198,7 +197,7 @@ class RefImpl { * @see {@link https://vuejs.org/api/reactivity-advanced.html#triggerref} */ export function triggerRef(ref: Ref) { - triggerRefValue(ref, TriggerType.ForceDirty, __DEV__ ? ref.value : void 0) + triggerRefValue(ref, TriggerTypes.ForceDirty, __DEV__ ? ref.value : void 0) } export type MaybeRef = T | Ref diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 003a8e77fa3..eeee23b391d 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -45,12 +45,7 @@ import { flushPreFlushCbs, SchedulerJob } from './scheduler' -import { - pauseTracking, - resetTracking, - ReactiveEffect, - TriggerType -} from '@vue/reactivity' +import { pauseTracking, resetTracking, ReactiveEffect } from '@vue/reactivity' import { updateProps } from './componentProps' import { updateSlots } from './componentSlots' import { pushWarningContext, popWarningContext, warn } from './warning' @@ -1549,21 +1544,10 @@ function baseCreateRenderer( } } - let scheduled = false - // create reactive effect for rendering const effect = (instance.effect = new ReactiveEffect( componentUpdateFn, - triggerType => { - if ( - triggerType === TriggerType.ForceDirty || - triggerType === TriggerType.ComputedDepsUpdated || - !scheduled - ) { - scheduled = true - queueJob(update) - } - }, + () => queueJob(update), instance.scope // track it in component's effect scope )) @@ -1571,7 +1555,6 @@ function baseCreateRenderer( if (effect.dirty) { effect.run() } - scheduled = false }) update.id = instance.uid // allowRecurse From f3625b0ffff0951bea8f83683f1bf1346edc5693 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 2 Sep 2023 07:09:58 +0800 Subject: [PATCH 096/110] chore: fix deferredComputed --- packages/reactivity/src/effect.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 75c3f44cb94..e6506b8a95c 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -471,7 +471,7 @@ function triggerEffect( } } if (effect._triggerFlags & triggerType) { - effect.scheduler(triggerType) + effect.scheduler() } } scheduleEffectCallbacks() From f41bc9b8a62826d41b308e2b097077694e3183b0 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 2 Sep 2023 07:14:40 +0800 Subject: [PATCH 097/110] feat: redo apiWatch --- .../runtime-core/__tests__/apiWatch.spec.ts | 25 +++++++++++++++++++ packages/runtime-core/src/apiWatch.ts | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/__tests__/apiWatch.spec.ts b/packages/runtime-core/__tests__/apiWatch.spec.ts index f24ce80b9df..7ead1011e25 100644 --- a/packages/runtime-core/__tests__/apiWatch.spec.ts +++ b/packages/runtime-core/__tests__/apiWatch.spec.ts @@ -1205,4 +1205,29 @@ describe('api: watch', () => { expect(countWE).toBe(3) expect(countW).toBe(2) }) + + it('watch callback on-demand trigger', () => { + const effectSpy = vi.fn() + + const sec = ref(0) + const min = computed(() => { + return Math.floor(sec.value / 60) + }) + const hour = computed(() => { + return Math.floor(min.value / 60) + }) + + watchEffect( + () => { + effectSpy() + min.value + hour.value + }, + { flush: 'sync' } + ) + + for (sec.value = 0; sec.value < 1000; sec.value++) {} + + expect(effectSpy).toHaveBeenCalledTimes(17) + }) }) diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts index 1b85ba12d19..4b57c4b355d 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -307,7 +307,7 @@ function doWatch( ? new Array((source as []).length).fill(INITIAL_WATCHER_VALUE) : INITIAL_WATCHER_VALUE const job: SchedulerJob = () => { - if (!effect.active) { + if (!effect.active || !effect.dirty) { return } if (cb) { From 09d6f0552161b2ddefd3e8b31acaabf38a92a652 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 2 Sep 2023 09:01:10 +0800 Subject: [PATCH 098/110] fix: continuous effects cause computed dirty race condition --- packages/reactivity/__tests__/computed.spec.ts | 18 ++++++++++++++++++ packages/reactivity/src/effect.ts | 6 ++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index 2a3629d66ce..6ee6f913379 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -359,4 +359,22 @@ describe('reactivity/computed', () => { d.value expect(cSpy).toHaveBeenCalledTimes(1) }) + + test('should not continuous effects cause computed dirty race condition', () => { + const fnSpy = vi.fn() + const v = ref(1) + const c = computed(() => v.value) + + effect(() => { + c.value + }) + effect(() => { + c.value + fnSpy() + }) + + expect(fnSpy).toBeCalledTimes(1) + v.value = 2 + expect(fnSpy).toBeCalledTimes(2) + }) }) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index e6506b8a95c..8f440472ea5 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -462,12 +462,10 @@ function triggerEffect( if (triggerType === TriggerTypes.ComputedDepsUpdated) { effect._depsMaybeDirty = true } else if ( - triggerType === TriggerTypes.ComputedValueUpdated && - (effect.computed || effect._depsMaybeDirty) + triggerType === TriggerTypes.ComputedValueUpdated || + triggerType === TriggerTypes.ForceDirty ) { effect.dirty = true - } else if (triggerType === TriggerTypes.ForceDirty) { - effect.dirty = true } } if (effect._triggerFlags & triggerType) { From 576be5f0bcdb31a092ca9bf3b16ea3c1b832b8cc Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 2 Sep 2023 11:33:28 +0800 Subject: [PATCH 099/110] fix: render triggers twice --- packages/reactivity/src/computed.ts | 7 ++-- packages/reactivity/src/effect.ts | 60 +++++++++++++-------------- packages/reactivity/src/operations.ts | 9 ++-- packages/reactivity/src/ref.ts | 12 +++--- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index aab3098fe30..9a58701ddd1 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -3,7 +3,7 @@ import { Ref, trackRefValue, triggerRefValue } from './ref' import { hasChanged, isFunction, NOOP } from '@vue/shared' import { ReactiveFlags, toRaw } from './reactive' import { Dep } from './dep' -import { TriggerTypes } from './operations' +import { DirtyLevels } from './operations' declare const ComputedRefSymbol: unique symbol @@ -45,10 +45,9 @@ export class ComputedRefImpl { this.effect = new ReactiveEffect(getter, () => { if (!this._scheduled) { this._scheduled = true - triggerRefValue(this, TriggerTypes.ComputedDepsUpdated) + triggerRefValue(this, DirtyLevels.DepsMaybeDirty) } }) - this.effect._triggerFlags |= TriggerTypes.ComputedValueUpdated this.effect.computed = this this.effect.active = this._cacheable = !isSSR this[ReactiveFlags.IS_READONLY] = isReadonly @@ -61,7 +60,7 @@ export class ComputedRefImpl { if (!self._cacheable || self.effect.dirty) { const newValue = self.effect.run()! if (hasChanged(self._value, newValue)) { - triggerRefValue(self, TriggerTypes.ComputedValueUpdated) + triggerRefValue(self, DirtyLevels.ComputedValueDirty) } self._value = newValue self._scheduled = false diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 8f440472ea5..e6e23a0ccfa 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -1,4 +1,4 @@ -import { TrackOpTypes, TriggerOpTypes, TriggerTypes } from './operations' +import { DirtyLevels, TrackOpTypes, TriggerOpTypes } from './operations' import { extend, isArray, isIntegerKey, isMap } from '@vue/shared' import { EffectScope, recordEffectScope } from './effectScope' import { @@ -79,9 +79,7 @@ export class ReactiveEffect { // dev only onTrigger?: (event: DebuggerEvent) => void - _dirty = true - _depsMaybeDirty = false - _triggerFlags = TriggerTypes.ForceDirty | TriggerTypes.ComputedDepsUpdated + _dirtyLevel = DirtyLevels.Dirty constructor( public fn: () => T, @@ -92,30 +90,36 @@ export class ReactiveEffect { } public get dirty() { - if (!this._dirty && this._depsMaybeDirty) { + if (this._dirtyLevel === DirtyLevels.DepsMaybeDirty) { + this._dirtyLevel = DirtyLevels.NotDirty pauseTracking() for (const dep of this.deps) { if (dep.computed?._scheduled) { triggerComputedGetter(dep.computed) // wrap with function call to avoid tree shaking - if (this._dirty) { + if (this._dirtyLevel >= DirtyLevels.ComputedValueDirty) { break } } } resetTracking() } - this._depsMaybeDirty = false - return this._dirty + return this._dirtyLevel >= DirtyLevels.ComputedValueDirty } public set dirty(v) { - this._dirty = v - this._depsMaybeDirty = false + this._dirtyLevel = v ? DirtyLevels.Dirty : DirtyLevels.NotDirty } run() { - this._dirty = false - this._depsMaybeDirty = false + this._dirtyLevel = DirtyLevels.NotDirty + const result = this._run() + if ((this._dirtyLevel as DirtyLevels) === DirtyLevels.ComputedValueDirty) { + this._dirtyLevel = DirtyLevels.NotDirty + } + return result + } + + _run() { if (!this.active) { return this.fn() } @@ -408,9 +412,9 @@ export function trigger( if (deps.length === 1) { if (deps[0]) { if (__DEV__) { - triggerEffects(TriggerTypes.ForceDirty, deps[0], eventInfo) + triggerEffects(deps[0], DirtyLevels.Dirty, eventInfo) } else { - triggerEffects(TriggerTypes.ForceDirty, deps[0]) + triggerEffects(deps[0], DirtyLevels.Dirty) } } } else { @@ -421,28 +425,28 @@ export function trigger( } } if (__DEV__) { - triggerEffects(TriggerTypes.ForceDirty, createDep(effects), eventInfo) + triggerEffects(createDep(effects), DirtyLevels.Dirty, eventInfo) } else { - triggerEffects(TriggerTypes.ForceDirty, createDep(effects)) + triggerEffects(createDep(effects), DirtyLevels.Dirty) } } } export function triggerEffects( - triggerType: TriggerTypes, dep: Dep, + dirtyLevel: DirtyLevels, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { // spread into array for stabilization const effects = [...dep] for (const effect of effects) { if (effect.computed) { - triggerEffect(triggerType, effect, debuggerEventExtraInfo) + triggerEffect(effect, dirtyLevel, debuggerEventExtraInfo) } } for (const effect of effects) { if (!effect.computed) { - triggerEffect(triggerType, effect, debuggerEventExtraInfo) + triggerEffect(effect, dirtyLevel, debuggerEventExtraInfo) } } } @@ -450,25 +454,21 @@ export function triggerEffects( const queueEffectCbs: (() => void)[] = [] function triggerEffect( - triggerType: TriggerTypes, effect: ReactiveEffect, + dirtyLevel: DirtyLevels, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { if (effect !== activeEffect || effect.allowRecurse) { if (__DEV__ && effect.onTrigger) { effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) } - if (!effect._dirty) { - if (triggerType === TriggerTypes.ComputedDepsUpdated) { - effect._depsMaybeDirty = true - } else if ( - triggerType === TriggerTypes.ComputedValueUpdated || - triggerType === TriggerTypes.ForceDirty - ) { - effect.dirty = true - } + if (effect._dirtyLevel < dirtyLevel) { + effect._dirtyLevel = dirtyLevel } - if (effect._triggerFlags & triggerType) { + if ( + dirtyLevel === DirtyLevels.DepsMaybeDirty || + dirtyLevel === DirtyLevels.Dirty + ) { effect.scheduler() } } diff --git a/packages/reactivity/src/operations.ts b/packages/reactivity/src/operations.ts index 024aa4f267b..2e3d89ddfd2 100644 --- a/packages/reactivity/src/operations.ts +++ b/packages/reactivity/src/operations.ts @@ -14,8 +14,9 @@ export const enum TriggerOpTypes { CLEAR = 'clear' } -export const enum TriggerTypes { - ForceDirty = 1 << 0, - ComputedDepsUpdated = 1 << 1, - ComputedValueUpdated = 1 << 2 +export const enum DirtyLevels { + NotDirty = 0, + DepsMaybeDirty = 1, + ComputedValueDirty = 2, + Dirty = 3 } diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 8b2c2d8bcd3..1d789353064 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -5,7 +5,7 @@ import { trackEffects, triggerEffects } from './effect' -import { TrackOpTypes, TriggerOpTypes, TriggerTypes } from './operations' +import { DirtyLevels, TrackOpTypes, TriggerOpTypes } from './operations' import { isArray, hasChanged, IfAny, isFunction, isObject } from '@vue/shared' import { isProxy, @@ -58,21 +58,21 @@ export function trackRefValue( export function triggerRefValue( ref: RefBase, - triggerType: TriggerTypes = TriggerTypes.ForceDirty, + dirtyLevel: DirtyLevels = DirtyLevels.Dirty, newVal?: any ) { ref = toRaw(ref) const dep = ref.dep if (dep) { if (__DEV__) { - triggerEffects(triggerType, dep, { + triggerEffects(dep, dirtyLevel, { target: ref, type: TriggerOpTypes.SET, key: 'value', newValue: newVal }) } else { - triggerEffects(triggerType, dep) + triggerEffects(dep, dirtyLevel) } } } @@ -166,7 +166,7 @@ class RefImpl { if (hasChanged(newVal, this._rawValue)) { this._rawValue = newVal this._value = useDirectValue ? newVal : toReactive(newVal) - triggerRefValue(this, TriggerTypes.ForceDirty, newVal) + triggerRefValue(this, DirtyLevels.Dirty, newVal) } } } @@ -197,7 +197,7 @@ class RefImpl { * @see {@link https://vuejs.org/api/reactivity-advanced.html#triggerref} */ export function triggerRef(ref: Ref) { - triggerRefValue(ref, TriggerTypes.ForceDirty, __DEV__ ? ref.value : void 0) + triggerRefValue(ref, DirtyLevels.Dirty, __DEV__ ? ref.value : void 0) } export type MaybeRef = T | Ref From 7058975890e58145b9f880777e1010a15963e231 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 2 Sep 2023 11:49:10 +0800 Subject: [PATCH 100/110] refactor: remove queueEffectCbs --- packages/reactivity/src/effect.ts | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index e6e23a0ccfa..cca91a89630 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -114,7 +114,7 @@ export class ReactiveEffect { this._dirtyLevel = DirtyLevels.NotDirty const result = this._run() if ((this._dirtyLevel as DirtyLevels) === DirtyLevels.ComputedValueDirty) { - this._dirtyLevel = DirtyLevels.NotDirty + this._dirtyLevel-- } return result } @@ -222,11 +222,9 @@ export function effect( } const _effect = new ReactiveEffect(fn, () => { - queueEffectCbs.push(() => { - if (_effect.dirty) { - _effect.run() - } - }) + if (_effect.dirty) { + _effect.run() + } }) if (options) { extend(_effect, options) @@ -451,8 +449,6 @@ export function triggerEffects( } } -const queueEffectCbs: (() => void)[] = [] - function triggerEffect( effect: ReactiveEffect, dirtyLevel: DirtyLevels, @@ -472,15 +468,6 @@ function triggerEffect( effect.scheduler() } } - scheduleEffectCallbacks() -} - -function scheduleEffectCallbacks() { - if (effectTrackDepth === 0) { - while (queueEffectCbs.length) { - queueEffectCbs.shift()!() - } - } } export function getDepFromReactive(object: any, key: string | number | symbol) { From b3f502ed5fd017e2ad062967f8fe7a0c149c8fcd Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 3 Sep 2023 11:11:20 +0800 Subject: [PATCH 101/110] chore: DepsMaybeDirty -> ComputedValueMaybeDirty --- packages/reactivity/src/computed.ts | 2 +- packages/reactivity/src/effect.ts | 4 ++-- packages/reactivity/src/operations.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 9a58701ddd1..d61c341b7d5 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -45,7 +45,7 @@ export class ComputedRefImpl { this.effect = new ReactiveEffect(getter, () => { if (!this._scheduled) { this._scheduled = true - triggerRefValue(this, DirtyLevels.DepsMaybeDirty) + triggerRefValue(this, DirtyLevels.ComputedValueMaybeDirty) } }) this.effect.computed = this diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index cca91a89630..bcd1619f280 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -90,7 +90,7 @@ export class ReactiveEffect { } public get dirty() { - if (this._dirtyLevel === DirtyLevels.DepsMaybeDirty) { + if (this._dirtyLevel === DirtyLevels.ComputedValueMaybeDirty) { this._dirtyLevel = DirtyLevels.NotDirty pauseTracking() for (const dep of this.deps) { @@ -462,7 +462,7 @@ function triggerEffect( effect._dirtyLevel = dirtyLevel } if ( - dirtyLevel === DirtyLevels.DepsMaybeDirty || + dirtyLevel === DirtyLevels.ComputedValueMaybeDirty || dirtyLevel === DirtyLevels.Dirty ) { effect.scheduler() diff --git a/packages/reactivity/src/operations.ts b/packages/reactivity/src/operations.ts index 2e3d89ddfd2..26b231e3dc6 100644 --- a/packages/reactivity/src/operations.ts +++ b/packages/reactivity/src/operations.ts @@ -16,7 +16,7 @@ export const enum TriggerOpTypes { export const enum DirtyLevels { NotDirty = 0, - DepsMaybeDirty = 1, + ComputedValueMaybeDirty = 1, ComputedValueDirty = 2, Dirty = 3 } From a1cb22a59b19266781e9fd7c910582212a8575b1 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 3 Sep 2023 11:57:27 +0800 Subject: [PATCH 102/110] fix: computed scheduler should always trigger --- packages/reactivity/src/effect.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index bcd1619f280..b9c28962761 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -463,7 +463,9 @@ function triggerEffect( } if ( dirtyLevel === DirtyLevels.ComputedValueMaybeDirty || - dirtyLevel === DirtyLevels.Dirty + dirtyLevel === DirtyLevels.Dirty || + // computed scheduler should always trigger + effect.computed ) { effect.scheduler() } From bf85f9aa93b7d93129f3b7d2383900ccc908698d Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 3 Sep 2023 12:06:28 +0800 Subject: [PATCH 103/110] refactor: remove computed hack --- packages/reactivity/src/effect.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index b9c28962761..39f90636dde 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -80,6 +80,7 @@ export class ReactiveEffect { onTrigger?: (event: DebuggerEvent) => void _dirtyLevel = DirtyLevels.Dirty + _queryingDirty = false constructor( public fn: () => T, @@ -92,6 +93,7 @@ export class ReactiveEffect { public get dirty() { if (this._dirtyLevel === DirtyLevels.ComputedValueMaybeDirty) { this._dirtyLevel = DirtyLevels.NotDirty + this._queryingDirty = true pauseTracking() for (const dep of this.deps) { if (dep.computed?._scheduled) { @@ -102,6 +104,7 @@ export class ReactiveEffect { } } resetTracking() + this._queryingDirty = false } return this._dirtyLevel >= DirtyLevels.ComputedValueDirty } @@ -464,8 +467,7 @@ function triggerEffect( if ( dirtyLevel === DirtyLevels.ComputedValueMaybeDirty || dirtyLevel === DirtyLevels.Dirty || - // computed scheduler should always trigger - effect.computed + (dirtyLevel === DirtyLevels.ComputedValueDirty && !effect._queryingDirty) ) { effect.scheduler() } From ab252291a19102b8555f65c187788f5aa29764dd Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 7 Sep 2023 16:44:16 +0800 Subject: [PATCH 104/110] Update deferredComputed.ts --- packages/reactivity/src/deferredComputed.ts | 51 +++++++++++---------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/packages/reactivity/src/deferredComputed.ts b/packages/reactivity/src/deferredComputed.ts index 16f708c2b75..8c4b163d0ba 100644 --- a/packages/reactivity/src/deferredComputed.ts +++ b/packages/reactivity/src/deferredComputed.ts @@ -38,33 +38,38 @@ class DeferredComputedRefImpl { let compareTarget: any let hasCompareTarget = false let scheduled = false - this.effect = new ReactiveEffect(getter, (computedTrigger?: boolean) => { - if (this.dep) { - if (computedTrigger) { - compareTarget = this._value - hasCompareTarget = true - } else if (!scheduled) { - const valueToCompare = hasCompareTarget ? compareTarget : this._value - scheduled = true - hasCompareTarget = false - scheduler(() => { - if (this.effect.active && this._get() !== valueToCompare) { - triggerRefValue(this) + this.effect = new ReactiveEffect( + getter, + (onScheduled, computedTrigger?: boolean) => { + if (this.dep) { + if (computedTrigger) { + compareTarget = this._value + hasCompareTarget = true + } else if (!scheduled) { + const valueToCompare = hasCompareTarget + ? compareTarget + : this._value + scheduled = true + hasCompareTarget = false + scheduler(() => { + if (this.effect.active && this._get() !== valueToCompare) { + triggerRefValue(this) + } + scheduled = false + }) + } + // chained upstream computeds are notified synchronously to ensure + // value invalidation in case of sync access; normal effects are + // deferred to be triggered in scheduler. + for (const e of this.dep) { + if (e.computed instanceof DeferredComputedRefImpl) { + e.scheduler(onScheduled, true /* computedTrigger */) } - scheduled = false - }) - } - // chained upstream computeds are notified synchronously to ensure - // value invalidation in case of sync access; normal effects are - // deferred to be triggered in scheduler. - for (const e of this.dep) { - if (e.computed instanceof DeferredComputedRefImpl) { - e.scheduler(true /* computedTrigger */) } } + this._dirty = true } - this._dirty = true - }) + ) this.effect.computed = this as any } From 3f5c373da4c231088543fe294292255417cc270a Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 7 Sep 2023 17:57:32 +0800 Subject: [PATCH 105/110] make new logic working for `watch()` --- packages/reactivity/src/computed.ts | 5 +++-- packages/reactivity/src/effect.ts | 6 ++++-- packages/runtime-core/src/apiWatch.ts | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index d61c341b7d5..7509d397d83 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -59,10 +59,11 @@ export class ComputedRefImpl { trackRefValue(self, self) if (!self._cacheable || self.effect.dirty) { const newValue = self.effect.run()! - if (hasChanged(self._value, newValue)) { + const changed = hasChanged(self._value, newValue) + self._value = newValue + if (changed) { triggerRefValue(self, DirtyLevels.ComputedValueDirty) } - self._value = newValue self._scheduled = false } return self._value diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 40bbc14b7cf..672cca55aea 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -457,6 +457,7 @@ export function triggerEffects( dirtyLevel: DirtyLevels, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { + pauseScheduling() // spread into array for stabilization const effects = [...dep] for (const effect of effects) { @@ -469,6 +470,8 @@ export function triggerEffects( triggerEffect(effect, dirtyLevel, debuggerEventExtraInfo) } } + resetScheduling() + scheduleEffectCallbacks() } const queueEffectCbs: (() => void)[] = [] @@ -494,11 +497,10 @@ function triggerEffect( effect.scheduler(pushEffectCb) } } - scheduleEffectCallbacks() } function scheduleEffectCallbacks() { - if (effectTrackDepth === 0 && shouldSchedule) { + if (shouldSchedule) { while (queueEffectCbs.length) { queueEffectCbs.shift()!() } diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts index 4b57c4b355d..e508b26b44f 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -351,7 +351,7 @@ function doWatch( let scheduler: EffectScheduler if (flush === 'sync') { - scheduler = job as any // the scheduler function gets called directly + scheduler = onScheduled => onScheduled(job as any) // the scheduler function gets called directly } else if (flush === 'post') { scheduler = () => queuePostRenderEffect(job, instance && instance.suspense) } else { From 1591ce183f5ca92e401aa64a0a66524e4e918b21 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 7 Sep 2023 17:58:26 +0800 Subject: [PATCH 106/110] update test result --- packages/reactivity/__tests__/computed.spec.ts | 2 +- packages/reactivity/__tests__/effect.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index 6ee6f913379..abceca3eb84 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -184,7 +184,7 @@ describe('reactivity/computed', () => { // mutate n n.value++ // on the 2nd run, plusOne.value should have already updated. - expect(plusOneValues).toMatchObject([1, 2, 2]) + expect(plusOneValues).toMatchObject([1, 2]) }) it('should warn if trying to set a readonly computed', () => { diff --git a/packages/reactivity/__tests__/effect.spec.ts b/packages/reactivity/__tests__/effect.spec.ts index 69d24a76520..f056b114846 100644 --- a/packages/reactivity/__tests__/effect.spec.ts +++ b/packages/reactivity/__tests__/effect.spec.ts @@ -559,7 +559,7 @@ describe('reactivity/effect', () => { expect(fx1Spy).toHaveBeenCalledTimes(1) // Invoked twice due to change of fx1. - expect(fx2Spy).toHaveBeenCalledTimes(2) + expect(fx2Spy).toHaveBeenCalledTimes(1) fx1Spy.mockClear() fx2Spy.mockClear() From eec0a786da0cbb4191e3896a7b1b22fdf274d38a Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 7 Sep 2023 18:11:55 +0800 Subject: [PATCH 107/110] sync #9056 --- packages/reactivity/src/computed.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index d61c341b7d5..7509d397d83 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -59,10 +59,11 @@ export class ComputedRefImpl { trackRefValue(self, self) if (!self._cacheable || self.effect.dirty) { const newValue = self.effect.run()! - if (hasChanged(self._value, newValue)) { + const changed = hasChanged(self._value, newValue) + self._value = newValue + if (changed) { triggerRefValue(self, DirtyLevels.ComputedValueDirty) } - self._value = newValue self._scheduled = false } return self._value From 01e21d2e3d133c9fc5c41caba786af671a3e5ae2 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 7 Sep 2023 18:50:54 +0800 Subject: [PATCH 108/110] update test comment --- packages/reactivity/__tests__/effect.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reactivity/__tests__/effect.spec.ts b/packages/reactivity/__tests__/effect.spec.ts index f056b114846..e673e570313 100644 --- a/packages/reactivity/__tests__/effect.spec.ts +++ b/packages/reactivity/__tests__/effect.spec.ts @@ -558,7 +558,7 @@ describe('reactivity/effect', () => { expect(output.fx2).toBe(1 + 3 + 3) expect(fx1Spy).toHaveBeenCalledTimes(1) - // Invoked twice due to change of fx1. + // Invoked once even change of fx1. expect(fx2Spy).toHaveBeenCalledTimes(1) fx1Spy.mockClear() From 0bdacad806314189d55dcfb13b4aaeee2d280125 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 7 Sep 2023 18:59:41 +0800 Subject: [PATCH 109/110] chore: remove scheduleEffectCallbacks --- packages/reactivity/src/effect.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 672cca55aea..3dcc92e66f1 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -293,7 +293,11 @@ export function pauseScheduling() { export function resetScheduling() { const last = scheduleStack.pop() shouldSchedule = last === undefined ? true : last - scheduleEffectCallbacks() + if (shouldSchedule) { + while (queueEffectCbs.length) { + queueEffectCbs.shift()!() + } + } } /** @@ -471,7 +475,6 @@ export function triggerEffects( } } resetScheduling() - scheduleEffectCallbacks() } const queueEffectCbs: (() => void)[] = [] @@ -499,14 +502,6 @@ function triggerEffect( } } -function scheduleEffectCallbacks() { - if (shouldSchedule) { - while (queueEffectCbs.length) { - queueEffectCbs.shift()!() - } - } -} - export function getDepFromReactive(object: any, key: string | number | symbol) { return targetMap.get(object)?.get(key) } From 3b0c4a75181bbe3b6ba7bbec984021d84c97d5d6 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Fri, 8 Sep 2023 14:25:42 +0800 Subject: [PATCH 110/110] combine if & while --- packages/reactivity/src/effect.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 3dcc92e66f1..dbdfb506e37 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -293,10 +293,8 @@ export function pauseScheduling() { export function resetScheduling() { const last = scheduleStack.pop() shouldSchedule = last === undefined ? true : last - if (shouldSchedule) { - while (queueEffectCbs.length) { - queueEffectCbs.shift()!() - } + while (shouldSchedule && queueEffectCbs.length) { + queueEffectCbs.shift()!() } }