From 1a24410df651ba2920a956a482c955c15fa32b86 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Tue, 17 Dec 2024 18:13:59 +0800 Subject: [PATCH 01/30] feat(reactivity): sync alien-signals 0.4.5 --- .../reactivity/__tests__/computed.spec.ts | 8 +++-- packages/reactivity/src/computed.ts | 31 ++++++++++--------- packages/reactivity/src/effectScope.ts | 4 +-- packages/reactivity/src/ref.ts | 2 +- packages/reactivity/src/system.ts | 19 ++++++------ 5 files changed, 35 insertions(+), 29 deletions(-) diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index 1e807df17a0..941b7825096 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -467,8 +467,12 @@ describe('reactivity/computed', () => { const c2 = computed(() => c1.value) as unknown as ComputedRefImpl c2.value - expect(c1.flags & SubscriberFlags.Dirtys).toBe(0) - expect(c2.flags & SubscriberFlags.Dirtys).toBe(0) + expect( + c1.flags & (SubscriberFlags.Dirty | SubscriberFlags.ToCheckDirty), + ).toBe(0) + expect( + c2.flags & (SubscriberFlags.Dirty | SubscriberFlags.ToCheckDirty), + ).toBe(0) }) it('should chained computeds dirtyLevel update with first computed effect', () => { diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 15748c88eb2..b513c4ca79a 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -110,7 +110,7 @@ export class ComputedRefImpl implements IComputed { if (v) { this.flags |= SubscriberFlags.Dirty } else { - this.flags &= ~SubscriberFlags.Dirtys + this.flags &= ~(SubscriberFlags.Dirty | SubscriberFlags.ToCheckDirty) } } @@ -136,21 +136,22 @@ export class ComputedRefImpl implements IComputed { if (this._dirty) { this.update() } - if (activeTrackId !== 0 && this.lastTrackedId !== activeTrackId) { - if (__DEV__) { - onTrack(activeSub!, { - target: this, - type: TrackOpTypes.GET, - key: 'value', - }) + if (activeTrackId) { + if (this.lastTrackedId !== activeTrackId) { + if (__DEV__) { + onTrack(activeSub!, { + target: this, + type: TrackOpTypes.GET, + key: 'value', + }) + } + this.lastTrackedId = activeTrackId + link(this, activeSub!).version = this.version + } + } else if (activeEffectScope !== undefined) { + if (this.lastTrackedId !== activeEffectScope.trackId) { + link(this, activeEffectScope) } - this.lastTrackedId = activeTrackId - link(this, activeSub!).version = this.version - } else if ( - activeEffectScope !== undefined && - this.lastTrackedId !== activeEffectScope.trackId - ) { - link(this, activeEffectScope) } return this._value! } diff --git a/packages/reactivity/src/effectScope.ts b/packages/reactivity/src/effectScope.ts index b03cbc2800f..142e3319c18 100644 --- a/packages/reactivity/src/effectScope.ts +++ b/packages/reactivity/src/effectScope.ts @@ -93,12 +93,12 @@ export class EffectScope implements Subscriber { run(fn: () => T): T | undefined { if (this.active) { - const currentEffectScope = activeEffectScope + const prevEffectScope = activeEffectScope try { activeEffectScope = this return fn() } finally { - activeEffectScope = currentEffectScope + activeEffectScope = prevEffectScope } } else if (__DEV__) { warn(`cannot run an inactive effect scope.`) diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 1778ea7ea1e..20655b0c032 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -196,7 +196,7 @@ export function triggerRef(ref: Ref): void { } function trackRef(dep: Dependency) { - if (activeTrackId !== 0 && dep.lastTrackedId !== activeTrackId) { + if (activeTrackId && dep.lastTrackedId !== activeTrackId) { if (__DEV__) { onTrack(activeSub!, { target: dep, diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index f34562b4f96..66df6f2688f 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -1,4 +1,4 @@ -// Ported from https://github.com/stackblitz/alien-signals/blob/v0.4.4/src/system.ts +// Ported from https://github.com/stackblitz/alien-signals/blob/v0.4.5/src/system.ts export interface IEffect extends Subscriber { nextNotify: IEffect | undefined @@ -42,7 +42,6 @@ export enum SubscriberFlags { // 2~5 are using in EffectFlags ToCheckDirty = 1 << 6, Dirty = 1 << 7, - Dirtys = SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty, DirtyFlagsIndex = 6, } @@ -278,7 +277,7 @@ export function checkDirty(deps: Link): boolean { continue } } else { - sub.flags &= ~SubscriberFlags.Dirtys + sub.flags &= ~SubscriberFlags.ToCheckDirty } deps = prevLink.nextDep! if (deps !== undefined) { @@ -297,7 +296,12 @@ export function checkDirty(deps: Link): boolean { export function startTrack(sub: Subscriber): void { sub.depsTail = undefined sub.flags = - (sub.flags & ~(SubscriberFlags.CanPropagate | SubscriberFlags.Dirtys)) | + (sub.flags & + ~( + SubscriberFlags.CanPropagate | + SubscriberFlags.ToCheckDirty | + SubscriberFlags.Dirty + )) | SubscriberFlags.Tracking } @@ -347,11 +351,8 @@ function clearTrack(link: Link): void { linkPool = link if (dep.subs === undefined && 'deps' in dep) { - if ('notify' in dep) { - dep.flags &= ~SubscriberFlags.Dirtys - } else { - dep.flags |= SubscriberFlags.Dirty - } + // dep is never be IEffect in Vue + dep.flags |= SubscriberFlags.Dirty const depDeps = dep.deps if (depDeps !== undefined) { link = depDeps From 2b596f933d07a569b6db7a3d603db8e83ad76c17 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 25 Dec 2024 23:01:52 +0800 Subject: [PATCH 02/30] feat(reactivity): sync alien-signals 0.4.10 --- packages/reactivity/src/computed.ts | 27 +++-- packages/reactivity/src/debug.ts | 17 ++- packages/reactivity/src/effect.ts | 12 +- packages/reactivity/src/system.ts | 166 +++++++++++++++++----------- 4 files changed, 133 insertions(+), 89 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index b513c4ca79a..a09bad2673e 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -19,6 +19,7 @@ import { checkDirty, endTrack, link, + shallowPropagate, startTrack, } from './system' import { warn } from './warning' @@ -59,7 +60,6 @@ export class ComputedRefImpl implements IComputed { * @internal */ _value: T | undefined = undefined - version = 0 // Dependency subs: Link | undefined = undefined @@ -134,7 +134,12 @@ export class ComputedRefImpl implements IComputed { get value(): T { if (this._dirty) { - this.update() + if (this.update()) { + const subs = this.subs + if (subs !== undefined) { + shallowPropagate(subs) + } + } } if (activeTrackId) { if (this.lastTrackedId !== activeTrackId) { @@ -146,7 +151,7 @@ export class ComputedRefImpl implements IComputed { }) } this.lastTrackedId = activeTrackId - link(this, activeSub!).version = this.version + link(this, activeSub!) } } else if (activeEffectScope !== undefined) { if (this.lastTrackedId !== activeEffectScope.trackId) { @@ -169,20 +174,18 @@ export class ComputedRefImpl implements IComputed { const prevTrackId = activeTrackId setActiveSub(this, nextTrackId()) startTrack(this) - const oldValue = this._value - let newValue: T try { - newValue = this.fn(oldValue) + const oldValue = this._value + const newValue = this.fn(oldValue) + if (hasChanged(oldValue, newValue)) { + this._value = newValue + return true + } + return false } finally { setActiveSub(prevSub, prevTrackId) endTrack(this) } - if (hasChanged(oldValue, newValue)) { - this._value = newValue - this.version++ - return true - } - return false } } diff --git a/packages/reactivity/src/debug.ts b/packages/reactivity/src/debug.ts index 14d22f0a1f2..69c771ec7e2 100644 --- a/packages/reactivity/src/debug.ts +++ b/packages/reactivity/src/debug.ts @@ -62,23 +62,22 @@ export function setupOnTrigger(target: { new (...args: any[]): any }): void { } function setupFlagsHandler(target: Subscriber): void { - // @ts-expect-error - target._flags = target.flags + ;(target as any)._flags = target.flags Object.defineProperty(target, 'flags', { get() { - // @ts-expect-error - return target._flags + return (target as any)._flags }, set(value) { if ( - // @ts-expect-error - !(target._flags >> SubscriberFlags.DirtyFlagsIndex) && - !!(value >> SubscriberFlags.DirtyFlagsIndex) + !( + (target as any)._flags & + (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty) + ) && + !!(value & (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty)) ) { onTrigger(this) } - // @ts-expect-error - target._flags = value + ;(target as any)._flags = value }, }) } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 690483caace..5f872c10e13 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -47,10 +47,10 @@ export enum EffectFlags { /** * ReactiveEffect only */ - ALLOW_RECURSE = 1 << 2, - PAUSED = 1 << 3, - NOTIFIED = 1 << 4, - STOP = 1 << 5, + ALLOW_RECURSE = 1 << 5, + PAUSED = 1 << 6, + NOTIFIED = 1 << 7, + STOP = 1 << 8, } export class ReactiveEffect implements IEffect, ReactiveEffectOptions { @@ -137,10 +137,10 @@ export class ReactiveEffect implements IEffect, ReactiveEffectOptions { setActiveSub(prevSub, prevTrackId) endTrack(this) if ( - this.flags & SubscriberFlags.CanPropagate && + this.flags & SubscriberFlags.Recursed && this.flags & EffectFlags.ALLOW_RECURSE ) { - this.flags &= ~SubscriberFlags.CanPropagate + this.flags &= ~SubscriberFlags.Recursed this.notify() } } diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index 66df6f2688f..426a622b4e6 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -1,4 +1,4 @@ -// Ported from https://github.com/stackblitz/alien-signals/blob/v0.4.5/src/system.ts +// Ported from https://github.com/stackblitz/alien-signals/blob/v0.4.10/src/system.ts export interface IEffect extends Subscriber { nextNotify: IEffect | undefined @@ -6,7 +6,6 @@ export interface IEffect extends Subscriber { } export interface IComputed extends Dependency, Subscriber { - version: number update(): boolean } @@ -25,7 +24,6 @@ export interface Subscriber { export interface Link { dep: Dependency | IComputed | (Dependency & IEffect) sub: Subscriber | IComputed | (Dependency & IEffect) | IEffect - version: number // Reuse to link prev stack in checkDirty // Reuse to link prev stack in propagate prevSub: Link | undefined @@ -37,13 +35,9 @@ export interface Link { export enum SubscriberFlags { None = 0, Tracking = 1 << 0, - CanPropagate = 1 << 1, - // RunInnerEffects = 1 << 2, // Not used in Vue - // 2~5 are using in EffectFlags - ToCheckDirty = 1 << 6, - Dirty = 1 << 7, - - DirtyFlagsIndex = 6, + Recursed = 1 << 1, + ToCheckDirty = 1 << 3, + Dirty = 1 << 4, } let batchDepth = 0 @@ -82,9 +76,8 @@ export function link(dep: Dependency, sub: Subscriber): Link { if (nextDep !== undefined && nextDep.dep === dep) { sub.depsTail = nextDep return nextDep - } else { - return linkNewDep(dep, sub, nextDep, currentDep) } + return linkNewDep(dep, sub, nextDep, currentDep) } function linkNewDep( @@ -105,7 +98,6 @@ function linkNewDep( newLink = { dep, sub, - version: 0, nextDep, prevSub: undefined, nextSub: undefined, @@ -132,6 +124,7 @@ function linkNewDep( return newLink } +// See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions export function propagate(subs: Link): void { let targetFlag = SubscriberFlags.Dirty let link = subs @@ -143,22 +136,24 @@ export function propagate(subs: Link): void { const subFlags = sub.flags if (!(subFlags & SubscriberFlags.Tracking)) { - let canPropagate = !(subFlags >> SubscriberFlags.DirtyFlagsIndex) - if (!canPropagate && subFlags & SubscriberFlags.CanPropagate) { - sub.flags &= ~SubscriberFlags.CanPropagate - canPropagate = true - } - if (canPropagate) { - sub.flags |= targetFlag + if ( + (!(subFlags & (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty)) && + ((sub.flags = subFlags | targetFlag), true)) || + (subFlags & SubscriberFlags.Recursed && + ((sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag), + true)) + ) { const subSubs = (sub as Dependency).subs if (subSubs !== undefined) { if (subSubs.nextSub !== undefined) { subSubs.prevSub = subs - subs = subSubs + link = subs = subSubs + targetFlag = SubscriberFlags.ToCheckDirty ++stack + } else { + link = subSubs + targetFlag = SubscriberFlags.ToCheckDirty } - link = subSubs - targetFlag = SubscriberFlags.ToCheckDirty continue } if ('notify' in sub) { @@ -169,25 +164,29 @@ export function propagate(subs: Link): void { } queuedEffectsTail = sub } - } else if (!(sub.flags & targetFlag)) { - sub.flags |= targetFlag + } else if (!(subFlags & targetFlag)) { + sub.flags = subFlags | targetFlag } } else if (isValidLink(link, sub)) { - if (!(subFlags >> SubscriberFlags.DirtyFlagsIndex)) { - sub.flags |= targetFlag | SubscriberFlags.CanPropagate + if ( + !(subFlags & (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty)) + ) { + sub.flags = subFlags | targetFlag | SubscriberFlags.Recursed const subSubs = (sub as Dependency).subs if (subSubs !== undefined) { if (subSubs.nextSub !== undefined) { subSubs.prevSub = subs - subs = subSubs + link = subs = subSubs + targetFlag = SubscriberFlags.ToCheckDirty ++stack + } else { + link = subSubs + targetFlag = SubscriberFlags.ToCheckDirty } - link = subSubs - targetFlag = SubscriberFlags.ToCheckDirty continue } - } else if (!(sub.flags & targetFlag)) { - sub.flags |= targetFlag + } else if (!(subFlags & targetFlag)) { + sub.flags = subFlags | targetFlag } } @@ -222,7 +221,22 @@ export function propagate(subs: Link): void { } } -function isValidLink(subLink: Link, sub: Subscriber) { +export function shallowPropagate(link: Link): void { + do { + const updateSub = link.sub + const updateSubFlags = updateSub.flags + if ( + (updateSubFlags & + (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty)) === + SubscriberFlags.ToCheckDirty + ) { + updateSub.flags = updateSubFlags | SubscriberFlags.Dirty + } + link = link.nextSub! + } while (link !== undefined) +} + +function isValidLink(subLink: Link, sub: Subscriber): boolean { const depsTail = sub.depsTail if (depsTail !== undefined) { let link = sub.deps! @@ -239,48 +253,70 @@ function isValidLink(subLink: Link, sub: Subscriber) { return false } -export function checkDirty(deps: Link): boolean { +// See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions +export function checkDirty(link: Link): boolean { let stack = 0 let dirty: boolean let nextDep: Link | undefined top: do { dirty = false - const dep = deps.dep + const dep = link.dep if ('update' in dep) { - if (dep.version !== deps.version) { - dirty = true - } else { - const depFlags = dep.flags - if (depFlags & SubscriberFlags.Dirty) { - dirty = dep.update() - } else if (depFlags & SubscriberFlags.ToCheckDirty) { - dep.subs!.prevSub = deps - deps = dep.deps! - ++stack - continue + const depFlags = dep.flags + if (depFlags & SubscriberFlags.Dirty) { + if (dep.update()) { + const subs = dep.subs! + if (subs.nextSub !== undefined) { + shallowPropagate(subs) + } + dirty = true + } + } else if (depFlags & SubscriberFlags.ToCheckDirty) { + const depSubs = dep.subs! + if (depSubs.nextSub !== undefined) { + depSubs.prevSub = link } + link = dep.deps! + ++stack + continue } } - if (dirty || (nextDep = deps.nextDep) === undefined) { + if (dirty || (nextDep = link.nextDep) === undefined) { if (stack) { - let sub = deps.sub as IComputed + let sub = link.sub as IComputed do { --stack const subSubs = sub.subs! - const prevLink = subSubs.prevSub! - subSubs.prevSub = undefined - if (dirty) { - if (sub.update()) { - sub = prevLink.sub as IComputed - dirty = true - continue + let prevLink = subSubs.prevSub! + if (prevLink !== undefined) { + subSubs.prevSub = undefined + if (dirty) { + if (sub.update()) { + shallowPropagate(sub.subs!) + sub = prevLink.sub as IComputed + sub = prevLink.sub as IComputed + dirty = true + sub = prevLink.sub as IComputed + dirty = true + continue + } + } else { + sub.flags &= ~SubscriberFlags.ToCheckDirty } } else { - sub.flags &= ~SubscriberFlags.ToCheckDirty + if (dirty) { + if (sub.update()) { + sub = subSubs.sub as IComputed + continue + } + } else { + sub.flags &= ~SubscriberFlags.ToCheckDirty + } + prevLink = subSubs } - deps = prevLink.nextDep! - if (deps !== undefined) { + link = prevLink.nextDep! + if (link !== undefined) { continue top } sub = prevLink.sub as IComputed @@ -289,7 +325,7 @@ export function checkDirty(deps: Link): boolean { } return dirty } - deps = nextDep + link = nextDep } while (true) } @@ -298,7 +334,7 @@ export function startTrack(sub: Subscriber): void { sub.flags = (sub.flags & ~( - SubscriberFlags.CanPropagate | + SubscriberFlags.Recursed | SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty )) | @@ -351,8 +387,14 @@ function clearTrack(link: Link): void { linkPool = link if (dep.subs === undefined && 'deps' in dep) { - // dep is never be IEffect in Vue - dep.flags |= SubscriberFlags.Dirty + if ('notify' in dep) { + dep.flags = SubscriberFlags.None + } else { + const depFlags = dep.flags + if (!(depFlags & SubscriberFlags.Dirty)) { + dep.flags = depFlags | SubscriberFlags.Dirty + } + } const depDeps = dep.deps if (depDeps !== undefined) { link = depDeps From cfb16a04749dee2b4703a3856b0e534c07092627 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 26 Dec 2024 07:20:35 +0800 Subject: [PATCH 03/30] refactor(reactivity): simplify flag checks in propagate function --- packages/reactivity/src/system.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index 426a622b4e6..e7999f8f1e5 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -137,12 +137,10 @@ export function propagate(subs: Link): void { if (!(subFlags & SubscriberFlags.Tracking)) { if ( - (!(subFlags & (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty)) && - ((sub.flags = subFlags | targetFlag), true)) || - (subFlags & SubscriberFlags.Recursed && - ((sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag), - true)) + !(subFlags & (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty)) || + subFlags & SubscriberFlags.Recursed ) { + sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag const subSubs = (sub as Dependency).subs if (subSubs !== undefined) { if (subSubs.nextSub !== undefined) { From 20c4f6e6a25b26c376d2212da25faf47a9c1f8c7 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 2 Jan 2025 18:34:11 +0800 Subject: [PATCH 04/30] Update system.ts --- packages/reactivity/src/system.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index e7999f8f1e5..6788d2f3814 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -1,4 +1,4 @@ -// Ported from https://github.com/stackblitz/alien-signals/blob/v0.4.10/src/system.ts +// Ported from https://github.com/stackblitz/alien-signals/blob/v0.4.12/src/system.ts export interface IEffect extends Subscriber { nextNotify: IEffect | undefined @@ -342,8 +342,9 @@ export function startTrack(sub: Subscriber): void { export function endTrack(sub: Subscriber): void { const depsTail = sub.depsTail if (depsTail !== undefined) { - if (depsTail.nextDep !== undefined) { - clearTrack(depsTail.nextDep) + const nextDep = depsTail.nextDep + if (nextDep !== undefined) { + clearTrack(nextDep) depsTail.nextDep = undefined } } else if (sub.deps !== undefined) { From 0a5ac69ca2f8d2076cd2689b1c20d4aaf0e194ca Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 2 Jan 2025 18:38:04 +0800 Subject: [PATCH 05/30] Update system.ts --- packages/reactivity/src/system.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index 6788d2f3814..a62a70712ec 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -70,14 +70,14 @@ function drainQueuedEffects(): void { } } -export function link(dep: Dependency, sub: Subscriber): Link { +export function link(dep: Dependency, sub: Subscriber): void { const currentDep = sub.depsTail const nextDep = currentDep !== undefined ? currentDep.nextDep : sub.deps if (nextDep !== undefined && nextDep.dep === dep) { sub.depsTail = nextDep - return nextDep + } else { + linkNewDep(dep, sub, nextDep, currentDep) } - return linkNewDep(dep, sub, nextDep, currentDep) } function linkNewDep( @@ -85,7 +85,7 @@ function linkNewDep( sub: Subscriber, nextDep: Link | undefined, depsTail: Link | undefined, -): Link { +): void { let newLink: Link if (linkPool !== undefined) { @@ -120,8 +120,6 @@ function linkNewDep( sub.depsTail = newLink dep.subsTail = newLink - - return newLink } // See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions From da1a003f91b68667f6cc6abb84fe8d5e638877be Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 2 Jan 2025 20:28:34 +0800 Subject: [PATCH 06/30] Update system.ts --- packages/reactivity/src/system.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index a62a70712ec..98201bcc661 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -291,9 +291,6 @@ export function checkDirty(link: Link): boolean { if (sub.update()) { shallowPropagate(sub.subs!) sub = prevLink.sub as IComputed - sub = prevLink.sub as IComputed - dirty = true - sub = prevLink.sub as IComputed dirty = true continue } From b8b6912ea34f8858bb386ede4394cb9c51d1e275 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 2 Jan 2025 20:29:54 +0800 Subject: [PATCH 07/30] Update system.ts --- packages/reactivity/src/system.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index 98201bcc661..ba9e843c1bd 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -291,7 +291,6 @@ export function checkDirty(link: Link): boolean { if (sub.update()) { shallowPropagate(sub.subs!) sub = prevLink.sub as IComputed - dirty = true continue } } else { From d69e94221cd87cdd593c64c2cdb7dd6dd3d857cf Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 2 Jan 2025 21:10:36 +0800 Subject: [PATCH 08/30] Update system.ts --- packages/reactivity/src/system.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index ba9e843c1bd..f8f46b254ee 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -143,13 +143,11 @@ export function propagate(subs: Link): void { if (subSubs !== undefined) { if (subSubs.nextSub !== undefined) { subSubs.prevSub = subs - link = subs = subSubs - targetFlag = SubscriberFlags.ToCheckDirty + subs = subSubs ++stack - } else { - link = subSubs - targetFlag = SubscriberFlags.ToCheckDirty } + link = subSubs + targetFlag = SubscriberFlags.ToCheckDirty continue } if ('notify' in sub) { @@ -172,13 +170,11 @@ export function propagate(subs: Link): void { if (subSubs !== undefined) { if (subSubs.nextSub !== undefined) { subSubs.prevSub = subs - link = subs = subSubs - targetFlag = SubscriberFlags.ToCheckDirty + subs = subSubs ++stack - } else { - link = subSubs - targetFlag = SubscriberFlags.ToCheckDirty } + link = subSubs + targetFlag = SubscriberFlags.ToCheckDirty continue } } else if (!(subFlags & targetFlag)) { From ce6a2ef99e09d7fd3a15154b528a631e518913fb Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 8 Jan 2025 00:28:15 +0800 Subject: [PATCH 09/30] Sync https://github.com/stackblitz/alien-signals/pull/29 --- packages/reactivity/src/system.ts | 84 +++++++++++++++---------------- 1 file changed, 40 insertions(+), 44 deletions(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index f8f46b254ee..7c801f0e6ac 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -1,4 +1,4 @@ -// Ported from https://github.com/stackblitz/alien-signals/blob/v0.4.12/src/system.ts +// Ported from https://github.com/stackblitz/alien-signals/blob/d605271723ca9529c9a7efc0d8ce70e50d0cc96b/src/system.ts export interface IEffect extends Subscriber { nextNotify: IEffect | undefined @@ -133,53 +133,49 @@ export function propagate(subs: Link): void { const sub = link.sub const subFlags = sub.flags - if (!(subFlags & SubscriberFlags.Tracking)) { - if ( - !(subFlags & (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty)) || - subFlags & SubscriberFlags.Recursed - ) { - sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag - const subSubs = (sub as Dependency).subs - if (subSubs !== undefined) { - if (subSubs.nextSub !== undefined) { - subSubs.prevSub = subs - subs = subSubs - ++stack - } - link = subSubs - targetFlag = SubscriberFlags.ToCheckDirty - continue - } - if ('notify' in sub) { - if (queuedEffectsTail !== undefined) { - queuedEffectsTail.nextNotify = sub - } else { - queuedEffects = sub - } - queuedEffectsTail = sub + if ( + (!( + subFlags & + (SubscriberFlags.Tracking | + SubscriberFlags.ToCheckDirty | + SubscriberFlags.Dirty) + ) && + ((sub.flags = subFlags | targetFlag), true)) || + ((subFlags & (SubscriberFlags.Tracking | SubscriberFlags.Recursed)) === + SubscriberFlags.Recursed && + ((sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag), + true)) || + (!(subFlags & (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty)) && + isValidLink(link, sub) && + ((sub.flags = subFlags | SubscriberFlags.Recursed | targetFlag), + (sub as Dependency).subs !== undefined)) + ) { + const subSubs = (sub as Dependency).subs + if (subSubs !== undefined) { + if (subSubs.nextSub !== undefined) { + subSubs.prevSub = subs + subs = subSubs + ++stack } - } else if (!(subFlags & targetFlag)) { - sub.flags = subFlags | targetFlag + link = subSubs + targetFlag = SubscriberFlags.ToCheckDirty + continue } - } else if (isValidLink(link, sub)) { - if ( - !(subFlags & (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty)) - ) { - sub.flags = subFlags | targetFlag | SubscriberFlags.Recursed - const subSubs = (sub as Dependency).subs - if (subSubs !== undefined) { - if (subSubs.nextSub !== undefined) { - subSubs.prevSub = subs - subs = subSubs - ++stack - } - link = subSubs - targetFlag = SubscriberFlags.ToCheckDirty - continue + if ('notify' in sub) { + if (queuedEffectsTail !== undefined) { + queuedEffectsTail.nextNotify = sub + } else { + queuedEffects = sub } - } else if (!(subFlags & targetFlag)) { - sub.flags = subFlags | targetFlag + queuedEffectsTail = sub } + } else if ( + !(subFlags & (SubscriberFlags.Tracking | targetFlag)) || + (!(subFlags & targetFlag) && + subFlags & (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty) && + isValidLink(link, sub)) + ) { + sub.flags = subFlags | targetFlag } if ((nextSub = subs.nextSub) === undefined) { From 0072ab1e7383c37b959a90f25939f40fd018164c Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 8 Jan 2025 05:12:18 +0800 Subject: [PATCH 10/30] Sync --- packages/reactivity/src/system.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index 7c801f0e6ac..17c069023ed 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -1,4 +1,4 @@ -// Ported from https://github.com/stackblitz/alien-signals/blob/d605271723ca9529c9a7efc0d8ce70e50d0cc96b/src/system.ts +// Ported from https://github.com/stackblitz/alien-signals/blob/e4a4ecec5f84395793ffb2707cda25b7b1a26a2f/src/system.ts export interface IEffect extends Subscriber { nextNotify: IEffect | undefined @@ -127,7 +127,6 @@ export function propagate(subs: Link): void { let targetFlag = SubscriberFlags.Dirty let link = subs let stack = 0 - let nextSub: Link | undefined top: do { const sub = link.sub @@ -178,7 +177,7 @@ export function propagate(subs: Link): void { sub.flags = subFlags | targetFlag } - if ((nextSub = subs.nextSub) === undefined) { + if ((link = subs.nextSub!) === undefined) { if (stack) { let dep = subs.dep do { @@ -186,8 +185,9 @@ export function propagate(subs: Link): void { const depSubs = dep.subs! const prevLink = depSubs.prevSub! depSubs.prevSub = undefined - link = subs = prevLink.nextSub! - if (subs !== undefined) { + link = prevLink.nextSub! + if (link !== undefined) { + subs = link targetFlag = stack ? SubscriberFlags.ToCheckDirty : SubscriberFlags.Dirty @@ -198,10 +198,8 @@ export function propagate(subs: Link): void { } break } - if (link !== subs) { - targetFlag = stack ? SubscriberFlags.ToCheckDirty : SubscriberFlags.Dirty - } - link = subs = nextSub + subs = link + targetFlag = stack ? SubscriberFlags.ToCheckDirty : SubscriberFlags.Dirty } while (true) if (!batchDepth) { From 8c3eadb0c9a4660e4e15f7c54995f195ca1ebe8c Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 8 Jan 2025 06:22:39 +0800 Subject: [PATCH 11/30] Update link --- packages/reactivity/src/system.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index 17c069023ed..3c8ddeaaf5b 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -1,4 +1,4 @@ -// Ported from https://github.com/stackblitz/alien-signals/blob/e4a4ecec5f84395793ffb2707cda25b7b1a26a2f/src/system.ts +// Ported from https://github.com/stackblitz/alien-signals/blob/v0.4.13/src/system.ts export interface IEffect extends Subscriber { nextNotify: IEffect | undefined From 6b0403b0351b42268e506437020c10892348b4f6 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 8 Jan 2025 20:17:55 +0800 Subject: [PATCH 12/30] Sync --- packages/reactivity/src/system.ts | 132 ++++++++++++++++-------------- 1 file changed, 70 insertions(+), 62 deletions(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index 3c8ddeaaf5b..eb30b02d66a 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -1,4 +1,4 @@ -// Ported from https://github.com/stackblitz/alien-signals/blob/v0.4.13/src/system.ts +// Ported from https://github.com/stackblitz/alien-signals/blob/8aa6ec512709527c4b3b8c17ff44b536e1371603/src/system.ts export interface IEffect extends Subscriber { nextNotify: IEffect | undefined @@ -123,9 +123,9 @@ function linkNewDep( } // See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions -export function propagate(subs: Link): void { +export function propagate(link: Link): void { let targetFlag = SubscriberFlags.Dirty - let link = subs + let subs = link let stack = 0 top: do { @@ -136,6 +136,7 @@ export function propagate(subs: Link): void { (!( subFlags & (SubscriberFlags.Tracking | + SubscriberFlags.Recursed | SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty) ) && @@ -153,11 +154,13 @@ export function propagate(subs: Link): void { if (subSubs !== undefined) { if (subSubs.nextSub !== undefined) { subSubs.prevSub = subs - subs = subSubs + link = subs = subSubs + targetFlag = SubscriberFlags.ToCheckDirty ++stack + } else { + link = subSubs + targetFlag = SubscriberFlags.ToCheckDirty } - link = subSubs - targetFlag = SubscriberFlags.ToCheckDirty continue } if ('notify' in sub) { @@ -177,29 +180,28 @@ export function propagate(subs: Link): void { sub.flags = subFlags | targetFlag } - if ((link = subs.nextSub!) === undefined) { - if (stack) { - let dep = subs.dep - do { - --stack - const depSubs = dep.subs! - const prevLink = depSubs.prevSub! - depSubs.prevSub = undefined - link = prevLink.nextSub! - if (link !== undefined) { - subs = link - targetFlag = stack - ? SubscriberFlags.ToCheckDirty - : SubscriberFlags.Dirty - continue top - } - dep = prevLink.dep - } while (stack) + if ((link = subs.nextSub!) !== undefined) { + subs = link + targetFlag = stack ? SubscriberFlags.ToCheckDirty : SubscriberFlags.Dirty + continue + } + + while (stack) { + --stack + const dep = subs.dep + const depSubs = dep.subs! + subs = depSubs.prevSub! + depSubs.prevSub = undefined + if ((link = subs.nextSub!) !== undefined) { + subs = link + targetFlag = stack + ? SubscriberFlags.ToCheckDirty + : SubscriberFlags.Dirty + continue top } - break } - subs = link - targetFlag = stack ? SubscriberFlags.ToCheckDirty : SubscriberFlags.Dirty + + break } while (true) if (!batchDepth) { @@ -243,11 +245,11 @@ function isValidLink(subLink: Link, sub: Subscriber): boolean { export function checkDirty(link: Link): boolean { let stack = 0 let dirty: boolean - let nextDep: Link | undefined top: do { dirty = false const dep = link.dep + if ('update' in dep) { const depFlags = dep.flags if (depFlags & SubscriberFlags.Dirty) { @@ -268,46 +270,52 @@ export function checkDirty(link: Link): boolean { continue } } - if (dirty || (nextDep = link.nextDep) === undefined) { - if (stack) { - let sub = link.sub as IComputed - do { - --stack - const subSubs = sub.subs! - let prevLink = subSubs.prevSub! - if (prevLink !== undefined) { - subSubs.prevSub = undefined - if (dirty) { - if (sub.update()) { - shallowPropagate(sub.subs!) - sub = prevLink.sub as IComputed - continue - } - } else { - sub.flags &= ~SubscriberFlags.ToCheckDirty - } - } else { - if (dirty) { - if (sub.update()) { - sub = subSubs.sub as IComputed - continue - } + + if (!dirty && link.nextDep !== undefined) { + link = link.nextDep + continue + } + + if (stack) { + let sub = link.sub as IComputed + do { + --stack + const subSubs = sub.subs! + + if (dirty) { + if (sub.update()) { + if ((link = subSubs.prevSub!) !== undefined) { + subSubs.prevSub = undefined + shallowPropagate(sub.subs!) + sub = link.sub as IComputed } else { - sub.flags &= ~SubscriberFlags.ToCheckDirty + sub = subSubs.sub as IComputed } - prevLink = subSubs + continue } - link = prevLink.nextDep! - if (link !== undefined) { + } else { + sub.flags &= ~SubscriberFlags.ToCheckDirty + } + + if ((link = subSubs.prevSub!) !== undefined) { + subSubs.prevSub = undefined + if (link.nextDep !== undefined) { + link = link.nextDep continue top } - sub = prevLink.sub as IComputed - dirty = false - } while (stack) - } - return dirty + sub = link.sub as IComputed + } else { + if ((link = subSubs.nextDep!) !== undefined) { + continue top + } + sub = subSubs.sub as IComputed + } + + dirty = false + } while (stack) } - link = nextDep + + return dirty } while (true) } From eb2f8de047b8a18c7515d34a81b35b5527ccc39f Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 8 Jan 2025 20:53:27 +0800 Subject: [PATCH 13/30] Update link --- packages/reactivity/src/system.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index eb30b02d66a..6afbffe788d 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -1,4 +1,4 @@ -// Ported from https://github.com/stackblitz/alien-signals/blob/8aa6ec512709527c4b3b8c17ff44b536e1371603/src/system.ts +// Ported from https://github.com/stackblitz/alien-signals/blob/v0.4.14/src/system.ts export interface IEffect extends Subscriber { nextNotify: IEffect | undefined From 6dd0ec4fe994d0e2a84b9fdca4a7a53a3964529e Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Fri, 10 Jan 2025 12:41:44 +0800 Subject: [PATCH 14/30] Sync --- packages/reactivity/src/system.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index 6afbffe788d..182ef97512b 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -141,8 +141,8 @@ export function propagate(link: Link): void { SubscriberFlags.Dirty) ) && ((sub.flags = subFlags | targetFlag), true)) || - ((subFlags & (SubscriberFlags.Tracking | SubscriberFlags.Recursed)) === - SubscriberFlags.Recursed && + (subFlags & SubscriberFlags.Recursed && + !(subFlags & SubscriberFlags.Tracking) && ((sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag), true)) || (!(subFlags & (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty)) && From f87d863dd5d391e6f7f056fc73e0f01b68886e95 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Fri, 10 Jan 2025 20:38:24 +0800 Subject: [PATCH 15/30] Sync https://github.com/stackblitz/alien-signals/pull/33 --- packages/reactivity/src/computed.ts | 47 +++++------------- packages/reactivity/src/dep.ts | 24 ++++------ packages/reactivity/src/effect.ts | 48 +++++-------------- packages/reactivity/src/effectScope.ts | 4 +- packages/reactivity/src/ref.ts | 7 +-- packages/reactivity/src/system.ts | 66 ++++++++++++++++---------- 6 files changed, 80 insertions(+), 116 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index a09bad2673e..9620f4a7481 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -5,8 +5,6 @@ import { type DebuggerEvent, type DebuggerOptions, activeSub, - activeTrackId, - nextTrackId, setActiveSub, } from './effect' import { activeEffectScope } from './effectScope' @@ -16,8 +14,8 @@ import { type IComputed, type Link, SubscriberFlags, - checkDirty, endTrack, + isDirty, link, shallowPropagate, startTrack, @@ -64,7 +62,6 @@ export class ComputedRefImpl implements IComputed { // Dependency subs: Link | undefined = undefined subsTail: Link | undefined = undefined - lastTrackedId = 0 // Subscriber deps: Link | undefined = undefined @@ -92,19 +89,7 @@ export class ComputedRefImpl implements IComputed { } // for backwards compat get _dirty(): boolean { - const flags = this.flags - if (flags & SubscriberFlags.Dirty) { - return true - } else if (flags & SubscriberFlags.ToCheckDirty) { - if (checkDirty(this.deps!)) { - this.flags |= SubscriberFlags.Dirty - return true - } else { - this.flags &= ~SubscriberFlags.ToCheckDirty - return false - } - } - return false + return isDirty(this, this.flags) } set _dirty(v: boolean) { if (v) { @@ -141,22 +126,17 @@ export class ComputedRefImpl implements IComputed { } } } - if (activeTrackId) { - if (this.lastTrackedId !== activeTrackId) { - if (__DEV__) { - onTrack(activeSub!, { - target: this, - type: TrackOpTypes.GET, - key: 'value', - }) - } - this.lastTrackedId = activeTrackId - link(this, activeSub!) + if (activeSub !== undefined) { + if (__DEV__) { + onTrack(activeSub!, { + target: this, + type: TrackOpTypes.GET, + key: 'value', + }) } + link(this, activeSub) } else if (activeEffectScope !== undefined) { - if (this.lastTrackedId !== activeEffectScope.trackId) { - link(this, activeEffectScope) - } + link(this, activeEffectScope) } return this._value! } @@ -171,8 +151,7 @@ export class ComputedRefImpl implements IComputed { update(): boolean { const prevSub = activeSub - const prevTrackId = activeTrackId - setActiveSub(this, nextTrackId()) + setActiveSub(this) startTrack(this) try { const oldValue = this._value @@ -183,7 +162,7 @@ export class ComputedRefImpl implements IComputed { } return false } finally { - setActiveSub(prevSub, prevTrackId) + setActiveSub(prevSub) endTrack(this) } } diff --git a/packages/reactivity/src/dep.ts b/packages/reactivity/src/dep.ts index 5c9b84739d4..c3e8e487874 100644 --- a/packages/reactivity/src/dep.ts +++ b/packages/reactivity/src/dep.ts @@ -1,11 +1,11 @@ import { isArray, isIntegerKey, isMap, isSymbol } from '@vue/shared' import { type TrackOpTypes, TriggerOpTypes } from './constants' import { onTrack, triggerEventInfos } from './debug' -import { activeSub, activeTrackId } from './effect' +import { activeSub } from './effect' import { type Dependency, - type Link, endBatch, + type Link, link, propagate, startBatch, @@ -14,7 +14,6 @@ import { class Dep implements Dependency { _subs: Link | undefined = undefined subsTail: Link | undefined = undefined - lastTrackedId = 0 constructor( private map: KeyToDepMap, @@ -62,7 +61,7 @@ export const ARRAY_ITERATE_KEY: unique symbol = Symbol( * @param key - Identifier of the reactive property to track. */ export function track(target: object, type: TrackOpTypes, key: unknown): void { - if (activeTrackId > 0) { + if (activeSub !== undefined) { let depsMap = targetMap.get(target) if (!depsMap) { targetMap.set(target, (depsMap = new Map())) @@ -71,17 +70,14 @@ export function track(target: object, type: TrackOpTypes, key: unknown): void { if (!dep) { depsMap.set(key, (dep = new Dep(depsMap, key))) } - if (dep.lastTrackedId !== activeTrackId) { - if (__DEV__) { - onTrack(activeSub!, { - target, - type, - key, - }) - } - dep.lastTrackedId = activeTrackId - link(dep, activeSub!) + if (__DEV__) { + onTrack(activeSub!, { + target, + type, + key, + }) } + link(dep, activeSub!) } } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 5f872c10e13..2b802290dbc 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -7,8 +7,8 @@ import { type Link, type Subscriber, SubscriberFlags, - checkDirty, endTrack, + isDirty, startTrack, } from './system' import { warn } from './warning' @@ -121,8 +121,7 @@ export class ReactiveEffect implements IEffect, ReactiveEffectOptions { } cleanupEffect(this) const prevSub = activeSub - const prevTrackId = activeTrackId - setActiveSub(this, nextTrackId()) + setActiveSub(this) startTrack(this) try { @@ -134,7 +133,7 @@ export class ReactiveEffect implements IEffect, ReactiveEffectOptions { 'this is likely a Vue internal bug.', ) } - setActiveSub(prevSub, prevTrackId) + setActiveSub(prevSub) endTrack(this) if ( this.flags & SubscriberFlags.Recursed && @@ -157,19 +156,7 @@ export class ReactiveEffect implements IEffect, ReactiveEffectOptions { } get dirty(): boolean { - const flags = this.flags - if (flags & SubscriberFlags.Dirty) { - return true - } else if (flags & SubscriberFlags.ToCheckDirty) { - if (checkDirty(this.deps!)) { - this.flags |= SubscriberFlags.Dirty - return true - } else { - this.flags &= ~SubscriberFlags.ToCheckDirty - return false - } - } - return false + return isDirty(this, this.flags) } } @@ -214,15 +201,14 @@ export function stop(runner: ReactiveEffectRunner): void { runner.effect.stop() } -const resetTrackingStack: [sub: typeof activeSub, trackId: number][] = [] +const resetTrackingStack: (Subscriber | undefined)[] = [] /** * Temporarily pauses tracking. */ export function pauseTracking(): void { - resetTrackingStack.push([activeSub, activeTrackId]) + resetTrackingStack.push(activeSub) activeSub = undefined - activeTrackId = 0 } /** @@ -233,14 +219,14 @@ export function enableTracking(): void { if (!isPaused) { // Add the current active effect to the trackResetStack so it can be // restored by calling resetTracking. - resetTrackingStack.push([activeSub, activeTrackId]) + resetTrackingStack.push(activeSub) } else { // Add a placeholder to the trackResetStack so we can it can be popped // to restore the previous active effect. - resetTrackingStack.push([undefined, 0]) + resetTrackingStack.push(undefined) for (let i = resetTrackingStack.length - 1; i >= 0; i--) { - if (resetTrackingStack[i][0] !== undefined) { - ;[activeSub, activeTrackId] = resetTrackingStack[i] + if (resetTrackingStack[i] !== undefined) { + activeSub = resetTrackingStack[i] break } } @@ -258,10 +244,9 @@ export function resetTracking(): void { ) } if (resetTrackingStack.length) { - ;[activeSub, activeTrackId] = resetTrackingStack.pop()! + activeSub = resetTrackingStack.pop()! } else { activeSub = undefined - activeTrackId = 0 } } @@ -304,14 +289,7 @@ function cleanupEffect(e: ReactiveEffect) { } export let activeSub: Subscriber | undefined = undefined -export let activeTrackId = 0 -export let lastTrackId = 0 -export const nextTrackId = (): number => ++lastTrackId - -export function setActiveSub( - sub: Subscriber | undefined, - trackId: number, -): void { + +export function setActiveSub(sub: Subscriber | undefined): void { activeSub = sub - activeTrackId = trackId } diff --git a/packages/reactivity/src/effectScope.ts b/packages/reactivity/src/effectScope.ts index 142e3319c18..de84be62d98 100644 --- a/packages/reactivity/src/effectScope.ts +++ b/packages/reactivity/src/effectScope.ts @@ -1,4 +1,4 @@ -import { EffectFlags, type ReactiveEffect, nextTrackId } from './effect' +import { EffectFlags, type ReactiveEffect } from './effect' import { type Link, type Subscriber, @@ -16,8 +16,6 @@ export class EffectScope implements Subscriber { depsTail: Link | undefined = undefined flags: number = SubscriberFlags.None - trackId: number = nextTrackId() - /** * @internal */ diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 20655b0c032..9ae365f5dba 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -9,7 +9,7 @@ import type { ComputedRef, WritableComputedRef } from './computed' import { ReactiveFlags, TrackOpTypes, TriggerOpTypes } from './constants' import { onTrack, triggerEventInfos } from './debug' import { getDepFromReactive } from './dep' -import { activeSub, activeTrackId } from './effect' +import { activeSub } from './effect' import { type Builtin, type ShallowReactiveMarker, @@ -112,7 +112,6 @@ class RefImpl implements Dependency { // Dependency subs: Link | undefined = undefined subsTail: Link | undefined = undefined - lastTrackedId = 0 _value: T private _rawValue: T @@ -196,7 +195,7 @@ export function triggerRef(ref: Ref): void { } function trackRef(dep: Dependency) { - if (activeTrackId && dep.lastTrackedId !== activeTrackId) { + if (activeSub !== undefined) { if (__DEV__) { onTrack(activeSub!, { target: dep, @@ -204,7 +203,6 @@ function trackRef(dep: Dependency) { key: 'value', }) } - dep.lastTrackedId = activeTrackId link(dep, activeSub!) } } @@ -301,7 +299,6 @@ class CustomRefImpl implements Dependency { // Dependency subs: Link | undefined = undefined subsTail: Link | undefined = undefined - lastTrackedId = 0 private readonly _get: ReturnType>['get'] private readonly _set: ReturnType>['set'] diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index 182ef97512b..69599658618 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -1,4 +1,4 @@ -// Ported from https://github.com/stackblitz/alien-signals/blob/v0.4.14/src/system.ts +// Ported from https://github.com/stackblitz/alien-signals/blob/81b07f8313bc69662a5766543e84c6dd77238b6b/src/system.ts export interface IEffect extends Subscriber { nextNotify: IEffect | undefined @@ -12,7 +12,6 @@ export interface IComputed extends Dependency, Subscriber { export interface Dependency { subs: Link | undefined subsTail: Link | undefined - lastTrackedId?: number } export interface Subscriber { @@ -36,8 +35,10 @@ export enum SubscriberFlags { None = 0, Tracking = 1 << 0, Recursed = 1 << 1, + InnerEffectsPending = 1 << 2, ToCheckDirty = 1 << 3, Dirty = 1 << 4, + Notified = InnerEffectsPending | ToCheckDirty | Dirty, } let batchDepth = 0 @@ -72,12 +73,23 @@ function drainQueuedEffects(): void { export function link(dep: Dependency, sub: Subscriber): void { const currentDep = sub.depsTail + if (currentDep !== undefined && currentDep.dep === dep) { + return + } const nextDep = currentDep !== undefined ? currentDep.nextDep : sub.deps if (nextDep !== undefined && nextDep.dep === dep) { sub.depsTail = nextDep - } else { - linkNewDep(dep, sub, nextDep, currentDep) + return } + const depLastSub = dep.subsTail + if ( + depLastSub !== undefined && + depLastSub.sub === sub && + isValidLink(depLastSub, sub) + ) { + return + } + linkNewDep(dep, sub, nextDep, currentDep) } function linkNewDep( @@ -137,15 +149,14 @@ export function propagate(link: Link): void { subFlags & (SubscriberFlags.Tracking | SubscriberFlags.Recursed | - SubscriberFlags.ToCheckDirty | - SubscriberFlags.Dirty) + SubscriberFlags.Notified) ) && ((sub.flags = subFlags | targetFlag), true)) || (subFlags & SubscriberFlags.Recursed && !(subFlags & SubscriberFlags.Tracking) && ((sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag), true)) || - (!(subFlags & (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty)) && + (!(subFlags & SubscriberFlags.Notified) && isValidLink(link, sub) && ((sub.flags = subFlags | SubscriberFlags.Recursed | targetFlag), (sub as Dependency).subs !== undefined)) @@ -159,7 +170,10 @@ export function propagate(link: Link): void { ++stack } else { link = subSubs - targetFlag = SubscriberFlags.ToCheckDirty + targetFlag = + 'notify' in sub + ? SubscriberFlags.InnerEffectsPending + : SubscriberFlags.ToCheckDirty } continue } @@ -174,7 +188,7 @@ export function propagate(link: Link): void { } else if ( !(subFlags & (SubscriberFlags.Tracking | targetFlag)) || (!(subFlags & targetFlag) && - subFlags & (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty) && + subFlags & SubscriberFlags.Notified && isValidLink(link, sub)) ) { sub.flags = subFlags | targetFlag @@ -322,12 +336,7 @@ export function checkDirty(link: Link): boolean { export function startTrack(sub: Subscriber): void { sub.depsTail = undefined sub.flags = - (sub.flags & - ~( - SubscriberFlags.Recursed | - SubscriberFlags.ToCheckDirty | - SubscriberFlags.Dirty - )) | + (sub.flags & ~(SubscriberFlags.Recursed | SubscriberFlags.Notified)) | SubscriberFlags.Tracking } @@ -358,9 +367,6 @@ function clearTrack(link: Link): void { link.nextSub = undefined } else { dep.subsTail = prevSub - if ('lastTrackedId' in dep) { - dep.lastTrackedId = 0 - } } if (prevSub !== undefined) { @@ -378,13 +384,9 @@ function clearTrack(link: Link): void { linkPool = link if (dep.subs === undefined && 'deps' in dep) { - if ('notify' in dep) { - dep.flags = SubscriberFlags.None - } else { - const depFlags = dep.flags - if (!(depFlags & SubscriberFlags.Dirty)) { - dep.flags = depFlags | SubscriberFlags.Dirty - } + const depFlags = dep.flags + if (!(depFlags & SubscriberFlags.Dirty)) { + dep.flags = depFlags | SubscriberFlags.Dirty } const depDeps = dep.deps if (depDeps !== undefined) { @@ -398,3 +400,17 @@ function clearTrack(link: Link): void { link = nextDep! } while (link !== undefined) } + +export function isDirty(sub: Subscriber, flags: SubscriberFlags): boolean { + if (flags & SubscriberFlags.Dirty) { + return true + } else if (flags & SubscriberFlags.ToCheckDirty) { + if (checkDirty(sub.deps!)) { + sub.flags = flags | SubscriberFlags.Dirty + return true + } else { + sub.flags = flags & ~SubscriberFlags.ToCheckDirty + } + } + return false +} From d119b501214399c35616cc93b59d4207db3bdee7 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:39:31 +0000 Subject: [PATCH 16/30] [autofix.ci] apply automated fixes --- packages/reactivity/src/dep.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reactivity/src/dep.ts b/packages/reactivity/src/dep.ts index c3e8e487874..184964c17b8 100644 --- a/packages/reactivity/src/dep.ts +++ b/packages/reactivity/src/dep.ts @@ -4,8 +4,8 @@ import { onTrack, triggerEventInfos } from './debug' import { activeSub } from './effect' import { type Dependency, - endBatch, type Link, + endBatch, link, propagate, startBatch, From ba00468b434b5cf524f5c4ad9779acd2de89d7c4 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Fri, 10 Jan 2025 20:41:45 +0800 Subject: [PATCH 17/30] Update system.ts --- packages/reactivity/src/system.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index 69599658618..43fd2c57cde 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -38,7 +38,9 @@ export enum SubscriberFlags { InnerEffectsPending = 1 << 2, ToCheckDirty = 1 << 3, Dirty = 1 << 4, - Notified = InnerEffectsPending | ToCheckDirty | Dirty, + Notified = SubscriberFlags.InnerEffectsPending | + SubscriberFlags.ToCheckDirty | + SubscriberFlags.Dirty, } let batchDepth = 0 From 928ab5122a8b61496a3f5a9fc1425e058d78da6b Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Fri, 10 Jan 2025 20:46:16 +0800 Subject: [PATCH 18/30] Remove SubscriberFlags.Notified --- packages/reactivity/src/system.ts | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index 43fd2c57cde..69a7a24bcbe 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -38,9 +38,6 @@ export enum SubscriberFlags { InnerEffectsPending = 1 << 2, ToCheckDirty = 1 << 3, Dirty = 1 << 4, - Notified = SubscriberFlags.InnerEffectsPending | - SubscriberFlags.ToCheckDirty | - SubscriberFlags.Dirty, } let batchDepth = 0 @@ -151,14 +148,21 @@ export function propagate(link: Link): void { subFlags & (SubscriberFlags.Tracking | SubscriberFlags.Recursed | - SubscriberFlags.Notified) + SubscriberFlags.InnerEffectsPending | + SubscriberFlags.ToCheckDirty | + SubscriberFlags.Dirty) ) && ((sub.flags = subFlags | targetFlag), true)) || (subFlags & SubscriberFlags.Recursed && !(subFlags & SubscriberFlags.Tracking) && ((sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag), true)) || - (!(subFlags & SubscriberFlags.Notified) && + (!( + subFlags & + (SubscriberFlags.InnerEffectsPending | + SubscriberFlags.ToCheckDirty | + SubscriberFlags.Dirty) + ) && isValidLink(link, sub) && ((sub.flags = subFlags | SubscriberFlags.Recursed | targetFlag), (sub as Dependency).subs !== undefined)) @@ -190,7 +194,10 @@ export function propagate(link: Link): void { } else if ( !(subFlags & (SubscriberFlags.Tracking | targetFlag)) || (!(subFlags & targetFlag) && - subFlags & SubscriberFlags.Notified && + subFlags & + (SubscriberFlags.InnerEffectsPending | + SubscriberFlags.ToCheckDirty | + SubscriberFlags.Dirty) && isValidLink(link, sub)) ) { sub.flags = subFlags | targetFlag @@ -338,7 +345,13 @@ export function checkDirty(link: Link): boolean { export function startTrack(sub: Subscriber): void { sub.depsTail = undefined sub.flags = - (sub.flags & ~(SubscriberFlags.Recursed | SubscriberFlags.Notified)) | + (sub.flags & + ~( + SubscriberFlags.Recursed | + SubscriberFlags.InnerEffectsPending | + SubscriberFlags.ToCheckDirty | + SubscriberFlags.Dirty + )) | SubscriberFlags.Tracking } From 78f4fdb7e6b6de34807c5bf164bdb2a0a8283992 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Fri, 10 Jan 2025 21:13:23 +0800 Subject: [PATCH 19/30] Sync v0.6.0 --- packages/reactivity/src/computed.ts | 16 +++---- packages/reactivity/src/debug.ts | 8 ++-- packages/reactivity/src/dep.ts | 10 ++-- packages/reactivity/src/effect.ts | 16 +++---- packages/reactivity/src/effectScope.ts | 10 ++-- packages/reactivity/src/ref.ts | 10 ++-- packages/reactivity/src/system.ts | 64 +++++++++++++------------- 7 files changed, 67 insertions(+), 67 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 9620f4a7481..b11fcb24294 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -10,9 +10,9 @@ import { import { activeEffectScope } from './effectScope' import type { Ref } from './ref' import { - type Dependency, type IComputed, - type Link, + type IDependency, + type ILink, SubscriberFlags, endTrack, isDirty, @@ -59,13 +59,13 @@ export class ComputedRefImpl implements IComputed { */ _value: T | undefined = undefined - // Dependency - subs: Link | undefined = undefined - subsTail: Link | undefined = undefined + // IDependency + subs: ILink | undefined = undefined + subsTail: ILink | undefined = undefined // Subscriber - deps: Link | undefined = undefined - depsTail: Link | undefined = undefined + deps: ILink | undefined = undefined + depsTail: ILink | undefined = undefined flags: SubscriberFlags = SubscriberFlags.Dirty /** @@ -84,7 +84,7 @@ export class ComputedRefImpl implements IComputed { return this } // for backwards compat - get dep(): Dependency { + get dep(): IDependency { return this } // for backwards compat diff --git a/packages/reactivity/src/debug.ts b/packages/reactivity/src/debug.ts index 69c771ec7e2..1b682983a29 100644 --- a/packages/reactivity/src/debug.ts +++ b/packages/reactivity/src/debug.ts @@ -1,11 +1,11 @@ import { extend } from '@vue/shared' import type { DebuggerEventExtraInfo, ReactiveEffectOptions } from './effect' -import { type Link, type Subscriber, SubscriberFlags } from './system' +import { type ILink, type ISubscriber, SubscriberFlags } from './system' export const triggerEventInfos: DebuggerEventExtraInfo[] = [] export function onTrack( - sub: Link['sub'], + sub: ILink['sub'], debugInfo: DebuggerEventExtraInfo, ): void { if (!__DEV__) { @@ -25,7 +25,7 @@ export function onTrack( } } -export function onTrigger(sub: Link['sub']): void { +export function onTrigger(sub: ILink['sub']): void { if (!__DEV__) { throw new Error( `Internal error: onTrigger should be called only in development.`, @@ -61,7 +61,7 @@ export function setupOnTrigger(target: { new (...args: any[]): any }): void { }) } -function setupFlagsHandler(target: Subscriber): void { +function setupFlagsHandler(target: ISubscriber): void { ;(target as any)._flags = target.flags Object.defineProperty(target, 'flags', { get() { diff --git a/packages/reactivity/src/dep.ts b/packages/reactivity/src/dep.ts index 184964c17b8..32d0641d5ce 100644 --- a/packages/reactivity/src/dep.ts +++ b/packages/reactivity/src/dep.ts @@ -4,7 +4,7 @@ import { onTrack, triggerEventInfos } from './debug' import { activeSub } from './effect' import { type Dependency, - type Link, + type ILink, endBatch, link, propagate, @@ -12,19 +12,19 @@ import { } from './system' class Dep implements Dependency { - _subs: Link | undefined = undefined - subsTail: Link | undefined = undefined + _subs: ILink | undefined = undefined + subsTail: ILink | undefined = undefined constructor( private map: KeyToDepMap, private key: unknown, ) {} - get subs(): Link | undefined { + get subs(): ILink | undefined { return this._subs } - set subs(value: Link | undefined) { + set subs(value: ILink | undefined) { this._subs = value if (value === undefined) { this.map.delete(this.key) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 2b802290dbc..a3bae9403a7 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -4,8 +4,8 @@ import { setupOnTrigger } from './debug' import { activeEffectScope } from './effectScope' import { type IEffect, - type Link, - type Subscriber, + type ILink, + type ISubscriber, SubscriberFlags, endTrack, isDirty, @@ -16,7 +16,7 @@ import { warn } from './warning' export type EffectScheduler = (...args: any[]) => any export type DebuggerEvent = { - effect: Subscriber + effect: ISubscriber } & DebuggerEventExtraInfo export type DebuggerEventExtraInfo = { @@ -57,8 +57,8 @@ export class ReactiveEffect implements IEffect, ReactiveEffectOptions { nextNotify: IEffect | undefined = undefined // Subscriber - deps: Link | undefined = undefined - depsTail: Link | undefined = undefined + deps: ILink | undefined = undefined + depsTail: ILink | undefined = undefined flags: number = SubscriberFlags.Dirty /** @@ -201,7 +201,7 @@ export function stop(runner: ReactiveEffectRunner): void { runner.effect.stop() } -const resetTrackingStack: (Subscriber | undefined)[] = [] +const resetTrackingStack: (ISubscriber | undefined)[] = [] /** * Temporarily pauses tracking. @@ -288,8 +288,8 @@ function cleanupEffect(e: ReactiveEffect) { } } -export let activeSub: Subscriber | undefined = undefined +export let activeSub: ISubscriber | undefined = undefined -export function setActiveSub(sub: Subscriber | undefined): void { +export function setActiveSub(sub: ISubscriber | undefined): void { activeSub = sub } diff --git a/packages/reactivity/src/effectScope.ts b/packages/reactivity/src/effectScope.ts index de84be62d98..2fdb15c0fbb 100644 --- a/packages/reactivity/src/effectScope.ts +++ b/packages/reactivity/src/effectScope.ts @@ -1,7 +1,7 @@ import { EffectFlags, type ReactiveEffect } from './effect' import { - type Link, - type Subscriber, + type ILink, + type ISubscriber, SubscriberFlags, endTrack, startTrack, @@ -10,10 +10,10 @@ import { warn } from './warning' export let activeEffectScope: EffectScope | undefined -export class EffectScope implements Subscriber { +export class EffectScope implements ISubscriber { // Subscriber: In order to collect orphans computeds - deps: Link | undefined = undefined - depsTail: Link | undefined = undefined + deps: ILink | undefined = undefined + depsTail: ILink | undefined = undefined flags: number = SubscriberFlags.None /** diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 9ae365f5dba..21a1d3fe04a 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -20,7 +20,7 @@ import { toRaw, toReactive, } from './reactive' -import { type Dependency, type Link, link, propagate } from './system' +import { type Dependency, type ILink, link, propagate } from './system' import { warn } from './warning' declare const RefSymbol: unique symbol @@ -110,8 +110,8 @@ function createRef(rawValue: unknown, shallow: boolean) { */ class RefImpl implements Dependency { // Dependency - subs: Link | undefined = undefined - subsTail: Link | undefined = undefined + subs: ILink | undefined = undefined + subsTail: ILink | undefined = undefined _value: T private _rawValue: T @@ -297,8 +297,8 @@ export type CustomRefFactory = ( class CustomRefImpl implements Dependency { // Dependency - subs: Link | undefined = undefined - subsTail: Link | undefined = undefined + subs: ILink | undefined = undefined + subsTail: ILink | undefined = undefined private readonly _get: ReturnType>['get'] private readonly _set: ReturnType>['set'] diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index 69a7a24bcbe..e8a31a6a20a 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -1,34 +1,34 @@ -// Ported from https://github.com/stackblitz/alien-signals/blob/81b07f8313bc69662a5766543e84c6dd77238b6b/src/system.ts +// Ported from https://github.com/stackblitz/alien-signals/blob/v0.6.0/src/system.ts -export interface IEffect extends Subscriber { +export interface IEffect extends ISubscriber { nextNotify: IEffect | undefined notify(): void } -export interface IComputed extends Dependency, Subscriber { +export interface IComputed extends IDependency, ISubscriber { update(): boolean } -export interface Dependency { - subs: Link | undefined - subsTail: Link | undefined +export interface IDependency { + subs: ILink | undefined + subsTail: ILink | undefined } -export interface Subscriber { +export interface ISubscriber { flags: SubscriberFlags - deps: Link | undefined - depsTail: Link | undefined + deps: ILink | undefined + depsTail: ILink | undefined } -export interface Link { - dep: Dependency | IComputed | (Dependency & IEffect) - sub: Subscriber | IComputed | (Dependency & IEffect) | IEffect +export interface ILink { + dep: IDependency | IComputed | (IDependency & IEffect) + sub: ISubscriber | IComputed | (IDependency & IEffect) | IEffect // Reuse to link prev stack in checkDirty // Reuse to link prev stack in propagate - prevSub: Link | undefined - nextSub: Link | undefined + prevSub: ILink | undefined + nextSub: ILink | undefined // Reuse to link next released link in linkPool - nextDep: Link | undefined + nextDep: ILink | undefined } export enum SubscriberFlags { @@ -43,7 +43,7 @@ export enum SubscriberFlags { let batchDepth = 0 let queuedEffects: IEffect | undefined let queuedEffectsTail: IEffect | undefined -let linkPool: Link | undefined +let linkPool: ILink | undefined export function startBatch(): void { ++batchDepth @@ -70,7 +70,7 @@ function drainQueuedEffects(): void { } } -export function link(dep: Dependency, sub: Subscriber): void { +export function link(dep: IDependency, sub: ISubscriber): void { const currentDep = sub.depsTail if (currentDep !== undefined && currentDep.dep === dep) { return @@ -92,12 +92,12 @@ export function link(dep: Dependency, sub: Subscriber): void { } function linkNewDep( - dep: Dependency, - sub: Subscriber, - nextDep: Link | undefined, - depsTail: Link | undefined, + dep: IDependency, + sub: ISubscriber, + nextDep: ILink | undefined, + depsTail: ILink | undefined, ): void { - let newLink: Link + let newLink: ILink if (linkPool !== undefined) { newLink = linkPool @@ -134,7 +134,7 @@ function linkNewDep( } // See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions -export function propagate(link: Link): void { +export function propagate(link: ILink): void { let targetFlag = SubscriberFlags.Dirty let subs = link let stack = 0 @@ -165,9 +165,9 @@ export function propagate(link: Link): void { ) && isValidLink(link, sub) && ((sub.flags = subFlags | SubscriberFlags.Recursed | targetFlag), - (sub as Dependency).subs !== undefined)) + (sub as IDependency).subs !== undefined)) ) { - const subSubs = (sub as Dependency).subs + const subSubs = (sub as IDependency).subs if (subSubs !== undefined) { if (subSubs.nextSub !== undefined) { subSubs.prevSub = subs @@ -232,7 +232,7 @@ export function propagate(link: Link): void { } } -export function shallowPropagate(link: Link): void { +export function shallowPropagate(link: ILink): void { do { const updateSub = link.sub const updateSubFlags = updateSub.flags @@ -247,7 +247,7 @@ export function shallowPropagate(link: Link): void { } while (link !== undefined) } -function isValidLink(subLink: Link, sub: Subscriber): boolean { +function isValidLink(subLink: ILink, sub: ISubscriber): boolean { const depsTail = sub.depsTail if (depsTail !== undefined) { let link = sub.deps! @@ -265,7 +265,7 @@ function isValidLink(subLink: Link, sub: Subscriber): boolean { } // See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions -export function checkDirty(link: Link): boolean { +export function checkDirty(link: ILink): boolean { let stack = 0 let dirty: boolean @@ -342,7 +342,7 @@ export function checkDirty(link: Link): boolean { } while (true) } -export function startTrack(sub: Subscriber): void { +export function startTrack(sub: ISubscriber): void { sub.depsTail = undefined sub.flags = (sub.flags & @@ -355,7 +355,7 @@ export function startTrack(sub: Subscriber): void { SubscriberFlags.Tracking } -export function endTrack(sub: Subscriber): void { +export function endTrack(sub: ISubscriber): void { const depsTail = sub.depsTail if (depsTail !== undefined) { const nextDep = depsTail.nextDep @@ -370,7 +370,7 @@ export function endTrack(sub: Subscriber): void { sub.flags &= ~SubscriberFlags.Tracking } -function clearTrack(link: Link): void { +function clearTrack(link: ILink): void { do { const dep = link.dep const nextDep = link.nextDep @@ -416,7 +416,7 @@ function clearTrack(link: Link): void { } while (link !== undefined) } -export function isDirty(sub: Subscriber, flags: SubscriberFlags): boolean { +export function isDirty(sub: ISubscriber, flags: SubscriberFlags): boolean { if (flags & SubscriberFlags.Dirty) { return true } else if (flags & SubscriberFlags.ToCheckDirty) { From 55ab1d5c9407782ce759c6dd1050b1c7679b4c94 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 11 Jan 2025 00:03:11 +0800 Subject: [PATCH 20/30] Fix types --- packages/reactivity/__tests__/effect.spec.ts | 4 ++-- packages/reactivity/src/dep.ts | 8 ++++---- packages/reactivity/src/ref.ts | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/reactivity/__tests__/effect.spec.ts b/packages/reactivity/__tests__/effect.spec.ts index 20f0244a7bc..0a3c36d00c4 100644 --- a/packages/reactivity/__tests__/effect.spec.ts +++ b/packages/reactivity/__tests__/effect.spec.ts @@ -22,7 +22,7 @@ import { stop, toRaw, } from '../src/index' -import { type Dependency, endBatch, startBatch } from '../src/system' +import { type IDependency, endBatch, startBatch } from '../src/system' describe('reactivity/effect', () => { it('should run the passed function once (wrapped by a effect)', () => { @@ -1178,7 +1178,7 @@ describe('reactivity/effect', () => { }) describe('dep unsubscribe', () => { - function getSubCount(dep: Dependency | undefined) { + function getSubCount(dep: IDependency | undefined) { let count = 0 let sub = dep!.subs while (sub) { diff --git a/packages/reactivity/src/dep.ts b/packages/reactivity/src/dep.ts index 32d0641d5ce..49b81d33dab 100644 --- a/packages/reactivity/src/dep.ts +++ b/packages/reactivity/src/dep.ts @@ -3,7 +3,7 @@ import { type TrackOpTypes, TriggerOpTypes } from './constants' import { onTrack, triggerEventInfos } from './debug' import { activeSub } from './effect' import { - type Dependency, + type IDependency, type ILink, endBatch, link, @@ -11,7 +11,7 @@ import { startBatch, } from './system' -class Dep implements Dependency { +class Dep implements IDependency { _subs: ILink | undefined = undefined subsTail: ILink | undefined = undefined @@ -103,7 +103,7 @@ export function trigger( return } - const run = (dep: Dependency | undefined) => { + const run = (dep: IDependency | undefined) => { if (dep !== undefined && dep.subs !== undefined) { if (__DEV__) { triggerEventInfos.push({ @@ -190,7 +190,7 @@ export function trigger( export function getDepFromReactive( object: any, key: string | number | symbol, -): Dependency | undefined { +): IDependency | undefined { const depMap = targetMap.get(object) return depMap && depMap.get(key) } diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 21a1d3fe04a..fd788dded90 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -20,7 +20,7 @@ import { toRaw, toReactive, } from './reactive' -import { type Dependency, type ILink, link, propagate } from './system' +import { type IDependency, type ILink, link, propagate } from './system' import { warn } from './warning' declare const RefSymbol: unique symbol @@ -108,7 +108,7 @@ function createRef(rawValue: unknown, shallow: boolean) { /** * @internal */ -class RefImpl implements Dependency { +class RefImpl implements IDependency { // Dependency subs: ILink | undefined = undefined subsTail: ILink | undefined = undefined @@ -194,7 +194,7 @@ export function triggerRef(ref: Ref): void { } } -function trackRef(dep: Dependency) { +function trackRef(dep: IDependency) { if (activeSub !== undefined) { if (__DEV__) { onTrack(activeSub!, { @@ -295,7 +295,7 @@ export type CustomRefFactory = ( set: (value: T) => void } -class CustomRefImpl implements Dependency { +class CustomRefImpl implements IDependency { // Dependency subs: ILink | undefined = undefined subsTail: ILink | undefined = undefined @@ -382,7 +382,7 @@ class ObjectRefImpl { this._object[this._key] = newVal } - get dep(): Dependency | undefined { + get dep(): IDependency | undefined { return getDepFromReactive(toRaw(this._object), this._key) } } From 11b7f4d437130853666c5e7050b0753a18b931f9 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 11 Jan 2025 11:23:43 +0800 Subject: [PATCH 21/30] Sync https://github.com/stackblitz/alien-signals/pull/34 --- packages/reactivity/src/effect.ts | 2 -- packages/reactivity/src/system.ts | 12 ++++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index a3bae9403a7..e09446364e8 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -54,8 +54,6 @@ export enum EffectFlags { } export class ReactiveEffect implements IEffect, ReactiveEffectOptions { - nextNotify: IEffect | undefined = undefined - // Subscriber deps: ILink | undefined = undefined depsTail: ILink | undefined = undefined diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index e8a31a6a20a..734bafd2bcf 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -1,7 +1,6 @@ -// Ported from https://github.com/stackblitz/alien-signals/blob/v0.6.0/src/system.ts +// Ported from https://github.com/stackblitz/alien-signals/blob/4551e66317a369f95dc2233e71987549e18bd695/src/system.ts export interface IEffect extends ISubscriber { - nextNotify: IEffect | undefined notify(): void } @@ -58,10 +57,11 @@ export function endBatch(): void { function drainQueuedEffects(): void { while (queuedEffects !== undefined) { const effect = queuedEffects - const queuedNext = effect.nextNotify + const depsTail = effect.depsTail! + const queuedNext = depsTail.nextDep if (queuedNext !== undefined) { - effect.nextNotify = undefined - queuedEffects = queuedNext + depsTail.nextDep = undefined + queuedEffects = queuedNext.sub as IEffect } else { queuedEffects = undefined queuedEffectsTail = undefined @@ -185,7 +185,7 @@ export function propagate(link: ILink): void { } if ('notify' in sub) { if (queuedEffectsTail !== undefined) { - queuedEffectsTail.nextNotify = sub + queuedEffectsTail.depsTail!.nextDep = sub.deps } else { queuedEffects = sub } From 853e825ff177496f52fd46fb46a3c37247a1f74e Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 13 Jan 2025 01:19:50 +0800 Subject: [PATCH 22/30] Sync https://github.com/stackblitz/alien-signals/pull/35 --- .../reactivity/__tests__/computed.spec.ts | 4 +- packages/reactivity/__tests__/effect.spec.ts | 4 +- packages/reactivity/src/computed.ts | 34 +- packages/reactivity/src/debug.ts | 12 +- packages/reactivity/src/dep.ts | 18 +- packages/reactivity/src/effect.ts | 29 +- packages/reactivity/src/effectScope.ts | 10 +- packages/reactivity/src/ref.ts | 18 +- packages/reactivity/src/system.ts | 835 ++++++++++-------- tsconfig.json | 4 +- 10 files changed, 541 insertions(+), 427 deletions(-) diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index 941b7825096..847520a3177 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -468,10 +468,10 @@ describe('reactivity/computed', () => { c2.value expect( - c1.flags & (SubscriberFlags.Dirty | SubscriberFlags.ToCheckDirty), + c1.flags & (SubscriberFlags.Dirty | SubscriberFlags.CheckRequired), ).toBe(0) expect( - c2.flags & (SubscriberFlags.Dirty | SubscriberFlags.ToCheckDirty), + c2.flags & (SubscriberFlags.Dirty | SubscriberFlags.CheckRequired), ).toBe(0) }) diff --git a/packages/reactivity/__tests__/effect.spec.ts b/packages/reactivity/__tests__/effect.spec.ts index 0a3c36d00c4..20f0244a7bc 100644 --- a/packages/reactivity/__tests__/effect.spec.ts +++ b/packages/reactivity/__tests__/effect.spec.ts @@ -22,7 +22,7 @@ import { stop, toRaw, } from '../src/index' -import { type IDependency, endBatch, startBatch } from '../src/system' +import { type Dependency, endBatch, startBatch } from '../src/system' describe('reactivity/effect', () => { it('should run the passed function once (wrapped by a effect)', () => { @@ -1178,7 +1178,7 @@ describe('reactivity/effect', () => { }) describe('dep unsubscribe', () => { - function getSubCount(dep: IDependency | undefined) { + function getSubCount(dep: Dependency | undefined) { let count = 0 let sub = dep!.subs while (sub) { diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index b11fcb24294..5950f021485 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -10,14 +10,14 @@ import { import { activeEffectScope } from './effectScope' import type { Ref } from './ref' import { - type IComputed, - type IDependency, - type ILink, + type Dependency, + type Link, + type Subscriber, SubscriberFlags, endTrack, isDirty, link, - shallowPropagate, + processComputedUpdate, startTrack, } from './system' import { warn } from './warning' @@ -53,19 +53,19 @@ export interface WritableComputedOptions { * @private exported by @vue/reactivity for Vue core use, but not exported from * the main vue package */ -export class ComputedRefImpl implements IComputed { +export class ComputedRefImpl implements Dependency, Subscriber { /** * @internal */ _value: T | undefined = undefined - // IDependency - subs: ILink | undefined = undefined - subsTail: ILink | undefined = undefined + // Dependency + subs: Link | undefined = undefined + subsTail: Link | undefined = undefined // Subscriber - deps: ILink | undefined = undefined - depsTail: ILink | undefined = undefined + deps: Link | undefined = undefined + depsTail: Link | undefined = undefined flags: SubscriberFlags = SubscriberFlags.Dirty /** @@ -84,7 +84,7 @@ export class ComputedRefImpl implements IComputed { return this } // for backwards compat - get dep(): IDependency { + get dep(): Dependency { return this } // for backwards compat @@ -95,7 +95,7 @@ export class ComputedRefImpl implements IComputed { if (v) { this.flags |= SubscriberFlags.Dirty } else { - this.flags &= ~(SubscriberFlags.Dirty | SubscriberFlags.ToCheckDirty) + this.flags &= ~(SubscriberFlags.Dirty | SubscriberFlags.CheckRequired) } } @@ -118,13 +118,9 @@ export class ComputedRefImpl implements IComputed { } get value(): T { - if (this._dirty) { - if (this.update()) { - const subs = this.subs - if (subs !== undefined) { - shallowPropagate(subs) - } - } + const flags = this.flags + if (flags) { + processComputedUpdate(this, flags) } if (activeSub !== undefined) { if (__DEV__) { diff --git a/packages/reactivity/src/debug.ts b/packages/reactivity/src/debug.ts index 1b682983a29..d6db3438019 100644 --- a/packages/reactivity/src/debug.ts +++ b/packages/reactivity/src/debug.ts @@ -1,11 +1,11 @@ import { extend } from '@vue/shared' import type { DebuggerEventExtraInfo, ReactiveEffectOptions } from './effect' -import { type ILink, type ISubscriber, SubscriberFlags } from './system' +import { type Link, type Subscriber, SubscriberFlags } from './system' export const triggerEventInfos: DebuggerEventExtraInfo[] = [] export function onTrack( - sub: ILink['sub'], + sub: Link['sub'], debugInfo: DebuggerEventExtraInfo, ): void { if (!__DEV__) { @@ -25,7 +25,7 @@ export function onTrack( } } -export function onTrigger(sub: ILink['sub']): void { +export function onTrigger(sub: Link['sub']): void { if (!__DEV__) { throw new Error( `Internal error: onTrigger should be called only in development.`, @@ -61,7 +61,7 @@ export function setupOnTrigger(target: { new (...args: any[]): any }): void { }) } -function setupFlagsHandler(target: ISubscriber): void { +function setupFlagsHandler(target: Subscriber): void { ;(target as any)._flags = target.flags Object.defineProperty(target, 'flags', { get() { @@ -71,9 +71,9 @@ function setupFlagsHandler(target: ISubscriber): void { if ( !( (target as any)._flags & - (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty) + (SubscriberFlags.CheckRequired | SubscriberFlags.Dirty) ) && - !!(value & (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty)) + !!(value & (SubscriberFlags.CheckRequired | SubscriberFlags.Dirty)) ) { onTrigger(this) } diff --git a/packages/reactivity/src/dep.ts b/packages/reactivity/src/dep.ts index 49b81d33dab..184964c17b8 100644 --- a/packages/reactivity/src/dep.ts +++ b/packages/reactivity/src/dep.ts @@ -3,28 +3,28 @@ import { type TrackOpTypes, TriggerOpTypes } from './constants' import { onTrack, triggerEventInfos } from './debug' import { activeSub } from './effect' import { - type IDependency, - type ILink, + type Dependency, + type Link, endBatch, link, propagate, startBatch, } from './system' -class Dep implements IDependency { - _subs: ILink | undefined = undefined - subsTail: ILink | undefined = undefined +class Dep implements Dependency { + _subs: Link | undefined = undefined + subsTail: Link | undefined = undefined constructor( private map: KeyToDepMap, private key: unknown, ) {} - get subs(): ILink | undefined { + get subs(): Link | undefined { return this._subs } - set subs(value: ILink | undefined) { + set subs(value: Link | undefined) { this._subs = value if (value === undefined) { this.map.delete(this.key) @@ -103,7 +103,7 @@ export function trigger( return } - const run = (dep: IDependency | undefined) => { + const run = (dep: Dependency | undefined) => { if (dep !== undefined && dep.subs !== undefined) { if (__DEV__) { triggerEventInfos.push({ @@ -190,7 +190,7 @@ export function trigger( export function getDepFromReactive( object: any, key: string | number | symbol, -): IDependency | undefined { +): Dependency | undefined { const depMap = targetMap.get(object) return depMap && depMap.get(key) } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index e09446364e8..c411928d0d8 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -3,9 +3,8 @@ import type { TrackOpTypes, TriggerOpTypes } from './constants' import { setupOnTrigger } from './debug' import { activeEffectScope } from './effectScope' import { - type IEffect, - type ILink, - type ISubscriber, + type Link, + type Subscriber, SubscriberFlags, endTrack, isDirty, @@ -16,7 +15,7 @@ import { warn } from './warning' export type EffectScheduler = (...args: any[]) => any export type DebuggerEvent = { - effect: ISubscriber + effect: Subscriber } & DebuggerEventExtraInfo export type DebuggerEventExtraInfo = { @@ -47,17 +46,17 @@ export enum EffectFlags { /** * ReactiveEffect only */ - ALLOW_RECURSE = 1 << 5, - PAUSED = 1 << 6, - NOTIFIED = 1 << 7, - STOP = 1 << 8, + ALLOW_RECURSE = 1 << 6, + PAUSED = 1 << 7, + NOTIFIED = 1 << 8, + STOP = 1 << 9, } -export class ReactiveEffect implements IEffect, ReactiveEffectOptions { +export class ReactiveEffect implements ReactiveEffectOptions { // Subscriber - deps: ILink | undefined = undefined - depsTail: ILink | undefined = undefined - flags: number = SubscriberFlags.Dirty + deps: Link | undefined = undefined + depsTail: Link | undefined = undefined + flags: number = 0 /** * @internal @@ -199,7 +198,7 @@ export function stop(runner: ReactiveEffectRunner): void { runner.effect.stop() } -const resetTrackingStack: (ISubscriber | undefined)[] = [] +const resetTrackingStack: (Subscriber | undefined)[] = [] /** * Temporarily pauses tracking. @@ -286,8 +285,8 @@ function cleanupEffect(e: ReactiveEffect) { } } -export let activeSub: ISubscriber | undefined = undefined +export let activeSub: Subscriber | undefined = undefined -export function setActiveSub(sub: ISubscriber | undefined): void { +export function setActiveSub(sub: Subscriber | undefined): void { activeSub = sub } diff --git a/packages/reactivity/src/effectScope.ts b/packages/reactivity/src/effectScope.ts index 2fdb15c0fbb..de84be62d98 100644 --- a/packages/reactivity/src/effectScope.ts +++ b/packages/reactivity/src/effectScope.ts @@ -1,7 +1,7 @@ import { EffectFlags, type ReactiveEffect } from './effect' import { - type ILink, - type ISubscriber, + type Link, + type Subscriber, SubscriberFlags, endTrack, startTrack, @@ -10,10 +10,10 @@ import { warn } from './warning' export let activeEffectScope: EffectScope | undefined -export class EffectScope implements ISubscriber { +export class EffectScope implements Subscriber { // Subscriber: In order to collect orphans computeds - deps: ILink | undefined = undefined - depsTail: ILink | undefined = undefined + deps: Link | undefined = undefined + depsTail: Link | undefined = undefined flags: number = SubscriberFlags.None /** diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index fd788dded90..9ae365f5dba 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -20,7 +20,7 @@ import { toRaw, toReactive, } from './reactive' -import { type IDependency, type ILink, link, propagate } from './system' +import { type Dependency, type Link, link, propagate } from './system' import { warn } from './warning' declare const RefSymbol: unique symbol @@ -108,10 +108,10 @@ function createRef(rawValue: unknown, shallow: boolean) { /** * @internal */ -class RefImpl implements IDependency { +class RefImpl implements Dependency { // Dependency - subs: ILink | undefined = undefined - subsTail: ILink | undefined = undefined + subs: Link | undefined = undefined + subsTail: Link | undefined = undefined _value: T private _rawValue: T @@ -194,7 +194,7 @@ export function triggerRef(ref: Ref): void { } } -function trackRef(dep: IDependency) { +function trackRef(dep: Dependency) { if (activeSub !== undefined) { if (__DEV__) { onTrack(activeSub!, { @@ -295,10 +295,10 @@ export type CustomRefFactory = ( set: (value: T) => void } -class CustomRefImpl implements IDependency { +class CustomRefImpl implements Dependency { // Dependency - subs: ILink | undefined = undefined - subsTail: ILink | undefined = undefined + subs: Link | undefined = undefined + subsTail: Link | undefined = undefined private readonly _get: ReturnType>['get'] private readonly _set: ReturnType>['set'] @@ -382,7 +382,7 @@ class ObjectRefImpl { this._object[this._key] = newVal } - get dep(): IDependency | undefined { + get dep(): Dependency | undefined { return getDepFromReactive(toRaw(this._object), this._key) } } diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index 734bafd2bcf..29c50fb8b2c 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -1,431 +1,550 @@ -// Ported from https://github.com/stackblitz/alien-signals/blob/4551e66317a369f95dc2233e71987549e18bd695/src/system.ts +/* eslint-disable */ +// Ported from https://github.com/stackblitz/alien-signals/blob/master/src/system.ts -export interface IEffect extends ISubscriber { - notify(): void +export interface Dependency { + subs: Link | undefined + subsTail: Link | undefined } -export interface IComputed extends IDependency, ISubscriber { - update(): boolean -} - -export interface IDependency { - subs: ILink | undefined - subsTail: ILink | undefined -} - -export interface ISubscriber { +export interface Subscriber { flags: SubscriberFlags - deps: ILink | undefined - depsTail: ILink | undefined + deps: Link | undefined + depsTail: Link | undefined } -export interface ILink { - dep: IDependency | IComputed | (IDependency & IEffect) - sub: ISubscriber | IComputed | (IDependency & IEffect) | IEffect +export interface Link { + dep: Dependency | (Dependency & Subscriber) + sub: Subscriber | (Dependency & Subscriber) // Reuse to link prev stack in checkDirty // Reuse to link prev stack in propagate - prevSub: ILink | undefined - nextSub: ILink | undefined + prevSub: Link | undefined + nextSub: Link | undefined // Reuse to link next released link in linkPool - nextDep: ILink | undefined + // Reuse to link notify effect in queuedEffects + nextDep: Link | undefined } -export enum SubscriberFlags { +export const enum SubscriberFlags { None = 0, Tracking = 1 << 0, - Recursed = 1 << 1, - InnerEffectsPending = 1 << 2, - ToCheckDirty = 1 << 3, - Dirty = 1 << 4, -} - -let batchDepth = 0 -let queuedEffects: IEffect | undefined -let queuedEffectsTail: IEffect | undefined -let linkPool: ILink | undefined - -export function startBatch(): void { - ++batchDepth -} - -export function endBatch(): void { - if (!--batchDepth) { - drainQueuedEffects() - } -} - -function drainQueuedEffects(): void { - while (queuedEffects !== undefined) { - const effect = queuedEffects - const depsTail = effect.depsTail! - const queuedNext = depsTail.nextDep - if (queuedNext !== undefined) { - depsTail.nextDep = undefined - queuedEffects = queuedNext.sub as IEffect - } else { - queuedEffects = undefined - queuedEffectsTail = undefined - } - effect.notify() - } + Notified = 1 << 1, + Recursed = 1 << 2, + PendingInnerEffects = 1 << 3, + CheckRequired = 1 << 4, + Dirty = 1 << 5, + Propagated = PendingInnerEffects | CheckRequired | Dirty, } -export function link(dep: IDependency, sub: ISubscriber): void { - const currentDep = sub.depsTail - if (currentDep !== undefined && currentDep.dep === dep) { - return +export function createSystem< + Computed extends Dependency & Subscriber, + Effect extends Subscriber, +>({ + computed: { is: isComputed, update: updateComputed }, + effect: { is: isEffect, notify: notifyEffect }, +}: { + computed: { + is(sub: Dependency & Subscriber): sub is Computed + update(computed: Computed): boolean } - const nextDep = currentDep !== undefined ? currentDep.nextDep : sub.deps - if (nextDep !== undefined && nextDep.dep === dep) { - sub.depsTail = nextDep - return + effect: { + is(sub: Subscriber): sub is Effect + notify(effect: Effect): boolean } - const depLastSub = dep.subsTail - if ( - depLastSub !== undefined && - depLastSub.sub === sub && - isValidLink(depLastSub, sub) - ) { - return +}) { + let queuedEffects: Effect | undefined + let queuedEffectsTail: Effect | undefined + let linkPool: Link | undefined + + return { + link, + propagate, + startTrack, + endTrack, + isDirty, + processComputedUpdate, + processInnerEffects, + processQueuedEffects, } - linkNewDep(dep, sub, nextDep, currentDep) -} -function linkNewDep( - dep: IDependency, - sub: ISubscriber, - nextDep: ILink | undefined, - depsTail: ILink | undefined, -): void { - let newLink: ILink - - if (linkPool !== undefined) { - newLink = linkPool - linkPool = newLink.nextDep - newLink.nextDep = nextDep - newLink.dep = dep - newLink.sub = sub - } else { - newLink = { - dep, - sub, - nextDep, - prevSub: undefined, - nextSub: undefined, + function link(dep: Dependency, sub: Subscriber): boolean { + const currentDep = sub.depsTail + if (currentDep !== undefined && currentDep.dep === dep) { + return false } - } - - if (depsTail === undefined) { - sub.deps = newLink - } else { - depsTail.nextDep = newLink - } - - if (dep.subs === undefined) { - dep.subs = newLink - } else { - const oldTail = dep.subsTail! - newLink.prevSub = oldTail - oldTail.nextSub = newLink - } - - sub.depsTail = newLink - dep.subsTail = newLink -} - -// See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions -export function propagate(link: ILink): void { - let targetFlag = SubscriberFlags.Dirty - let subs = link - let stack = 0 - - top: do { - const sub = link.sub - const subFlags = sub.flags - + const nextDep = currentDep !== undefined ? currentDep.nextDep : sub.deps + if (nextDep !== undefined && nextDep.dep === dep) { + sub.depsTail = nextDep + return false + } + const depLastSub = dep.subsTail if ( - (!( - subFlags & - (SubscriberFlags.Tracking | - SubscriberFlags.Recursed | - SubscriberFlags.InnerEffectsPending | - SubscriberFlags.ToCheckDirty | - SubscriberFlags.Dirty) - ) && - ((sub.flags = subFlags | targetFlag), true)) || - (subFlags & SubscriberFlags.Recursed && - !(subFlags & SubscriberFlags.Tracking) && - ((sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag), - true)) || - (!( - subFlags & - (SubscriberFlags.InnerEffectsPending | - SubscriberFlags.ToCheckDirty | - SubscriberFlags.Dirty) - ) && - isValidLink(link, sub) && - ((sub.flags = subFlags | SubscriberFlags.Recursed | targetFlag), - (sub as IDependency).subs !== undefined)) - ) { - const subSubs = (sub as IDependency).subs - if (subSubs !== undefined) { - if (subSubs.nextSub !== undefined) { - subSubs.prevSub = subs - link = subs = subSubs - targetFlag = SubscriberFlags.ToCheckDirty - ++stack - } else { - link = subSubs - targetFlag = - 'notify' in sub - ? SubscriberFlags.InnerEffectsPending - : SubscriberFlags.ToCheckDirty - } - continue - } - if ('notify' in sub) { - if (queuedEffectsTail !== undefined) { - queuedEffectsTail.depsTail!.nextDep = sub.deps - } else { - queuedEffects = sub - } - queuedEffectsTail = sub - } - } else if ( - !(subFlags & (SubscriberFlags.Tracking | targetFlag)) || - (!(subFlags & targetFlag) && - subFlags & - (SubscriberFlags.InnerEffectsPending | - SubscriberFlags.ToCheckDirty | - SubscriberFlags.Dirty) && - isValidLink(link, sub)) + depLastSub !== undefined && + depLastSub.sub === sub && + _isValidLink(depLastSub, sub) ) { - sub.flags = subFlags | targetFlag + return false } + _linkNewDep(dep, sub, nextDep, currentDep) + return true + } - if ((link = subs.nextSub!) !== undefined) { - subs = link - targetFlag = stack ? SubscriberFlags.ToCheckDirty : SubscriberFlags.Dirty - continue + function _linkNewDep( + dep: Dependency, + sub: Subscriber, + nextDep: Link | undefined, + depsTail: Link | undefined, + ): void { + let newLink: Link + + if (linkPool !== undefined) { + newLink = linkPool + linkPool = newLink.nextDep + newLink.nextDep = nextDep + newLink.dep = dep + newLink.sub = sub + } else { + newLink = { + dep, + sub, + nextDep, + prevSub: undefined, + nextSub: undefined, + } } - while (stack) { - --stack - const dep = subs.dep - const depSubs = dep.subs! - subs = depSubs.prevSub! - depSubs.prevSub = undefined - if ((link = subs.nextSub!) !== undefined) { - subs = link - targetFlag = stack - ? SubscriberFlags.ToCheckDirty - : SubscriberFlags.Dirty - continue top - } + if (depsTail === undefined) { + sub.deps = newLink + } else { + depsTail.nextDep = newLink } - break - } while (true) + if (dep.subs === undefined) { + dep.subs = newLink + } else { + const oldTail = dep.subsTail! + newLink.prevSub = oldTail + oldTail.nextSub = newLink + } - if (!batchDepth) { - drainQueuedEffects() + sub.depsTail = newLink + dep.subsTail = newLink } -} -export function shallowPropagate(link: ILink): void { - do { - const updateSub = link.sub - const updateSubFlags = updateSub.flags - if ( - (updateSubFlags & - (SubscriberFlags.ToCheckDirty | SubscriberFlags.Dirty)) === - SubscriberFlags.ToCheckDirty - ) { - updateSub.flags = updateSubFlags | SubscriberFlags.Dirty + function processQueuedEffects(): void { + while (queuedEffects !== undefined) { + const effect = queuedEffects + const depsTail = effect.depsTail! + const queuedNext = depsTail.nextDep + if (queuedNext !== undefined) { + depsTail.nextDep = undefined + queuedEffects = queuedNext.sub as Effect + } else { + queuedEffects = undefined + queuedEffectsTail = undefined + } + if (!notifyEffect(effect)) { + effect.flags &= ~SubscriberFlags.Notified + } } - link = link.nextSub! - } while (link !== undefined) -} + } -function isValidLink(subLink: ILink, sub: ISubscriber): boolean { - const depsTail = sub.depsTail - if (depsTail !== undefined) { - let link = sub.deps! + function processInnerEffects(link: Link): void { do { - if (link === subLink) { - return true - } - if (link === depsTail) { - break + const dep = link.dep + if ('flags' in dep && dep.flags && isEffect(dep)) { + notifyEffect(dep) } link = link.nextDep! } while (link !== undefined) } - return false -} -// See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions -export function checkDirty(link: ILink): boolean { - let stack = 0 - let dirty: boolean - - top: do { - dirty = false - const dep = link.dep - - if ('update' in dep) { - const depFlags = dep.flags - if (depFlags & SubscriberFlags.Dirty) { - if (dep.update()) { - const subs = dep.subs! - if (subs.nextSub !== undefined) { + function isDirty(sub: Subscriber, flags: SubscriberFlags): boolean { + if (flags & SubscriberFlags.Dirty) { + return true + } else if (flags & SubscriberFlags.CheckRequired) { + if (checkDirty(sub.deps!)) { + sub.flags = flags | SubscriberFlags.Dirty + return true + } else { + sub.flags &= ~SubscriberFlags.CheckRequired + } + } + return false + } + + function processComputedUpdate( + computed: Computed, + flags: SubscriberFlags, + ): void { + if (flags & SubscriberFlags.Dirty) { + if (updateComputed(computed)) { + const subs = computed.subs + if (subs !== undefined) { + shallowPropagate(subs) + } + } + } else if (flags & SubscriberFlags.CheckRequired) { + if (checkDirty(computed.deps!)) { + if (updateComputed(computed)) { + const subs = computed.subs + if (subs !== undefined) { shallowPropagate(subs) } - dirty = true } - } else if (depFlags & SubscriberFlags.ToCheckDirty) { - const depSubs = dep.subs! - if (depSubs.nextSub !== undefined) { - depSubs.prevSub = link + } else { + computed.flags &= ~SubscriberFlags.CheckRequired + } + } + } + + // See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions + function checkDirty(link: Link): boolean { + let stack = 0 + let dirty: boolean + + top: do { + dirty = false + const dep = link.dep + + if ('flags' in dep) { + const depFlags = dep.flags + if (depFlags & SubscriberFlags.Dirty) { + if (isComputed(dep) && updateComputed(dep)) { + const subs = dep.subs! + if (subs.nextSub !== undefined) { + shallowPropagate(subs) + } + dirty = true + } + } else if (depFlags & SubscriberFlags.CheckRequired) { + if (isComputed(dep)) { + const depSubs = dep.subs! + if (depSubs.nextSub !== undefined) { + depSubs.prevSub = link + } + link = dep.deps! + ++stack + continue + } } - link = dep.deps! - ++stack + } + + if (!dirty && link.nextDep !== undefined) { + link = link.nextDep continue } - } - if (!dirty && link.nextDep !== undefined) { - link = link.nextDep - continue - } + if (stack) { + let sub = link.sub as Computed + do { + --stack + const subSubs = sub.subs! + + if (dirty) { + if (updateComputed(sub)) { + if ((link = subSubs.prevSub!) !== undefined) { + subSubs.prevSub = undefined + shallowPropagate(sub.subs!) + sub = link.sub as Computed + } else { + sub = subSubs.sub as Computed + } + continue + } + } else { + sub.flags &= ~SubscriberFlags.CheckRequired + } - if (stack) { - let sub = link.sub as IComputed - do { - --stack - const subSubs = sub.subs! - - if (dirty) { - if (sub.update()) { - if ((link = subSubs.prevSub!) !== undefined) { - subSubs.prevSub = undefined - shallowPropagate(sub.subs!) - sub = link.sub as IComputed + if ((link = subSubs.prevSub!) !== undefined) { + subSubs.prevSub = undefined + if (link.nextDep !== undefined) { + link = link.nextDep + continue top + } + sub = link.sub as Computed + } else { + if ((link = subSubs.nextDep!) !== undefined) { + continue top + } + sub = subSubs.sub as Computed + } + + dirty = false + } while (stack) + } + + return dirty + } while (true) + } + + function shallowPropagate(link: Link): void { + do { + const sub = link.sub + const subFlags = sub.flags + if ( + (subFlags & (SubscriberFlags.CheckRequired | SubscriberFlags.Dirty)) === + SubscriberFlags.CheckRequired + ) { + sub.flags = subFlags | SubscriberFlags.Dirty | SubscriberFlags.Notified + if (!(subFlags & SubscriberFlags.Notified)) { + if (isEffect(sub)) { + if (queuedEffectsTail !== undefined) { + queuedEffectsTail.depsTail!.nextDep = sub.deps } else { - sub = subSubs.sub as IComputed + queuedEffects = sub } - continue + queuedEffectsTail = sub } - } else { - sub.flags &= ~SubscriberFlags.ToCheckDirty } + } + link = link.nextSub! + } while (link !== undefined) + } - if ((link = subSubs.prevSub!) !== undefined) { - subSubs.prevSub = undefined - if (link.nextDep !== undefined) { - link = link.nextDep - continue top + // See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions + function propagate(link: Link): void { + let targetFlag = SubscriberFlags.Dirty + let subs = link + let stack = 0 + + top: do { + const sub = link.sub + const subFlags = sub.flags + + if ( + (!( + subFlags & + (SubscriberFlags.Tracking | + SubscriberFlags.Recursed | + SubscriberFlags.Propagated) + ) && + ((sub.flags = subFlags | targetFlag | SubscriberFlags.Notified), + true)) || + (subFlags & SubscriberFlags.Recursed && + !(subFlags & SubscriberFlags.Tracking) && + ((sub.flags = + (subFlags & ~SubscriberFlags.Recursed) | + targetFlag | + SubscriberFlags.Notified), + true)) || + (!(subFlags & SubscriberFlags.Propagated) && + _isValidLink(link, sub) && + ((sub.flags = + subFlags | + SubscriberFlags.Recursed | + targetFlag | + SubscriberFlags.Notified), + (sub as Dependency).subs !== undefined)) + ) { + const subSubs = (sub as Dependency).subs + if (subSubs !== undefined) { + if (subSubs.nextSub !== undefined) { + subSubs.prevSub = subs + link = subs = subSubs + targetFlag = SubscriberFlags.CheckRequired + ++stack + } else { + link = subSubs + targetFlag = isEffect(sub) + ? SubscriberFlags.PendingInnerEffects + : SubscriberFlags.CheckRequired } - sub = link.sub as IComputed - } else { - if ((link = subSubs.nextDep!) !== undefined) { - continue top + continue + } + if (isEffect(sub)) { + if (queuedEffectsTail !== undefined) { + queuedEffectsTail.depsTail!.nextDep = sub.deps + } else { + queuedEffects = sub + } + queuedEffectsTail = sub + } + } else if (!(subFlags & (SubscriberFlags.Tracking | targetFlag))) { + sub.flags = subFlags | targetFlag | SubscriberFlags.Notified + if (!(subFlags & SubscriberFlags.Notified)) { + if (isEffect(sub)) { + if (queuedEffectsTail !== undefined) { + queuedEffectsTail.depsTail!.nextDep = sub.deps + } else { + queuedEffects = sub + } + queuedEffectsTail = sub } - sub = subSubs.sub as IComputed } + } else if ( + !(subFlags & targetFlag) && + subFlags & SubscriberFlags.Propagated && + _isValidLink(link, sub) + ) { + sub.flags = subFlags | targetFlag + } - dirty = false - } while (stack) - } + if ((link = subs.nextSub!) !== undefined) { + subs = link + targetFlag = stack + ? SubscriberFlags.CheckRequired + : SubscriberFlags.Dirty + continue + } - return dirty - } while (true) -} + while (stack) { + --stack + const dep = subs.dep + const depSubs = dep.subs! + subs = depSubs.prevSub! + depSubs.prevSub = undefined + if ((link = subs.nextSub!) !== undefined) { + subs = link + targetFlag = stack + ? SubscriberFlags.CheckRequired + : SubscriberFlags.Dirty + continue top + } + } -export function startTrack(sub: ISubscriber): void { - sub.depsTail = undefined - sub.flags = - (sub.flags & - ~( - SubscriberFlags.Recursed | - SubscriberFlags.InnerEffectsPending | - SubscriberFlags.ToCheckDirty | - SubscriberFlags.Dirty - )) | - SubscriberFlags.Tracking -} + break + } while (true) + } -export function endTrack(sub: ISubscriber): void { - const depsTail = sub.depsTail - if (depsTail !== undefined) { - const nextDep = depsTail.nextDep - if (nextDep !== undefined) { - clearTrack(nextDep) - depsTail.nextDep = undefined + function _isValidLink(subLink: Link, sub: Subscriber): boolean { + const depsTail = sub.depsTail + if (depsTail !== undefined) { + let link = sub.deps! + do { + if (link === subLink) { + return true + } + if (link === depsTail) { + break + } + link = link.nextDep! + } while (link !== undefined) } - } else if (sub.deps !== undefined) { - clearTrack(sub.deps) - sub.deps = undefined + return false } - sub.flags &= ~SubscriberFlags.Tracking -} -function clearTrack(link: ILink): void { - do { - const dep = link.dep - const nextDep = link.nextDep - const nextSub = link.nextSub - const prevSub = link.prevSub + function startTrack(sub: Subscriber): void { + sub.depsTail = undefined + sub.flags = + (sub.flags & + ~( + SubscriberFlags.Notified | + SubscriberFlags.Recursed | + SubscriberFlags.Propagated + )) | + SubscriberFlags.Tracking + } - if (nextSub !== undefined) { - nextSub.prevSub = prevSub - link.nextSub = undefined - } else { - dep.subsTail = prevSub + function endTrack(sub: Subscriber): void { + const depsTail = sub.depsTail + if (depsTail !== undefined) { + const nextDep = depsTail.nextDep + if (nextDep !== undefined) { + _clearTrack(nextDep) + depsTail.nextDep = undefined + } + } else if (sub.deps !== undefined) { + _clearTrack(sub.deps) + sub.deps = undefined } + sub.flags &= ~SubscriberFlags.Tracking + } - if (prevSub !== undefined) { - prevSub.nextSub = nextSub - link.prevSub = undefined - } else { - dep.subs = nextSub - } + function _clearTrack(link: Link): void { + do { + const dep = link.dep + const nextDep = link.nextDep + const nextSub = link.nextSub + const prevSub = link.prevSub + + if (nextSub !== undefined) { + nextSub.prevSub = prevSub + link.nextSub = undefined + } else { + dep.subsTail = prevSub + } - // @ts-expect-error - link.dep = undefined - // @ts-expect-error - link.sub = undefined - link.nextDep = linkPool - linkPool = link - - if (dep.subs === undefined && 'deps' in dep) { - const depFlags = dep.flags - if (!(depFlags & SubscriberFlags.Dirty)) { - dep.flags = depFlags | SubscriberFlags.Dirty + if (prevSub !== undefined) { + prevSub.nextSub = nextSub + link.prevSub = undefined + } else { + dep.subs = nextSub } - const depDeps = dep.deps - if (depDeps !== undefined) { - link = depDeps - dep.depsTail!.nextDep = nextDep - dep.deps = undefined - dep.depsTail = undefined - continue + + // @ts-expect-error + link.dep = undefined + // @ts-expect-error + link.sub = undefined + link.nextDep = linkPool + linkPool = link + + if (dep.subs === undefined && 'deps' in dep) { + const depFlags = dep.flags + if (!(depFlags & SubscriberFlags.Dirty)) { + dep.flags = depFlags | SubscriberFlags.Dirty + } + const depDeps = dep.deps + if (depDeps !== undefined) { + link = depDeps + dep.depsTail!.nextDep = nextDep + dep.deps = undefined + dep.depsTail = undefined + continue + } } - } - link = nextDep! - } while (link !== undefined) + link = nextDep! + } while (link !== undefined) + } } -export function isDirty(sub: ISubscriber, flags: SubscriberFlags): boolean { - if (flags & SubscriberFlags.Dirty) { - return true - } else if (flags & SubscriberFlags.ToCheckDirty) { - if (checkDirty(sub.deps!)) { - sub.flags = flags | SubscriberFlags.Dirty +import type { ComputedRefImpl } from './computed.js' +import type { ReactiveEffect } from './effect.js' + +const { + endTrack, + link, + isDirty, + processComputedUpdate, + processQueuedEffects, + propagate: _propagate, + startTrack, +} = createSystem({ + computed: { + is(sub): sub is ComputedRefImpl { + return 'update' in sub + }, + update(computed) { + return computed.update() + }, + }, + effect: { + is(sub): sub is ReactiveEffect { + return 'notify' in sub + }, + notify(effect) { + effect.notify() return true - } else { - sub.flags = flags & ~SubscriberFlags.ToCheckDirty - } + }, + }, +}) + +export { endTrack, link, isDirty, processComputedUpdate, startTrack } + +let batchDepth = 0 + +export function startBatch(): void { + ++batchDepth +} + +export function endBatch(): void { + if (!--batchDepth) { + processQueuedEffects() + } +} + +export function propagate(link: Link): void { + _propagate(link) + if (!batchDepth) { + processQueuedEffects() } - return false } diff --git a/tsconfig.json b/tsconfig.json index 9896083cf14..e56ba6fff6c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,7 +13,7 @@ "noUnusedLocals": true, "experimentalDecorators": true, "resolveJsonModule": true, - "isolatedModules": true, + // "isolatedModules": true, "skipLibCheck": true, "esModuleInterop": true, "removeComments": false, @@ -26,7 +26,7 @@ "@vue/*": ["packages/*/src"], "vue": ["packages/vue/src"] }, - "isolatedDeclarations": true, + // "isolatedDeclarations": true, "composite": true }, "include": [ From 1b49a9b4f600515136bd7ebefbc298516b9c262c Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 13 Jan 2025 01:56:42 +0800 Subject: [PATCH 23/30] Minimize --- packages/reactivity/src/system.ts | 83 +++++-------------------------- 1 file changed, 12 insertions(+), 71 deletions(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index 29c50fb8b2c..ef8c872d66b 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -27,12 +27,12 @@ export interface Link { export const enum SubscriberFlags { None = 0, Tracking = 1 << 0, - Notified = 1 << 1, + // Notified = 1 << 1, Recursed = 1 << 2, - PendingInnerEffects = 1 << 3, + // PendingInnerEffects = 1 << 3, CheckRequired = 1 << 4, Dirty = 1 << 5, - Propagated = PendingInnerEffects | CheckRequired | Dirty, + Propagated = CheckRequired | Dirty, } export function createSystem< @@ -62,7 +62,6 @@ export function createSystem< endTrack, isDirty, processComputedUpdate, - processInnerEffects, processQueuedEffects, } @@ -142,22 +141,10 @@ export function createSystem< queuedEffects = undefined queuedEffectsTail = undefined } - if (!notifyEffect(effect)) { - effect.flags &= ~SubscriberFlags.Notified - } + notifyEffect(effect) } } - function processInnerEffects(link: Link): void { - do { - const dep = link.dep - if ('flags' in dep && dep.flags && isEffect(dep)) { - notifyEffect(dep) - } - link = link.nextDep! - } while (link !== undefined) - } - function isDirty(sub: Subscriber, flags: SubscriberFlags): boolean { if (flags & SubscriberFlags.Dirty) { return true @@ -176,24 +163,13 @@ export function createSystem< computed: Computed, flags: SubscriberFlags, ): void { - if (flags & SubscriberFlags.Dirty) { + if (isDirty(computed, flags)) { if (updateComputed(computed)) { const subs = computed.subs if (subs !== undefined) { shallowPropagate(subs) } } - } else if (flags & SubscriberFlags.CheckRequired) { - if (checkDirty(computed.deps!)) { - if (updateComputed(computed)) { - const subs = computed.subs - if (subs !== undefined) { - shallowPropagate(subs) - } - } - } else { - computed.flags &= ~SubscriberFlags.CheckRequired - } } } @@ -285,17 +261,7 @@ export function createSystem< (subFlags & (SubscriberFlags.CheckRequired | SubscriberFlags.Dirty)) === SubscriberFlags.CheckRequired ) { - sub.flags = subFlags | SubscriberFlags.Dirty | SubscriberFlags.Notified - if (!(subFlags & SubscriberFlags.Notified)) { - if (isEffect(sub)) { - if (queuedEffectsTail !== undefined) { - queuedEffectsTail.depsTail!.nextDep = sub.deps - } else { - queuedEffects = sub - } - queuedEffectsTail = sub - } - } + sub.flags = subFlags | SubscriberFlags.Dirty } link = link.nextSub! } while (link !== undefined) @@ -318,22 +284,14 @@ export function createSystem< SubscriberFlags.Recursed | SubscriberFlags.Propagated) ) && - ((sub.flags = subFlags | targetFlag | SubscriberFlags.Notified), - true)) || + ((sub.flags = subFlags | targetFlag), true)) || (subFlags & SubscriberFlags.Recursed && !(subFlags & SubscriberFlags.Tracking) && - ((sub.flags = - (subFlags & ~SubscriberFlags.Recursed) | - targetFlag | - SubscriberFlags.Notified), + ((sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag), true)) || (!(subFlags & SubscriberFlags.Propagated) && _isValidLink(link, sub) && - ((sub.flags = - subFlags | - SubscriberFlags.Recursed | - targetFlag | - SubscriberFlags.Notified), + ((sub.flags = subFlags | SubscriberFlags.Recursed | targetFlag), (sub as Dependency).subs !== undefined)) ) { const subSubs = (sub as Dependency).subs @@ -345,9 +303,7 @@ export function createSystem< ++stack } else { link = subSubs - targetFlag = isEffect(sub) - ? SubscriberFlags.PendingInnerEffects - : SubscriberFlags.CheckRequired + targetFlag = SubscriberFlags.CheckRequired } continue } @@ -360,17 +316,7 @@ export function createSystem< queuedEffectsTail = sub } } else if (!(subFlags & (SubscriberFlags.Tracking | targetFlag))) { - sub.flags = subFlags | targetFlag | SubscriberFlags.Notified - if (!(subFlags & SubscriberFlags.Notified)) { - if (isEffect(sub)) { - if (queuedEffectsTail !== undefined) { - queuedEffectsTail.depsTail!.nextDep = sub.deps - } else { - queuedEffects = sub - } - queuedEffectsTail = sub - } - } + sub.flags = subFlags | targetFlag } else if ( !(subFlags & targetFlag) && subFlags & SubscriberFlags.Propagated && @@ -426,12 +372,7 @@ export function createSystem< function startTrack(sub: Subscriber): void { sub.depsTail = undefined sub.flags = - (sub.flags & - ~( - SubscriberFlags.Notified | - SubscriberFlags.Recursed | - SubscriberFlags.Propagated - )) | + (sub.flags & ~(SubscriberFlags.Recursed | SubscriberFlags.Propagated)) | SubscriberFlags.Tracking } From d534df31e6557683cf797a51607f1e1b055f9ef5 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 13 Jan 2025 02:04:30 +0800 Subject: [PATCH 24/30] Minimize --- packages/reactivity/src/system.ts | 706 ++++++++++++++---------------- 1 file changed, 323 insertions(+), 383 deletions(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index ef8c872d66b..ff1449d4fb7 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -13,8 +13,8 @@ export interface Subscriber { } export interface Link { - dep: Dependency | (Dependency & Subscriber) - sub: Subscriber | (Dependency & Subscriber) + dep: Dependency | Computed + sub: Subscriber | Computed | Effect // Reuse to link prev stack in checkDirty // Reuse to link prev stack in propagate prevSub: Link | undefined @@ -35,441 +35,386 @@ export const enum SubscriberFlags { Propagated = CheckRequired | Dirty, } -export function createSystem< - Computed extends Dependency & Subscriber, - Effect extends Subscriber, ->({ - computed: { is: isComputed, update: updateComputed }, - effect: { is: isEffect, notify: notifyEffect }, -}: { - computed: { - is(sub: Dependency & Subscriber): sub is Computed - update(computed: Computed): boolean +let queuedEffects: Effect | undefined +let queuedEffectsTail: Effect | undefined +let linkPool: Link | undefined + +function link(dep: Dependency, sub: Subscriber): boolean { + const currentDep = sub.depsTail + if (currentDep !== undefined && currentDep.dep === dep) { + return false } - effect: { - is(sub: Subscriber): sub is Effect - notify(effect: Effect): boolean + const nextDep = currentDep !== undefined ? currentDep.nextDep : sub.deps + if (nextDep !== undefined && nextDep.dep === dep) { + sub.depsTail = nextDep + return false } -}) { - let queuedEffects: Effect | undefined - let queuedEffectsTail: Effect | undefined - let linkPool: Link | undefined - - return { - link, - propagate, - startTrack, - endTrack, - isDirty, - processComputedUpdate, - processQueuedEffects, + const depLastSub = dep.subsTail + if ( + depLastSub !== undefined && + depLastSub.sub === sub && + _isValidLink(depLastSub, sub) + ) { + return false } + _linkNewDep(dep, sub, nextDep, currentDep) + return true +} - function link(dep: Dependency, sub: Subscriber): boolean { - const currentDep = sub.depsTail - if (currentDep !== undefined && currentDep.dep === dep) { - return false - } - const nextDep = currentDep !== undefined ? currentDep.nextDep : sub.deps - if (nextDep !== undefined && nextDep.dep === dep) { - sub.depsTail = nextDep - return false +function _linkNewDep( + dep: Dependency, + sub: Subscriber, + nextDep: Link | undefined, + depsTail: Link | undefined, +): void { + let newLink: Link + + if (linkPool !== undefined) { + newLink = linkPool + linkPool = newLink.nextDep + newLink.nextDep = nextDep + newLink.dep = dep + newLink.sub = sub + } else { + newLink = { + dep, + sub, + nextDep, + prevSub: undefined, + nextSub: undefined, } - const depLastSub = dep.subsTail - if ( - depLastSub !== undefined && - depLastSub.sub === sub && - _isValidLink(depLastSub, sub) - ) { - return false - } - _linkNewDep(dep, sub, nextDep, currentDep) - return true } - function _linkNewDep( - dep: Dependency, - sub: Subscriber, - nextDep: Link | undefined, - depsTail: Link | undefined, - ): void { - let newLink: Link - - if (linkPool !== undefined) { - newLink = linkPool - linkPool = newLink.nextDep - newLink.nextDep = nextDep - newLink.dep = dep - newLink.sub = sub - } else { - newLink = { - dep, - sub, - nextDep, - prevSub: undefined, - nextSub: undefined, - } - } - - if (depsTail === undefined) { - sub.deps = newLink - } else { - depsTail.nextDep = newLink - } - - if (dep.subs === undefined) { - dep.subs = newLink - } else { - const oldTail = dep.subsTail! - newLink.prevSub = oldTail - oldTail.nextSub = newLink - } + if (depsTail === undefined) { + sub.deps = newLink + } else { + depsTail.nextDep = newLink + } - sub.depsTail = newLink - dep.subsTail = newLink + if (dep.subs === undefined) { + dep.subs = newLink + } else { + const oldTail = dep.subsTail! + newLink.prevSub = oldTail + oldTail.nextSub = newLink } - function processQueuedEffects(): void { - while (queuedEffects !== undefined) { - const effect = queuedEffects - const depsTail = effect.depsTail! - const queuedNext = depsTail.nextDep - if (queuedNext !== undefined) { - depsTail.nextDep = undefined - queuedEffects = queuedNext.sub as Effect - } else { - queuedEffects = undefined - queuedEffectsTail = undefined - } - notifyEffect(effect) + sub.depsTail = newLink + dep.subsTail = newLink +} + +function processQueuedEffects(): void { + while (queuedEffects !== undefined) { + const effect = queuedEffects + const depsTail = effect.depsTail! + const queuedNext = depsTail.nextDep + if (queuedNext !== undefined) { + depsTail.nextDep = undefined + queuedEffects = queuedNext.sub as Effect + } else { + queuedEffects = undefined + queuedEffectsTail = undefined } + effect.notify() } +} - function isDirty(sub: Subscriber, flags: SubscriberFlags): boolean { - if (flags & SubscriberFlags.Dirty) { +function isDirty(sub: Subscriber, flags: SubscriberFlags): boolean { + if (flags & SubscriberFlags.Dirty) { + return true + } else if (flags & SubscriberFlags.CheckRequired) { + if (checkDirty(sub.deps!)) { + sub.flags = flags | SubscriberFlags.Dirty return true - } else if (flags & SubscriberFlags.CheckRequired) { - if (checkDirty(sub.deps!)) { - sub.flags = flags | SubscriberFlags.Dirty - return true - } else { - sub.flags &= ~SubscriberFlags.CheckRequired - } + } else { + sub.flags &= ~SubscriberFlags.CheckRequired } - return false } + return false +} - function processComputedUpdate( - computed: Computed, - flags: SubscriberFlags, - ): void { - if (isDirty(computed, flags)) { - if (updateComputed(computed)) { - const subs = computed.subs - if (subs !== undefined) { - shallowPropagate(subs) - } +function processComputedUpdate( + computed: Computed, + flags: SubscriberFlags, +): void { + if (isDirty(computed, flags)) { + if (computed.update()) { + const subs = computed.subs + if (subs !== undefined) { + shallowPropagate(subs) } } } +} - // See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions - function checkDirty(link: Link): boolean { - let stack = 0 - let dirty: boolean - - top: do { - dirty = false - const dep = link.dep - - if ('flags' in dep) { - const depFlags = dep.flags - if (depFlags & SubscriberFlags.Dirty) { - if (isComputed(dep) && updateComputed(dep)) { - const subs = dep.subs! - if (subs.nextSub !== undefined) { - shallowPropagate(subs) - } - dirty = true +// See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions +function checkDirty(link: Link): boolean { + let stack = 0 + let dirty: boolean + + top: do { + dirty = false + const dep = link.dep + + if ('flags' in dep) { + const depFlags = dep.flags + if (depFlags & SubscriberFlags.Dirty) { + if ('update' in dep && dep.update()) { + const subs = dep.subs! + if (subs.nextSub !== undefined) { + shallowPropagate(subs) } - } else if (depFlags & SubscriberFlags.CheckRequired) { - if (isComputed(dep)) { - const depSubs = dep.subs! - if (depSubs.nextSub !== undefined) { - depSubs.prevSub = link - } - link = dep.deps! - ++stack - continue + dirty = true + } + } else if (depFlags & SubscriberFlags.CheckRequired) { + if ('update' in dep) { + const depSubs = dep.subs! + if (depSubs.nextSub !== undefined) { + depSubs.prevSub = link } + link = dep.deps! + ++stack + continue } } + } - if (!dirty && link.nextDep !== undefined) { - link = link.nextDep - continue - } + if (!dirty && link.nextDep !== undefined) { + link = link.nextDep + continue + } - if (stack) { - let sub = link.sub as Computed - do { - --stack - const subSubs = sub.subs! - - if (dirty) { - if (updateComputed(sub)) { - if ((link = subSubs.prevSub!) !== undefined) { - subSubs.prevSub = undefined - shallowPropagate(sub.subs!) - sub = link.sub as Computed - } else { - sub = subSubs.sub as Computed - } - continue + if (stack) { + let sub = link.sub as Computed + do { + --stack + const subSubs = sub.subs! + + if (dirty) { + if (sub.update()) { + if ((link = subSubs.prevSub!) !== undefined) { + subSubs.prevSub = undefined + shallowPropagate(sub.subs!) + sub = link.sub as Computed + } else { + sub = subSubs.sub as Computed } - } else { - sub.flags &= ~SubscriberFlags.CheckRequired + continue } + } else { + sub.flags &= ~SubscriberFlags.CheckRequired + } - if ((link = subSubs.prevSub!) !== undefined) { - subSubs.prevSub = undefined - if (link.nextDep !== undefined) { - link = link.nextDep - continue top - } - sub = link.sub as Computed - } else { - if ((link = subSubs.nextDep!) !== undefined) { - continue top - } - sub = subSubs.sub as Computed + if ((link = subSubs.prevSub!) !== undefined) { + subSubs.prevSub = undefined + if (link.nextDep !== undefined) { + link = link.nextDep + continue top + } + sub = link.sub as Computed + } else { + if ((link = subSubs.nextDep!) !== undefined) { + continue top } + sub = subSubs.sub as Computed + } - dirty = false - } while (stack) - } + dirty = false + } while (stack) + } - return dirty - } while (true) - } + return dirty + } while (true) +} - function shallowPropagate(link: Link): void { - do { - const sub = link.sub - const subFlags = sub.flags - if ( - (subFlags & (SubscriberFlags.CheckRequired | SubscriberFlags.Dirty)) === - SubscriberFlags.CheckRequired - ) { - sub.flags = subFlags | SubscriberFlags.Dirty - } - link = link.nextSub! - } while (link !== undefined) - } +function shallowPropagate(link: Link): void { + do { + const sub = link.sub + const subFlags = sub.flags + if ( + (subFlags & (SubscriberFlags.CheckRequired | SubscriberFlags.Dirty)) === + SubscriberFlags.CheckRequired + ) { + sub.flags = subFlags | SubscriberFlags.Dirty + } + link = link.nextSub! + } while (link !== undefined) +} - // See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions - function propagate(link: Link): void { - let targetFlag = SubscriberFlags.Dirty - let subs = link - let stack = 0 - - top: do { - const sub = link.sub - const subFlags = sub.flags - - if ( - (!( - subFlags & - (SubscriberFlags.Tracking | - SubscriberFlags.Recursed | - SubscriberFlags.Propagated) - ) && - ((sub.flags = subFlags | targetFlag), true)) || - (subFlags & SubscriberFlags.Recursed && - !(subFlags & SubscriberFlags.Tracking) && - ((sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag), - true)) || - (!(subFlags & SubscriberFlags.Propagated) && - _isValidLink(link, sub) && - ((sub.flags = subFlags | SubscriberFlags.Recursed | targetFlag), - (sub as Dependency).subs !== undefined)) - ) { - const subSubs = (sub as Dependency).subs - if (subSubs !== undefined) { - if (subSubs.nextSub !== undefined) { - subSubs.prevSub = subs - link = subs = subSubs - targetFlag = SubscriberFlags.CheckRequired - ++stack - } else { - link = subSubs - targetFlag = SubscriberFlags.CheckRequired - } - continue +// See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions +function propagate(link: Link): void { + let targetFlag = SubscriberFlags.Dirty + let subs = link + let stack = 0 + + top: do { + const sub = link.sub + const subFlags = sub.flags + + if ( + (!( + subFlags & + (SubscriberFlags.Tracking | + SubscriberFlags.Recursed | + SubscriberFlags.Propagated) + ) && + ((sub.flags = subFlags | targetFlag), true)) || + (subFlags & SubscriberFlags.Recursed && + !(subFlags & SubscriberFlags.Tracking) && + ((sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag), + true)) || + (!(subFlags & SubscriberFlags.Propagated) && + _isValidLink(link, sub) && + ((sub.flags = subFlags | SubscriberFlags.Recursed | targetFlag), + (sub as Dependency).subs !== undefined)) + ) { + const subSubs = (sub as Dependency).subs + if (subSubs !== undefined) { + if (subSubs.nextSub !== undefined) { + subSubs.prevSub = subs + link = subs = subSubs + targetFlag = SubscriberFlags.CheckRequired + ++stack + } else { + link = subSubs + targetFlag = SubscriberFlags.CheckRequired } - if (isEffect(sub)) { - if (queuedEffectsTail !== undefined) { - queuedEffectsTail.depsTail!.nextDep = sub.deps - } else { - queuedEffects = sub - } - queuedEffectsTail = sub + continue + } + if ('notify' in sub) { + if (queuedEffectsTail !== undefined) { + queuedEffectsTail.depsTail!.nextDep = sub.deps + } else { + queuedEffects = sub } - } else if (!(subFlags & (SubscriberFlags.Tracking | targetFlag))) { - sub.flags = subFlags | targetFlag - } else if ( - !(subFlags & targetFlag) && - subFlags & SubscriberFlags.Propagated && - _isValidLink(link, sub) - ) { - sub.flags = subFlags | targetFlag + queuedEffectsTail = sub } + } else if (!(subFlags & (SubscriberFlags.Tracking | targetFlag))) { + sub.flags = subFlags | targetFlag + } else if ( + !(subFlags & targetFlag) && + subFlags & SubscriberFlags.Propagated && + _isValidLink(link, sub) + ) { + sub.flags = subFlags | targetFlag + } + + if ((link = subs.nextSub!) !== undefined) { + subs = link + targetFlag = stack ? SubscriberFlags.CheckRequired : SubscriberFlags.Dirty + continue + } + while (stack) { + --stack + const dep = subs.dep + const depSubs = dep.subs! + subs = depSubs.prevSub! + depSubs.prevSub = undefined if ((link = subs.nextSub!) !== undefined) { subs = link targetFlag = stack ? SubscriberFlags.CheckRequired : SubscriberFlags.Dirty - continue + continue top } + } - while (stack) { - --stack - const dep = subs.dep - const depSubs = dep.subs! - subs = depSubs.prevSub! - depSubs.prevSub = undefined - if ((link = subs.nextSub!) !== undefined) { - subs = link - targetFlag = stack - ? SubscriberFlags.CheckRequired - : SubscriberFlags.Dirty - continue top - } - } + break + } while (true) - break - } while (true) + if (!batchDepth) { + processQueuedEffects() } +} - function _isValidLink(subLink: Link, sub: Subscriber): boolean { - const depsTail = sub.depsTail - if (depsTail !== undefined) { - let link = sub.deps! - do { - if (link === subLink) { - return true - } - if (link === depsTail) { - break - } - link = link.nextDep! - } while (link !== undefined) - } - return false +function _isValidLink(subLink: Link, sub: Subscriber): boolean { + const depsTail = sub.depsTail + if (depsTail !== undefined) { + let link = sub.deps! + do { + if (link === subLink) { + return true + } + if (link === depsTail) { + break + } + link = link.nextDep! + } while (link !== undefined) } + return false +} - function startTrack(sub: Subscriber): void { - sub.depsTail = undefined - sub.flags = - (sub.flags & ~(SubscriberFlags.Recursed | SubscriberFlags.Propagated)) | - SubscriberFlags.Tracking - } +function startTrack(sub: Subscriber): void { + sub.depsTail = undefined + sub.flags = + (sub.flags & ~(SubscriberFlags.Recursed | SubscriberFlags.Propagated)) | + SubscriberFlags.Tracking +} - function endTrack(sub: Subscriber): void { - const depsTail = sub.depsTail - if (depsTail !== undefined) { - const nextDep = depsTail.nextDep - if (nextDep !== undefined) { - _clearTrack(nextDep) - depsTail.nextDep = undefined - } - } else if (sub.deps !== undefined) { - _clearTrack(sub.deps) - sub.deps = undefined +function endTrack(sub: Subscriber): void { + const depsTail = sub.depsTail + if (depsTail !== undefined) { + const nextDep = depsTail.nextDep + if (nextDep !== undefined) { + _clearTrack(nextDep) + depsTail.nextDep = undefined } - sub.flags &= ~SubscriberFlags.Tracking + } else if (sub.deps !== undefined) { + _clearTrack(sub.deps) + sub.deps = undefined } + sub.flags &= ~SubscriberFlags.Tracking +} - function _clearTrack(link: Link): void { - do { - const dep = link.dep - const nextDep = link.nextDep - const nextSub = link.nextSub - const prevSub = link.prevSub - - if (nextSub !== undefined) { - nextSub.prevSub = prevSub - link.nextSub = undefined - } else { - dep.subsTail = prevSub - } +function _clearTrack(link: Link): void { + do { + const dep = link.dep + const nextDep = link.nextDep + const nextSub = link.nextSub + const prevSub = link.prevSub - if (prevSub !== undefined) { - prevSub.nextSub = nextSub - link.prevSub = undefined - } else { - dep.subs = nextSub - } + if (nextSub !== undefined) { + nextSub.prevSub = prevSub + link.nextSub = undefined + } else { + dep.subsTail = prevSub + } - // @ts-expect-error - link.dep = undefined - // @ts-expect-error - link.sub = undefined - link.nextDep = linkPool - linkPool = link - - if (dep.subs === undefined && 'deps' in dep) { - const depFlags = dep.flags - if (!(depFlags & SubscriberFlags.Dirty)) { - dep.flags = depFlags | SubscriberFlags.Dirty - } - const depDeps = dep.deps - if (depDeps !== undefined) { - link = depDeps - dep.depsTail!.nextDep = nextDep - dep.deps = undefined - dep.depsTail = undefined - continue - } + if (prevSub !== undefined) { + prevSub.nextSub = nextSub + link.prevSub = undefined + } else { + dep.subs = nextSub + } + + // @ts-expect-error + link.dep = undefined + // @ts-expect-error + link.sub = undefined + link.nextDep = linkPool + linkPool = link + + if (dep.subs === undefined && 'deps' in dep) { + const depFlags = dep.flags + if (!(depFlags & SubscriberFlags.Dirty)) { + dep.flags = depFlags | SubscriberFlags.Dirty } - link = nextDep! - } while (link !== undefined) - } + const depDeps = dep.deps + if (depDeps !== undefined) { + link = depDeps + dep.depsTail!.nextDep = nextDep + dep.deps = undefined + dep.depsTail = undefined + continue + } + } + link = nextDep! + } while (link !== undefined) } -import type { ComputedRefImpl } from './computed.js' -import type { ReactiveEffect } from './effect.js' - -const { - endTrack, - link, - isDirty, - processComputedUpdate, - processQueuedEffects, - propagate: _propagate, - startTrack, -} = createSystem({ - computed: { - is(sub): sub is ComputedRefImpl { - return 'update' in sub - }, - update(computed) { - return computed.update() - }, - }, - effect: { - is(sub): sub is ReactiveEffect { - return 'notify' in sub - }, - notify(effect) { - effect.notify() - return true - }, - }, -}) - -export { endTrack, link, isDirty, processComputedUpdate, startTrack } +import type { ComputedRefImpl as Computed } from './computed.js' +import type { ReactiveEffect as Effect } from './effect.js' let batchDepth = 0 @@ -483,9 +428,4 @@ export function endBatch(): void { } } -export function propagate(link: Link): void { - _propagate(link) - if (!batchDepth) { - processQueuedEffects() - } -} +export { endTrack, link, isDirty, propagate, processComputedUpdate, startTrack } From 01b49e7d7796c45a777c8d0df48cd3c445d62952 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 13 Jan 2025 02:27:04 +0800 Subject: [PATCH 25/30] Update system.ts --- packages/reactivity/src/system.ts | 37 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index ff1449d4fb7..eb7ca158be2 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -39,15 +39,15 @@ let queuedEffects: Effect | undefined let queuedEffectsTail: Effect | undefined let linkPool: Link | undefined -function link(dep: Dependency, sub: Subscriber): boolean { +function link(dep: Dependency, sub: Subscriber): void { const currentDep = sub.depsTail if (currentDep !== undefined && currentDep.dep === dep) { - return false + return } const nextDep = currentDep !== undefined ? currentDep.nextDep : sub.deps if (nextDep !== undefined && nextDep.dep === dep) { sub.depsTail = nextDep - return false + return } const depLastSub = dep.subsTail if ( @@ -55,10 +55,9 @@ function link(dep: Dependency, sub: Subscriber): boolean { depLastSub.sub === sub && _isValidLink(depLastSub, sub) ) { - return false + return } _linkNewDep(dep, sub, nextDep, currentDep) - return true } function _linkNewDep( @@ -119,20 +118,6 @@ function processQueuedEffects(): void { } } -function isDirty(sub: Subscriber, flags: SubscriberFlags): boolean { - if (flags & SubscriberFlags.Dirty) { - return true - } else if (flags & SubscriberFlags.CheckRequired) { - if (checkDirty(sub.deps!)) { - sub.flags = flags | SubscriberFlags.Dirty - return true - } else { - sub.flags &= ~SubscriberFlags.CheckRequired - } - } - return false -} - function processComputedUpdate( computed: Computed, flags: SubscriberFlags, @@ -147,6 +132,20 @@ function processComputedUpdate( } } +function isDirty(sub: Subscriber, flags: SubscriberFlags): boolean { + if (flags & SubscriberFlags.Dirty) { + return true + } else if (flags & SubscriberFlags.CheckRequired) { + if (checkDirty(sub.deps!)) { + sub.flags = flags | SubscriberFlags.Dirty + return true + } else { + sub.flags &= ~SubscriberFlags.CheckRequired + } + } + return false +} + // See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions function checkDirty(link: Link): boolean { let stack = 0 From 593998b5fa829e0524757db7e64969142f50eb02 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 13 Jan 2025 02:45:26 +0800 Subject: [PATCH 26/30] Sort code --- packages/reactivity/src/system.ts | 284 +++++++++++++++--------------- 1 file changed, 140 insertions(+), 144 deletions(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index eb7ca158be2..ba2f2d4ab6c 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -1,5 +1,7 @@ /* eslint-disable */ // Ported from https://github.com/stackblitz/alien-signals/blob/master/src/system.ts +import type { ComputedRefImpl as Computed } from './computed.js' +import type { ReactiveEffect as Effect } from './effect.js' export interface Dependency { subs: Link | undefined @@ -35,11 +37,38 @@ export const enum SubscriberFlags { Propagated = CheckRequired | Dirty, } +let batchDepth = 0 let queuedEffects: Effect | undefined let queuedEffectsTail: Effect | undefined let linkPool: Link | undefined -function link(dep: Dependency, sub: Subscriber): void { +export function startBatch(): void { + ++batchDepth +} + +export function endBatch(): void { + if (!--batchDepth) { + processQueuedEffects() + } +} + +function processQueuedEffects(): void { + while (queuedEffects !== undefined) { + const effect = queuedEffects + const depsTail = effect.depsTail! + const queuedNext = depsTail.nextDep + if (queuedNext !== undefined) { + depsTail.nextDep = undefined + queuedEffects = queuedNext.sub as Effect + } else { + queuedEffects = undefined + queuedEffectsTail = undefined + } + effect.notify() + } +} + +export function link(dep: Dependency, sub: Subscriber): void { const currentDep = sub.depsTail if (currentDep !== undefined && currentDep.dep === dep) { return @@ -102,23 +131,111 @@ function _linkNewDep( dep.subsTail = newLink } -function processQueuedEffects(): void { - while (queuedEffects !== undefined) { - const effect = queuedEffects - const depsTail = effect.depsTail! - const queuedNext = depsTail.nextDep - if (queuedNext !== undefined) { - depsTail.nextDep = undefined - queuedEffects = queuedNext.sub as Effect - } else { - queuedEffects = undefined - queuedEffectsTail = undefined +// See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions +export function propagate(link: Link): void { + let targetFlag = SubscriberFlags.Dirty + let subs = link + let stack = 0 + + top: do { + const sub = link.sub + const subFlags = sub.flags + + if ( + (!( + subFlags & + (SubscriberFlags.Tracking | + SubscriberFlags.Recursed | + SubscriberFlags.Propagated) + ) && + ((sub.flags = subFlags | targetFlag), true)) || + (subFlags & SubscriberFlags.Recursed && + !(subFlags & SubscriberFlags.Tracking) && + ((sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag), + true)) || + (!(subFlags & SubscriberFlags.Propagated) && + _isValidLink(link, sub) && + ((sub.flags = subFlags | SubscriberFlags.Recursed | targetFlag), + (sub as Dependency).subs !== undefined)) + ) { + const subSubs = (sub as Dependency).subs + if (subSubs !== undefined) { + if (subSubs.nextSub !== undefined) { + subSubs.prevSub = subs + link = subs = subSubs + targetFlag = SubscriberFlags.CheckRequired + ++stack + } else { + link = subSubs + targetFlag = SubscriberFlags.CheckRequired + } + continue + } + if ('notify' in sub) { + if (queuedEffectsTail !== undefined) { + queuedEffectsTail.depsTail!.nextDep = sub.deps + } else { + queuedEffects = sub + } + queuedEffectsTail = sub + } + } else if (!(subFlags & (SubscriberFlags.Tracking | targetFlag))) { + sub.flags = subFlags | targetFlag + } else if ( + !(subFlags & targetFlag) && + subFlags & SubscriberFlags.Propagated && + _isValidLink(link, sub) + ) { + sub.flags = subFlags | targetFlag } - effect.notify() + + if ((link = subs.nextSub!) !== undefined) { + subs = link + targetFlag = stack ? SubscriberFlags.CheckRequired : SubscriberFlags.Dirty + continue + } + + while (stack) { + --stack + const dep = subs.dep + const depSubs = dep.subs! + subs = depSubs.prevSub! + depSubs.prevSub = undefined + if ((link = subs.nextSub!) !== undefined) { + subs = link + targetFlag = stack + ? SubscriberFlags.CheckRequired + : SubscriberFlags.Dirty + continue top + } + } + + break + } while (true) + + if (!batchDepth) { + processQueuedEffects() } } -function processComputedUpdate( +function _isValidLink(subLink: Link, sub: Subscriber): boolean { + const depsTail = sub.depsTail + if (depsTail !== undefined) { + let link = sub.deps! + do { + if (link === subLink) { + return true + } + if (link === depsTail) { + break + } + link = link.nextDep! + } while (link !== undefined) + } + return false +} + +export function processComputedUpdate( computed: Computed, flags: SubscriberFlags, ): void { @@ -126,17 +243,17 @@ function processComputedUpdate( if (computed.update()) { const subs = computed.subs if (subs !== undefined) { - shallowPropagate(subs) + _shallowPropagate(subs) } } } } -function isDirty(sub: Subscriber, flags: SubscriberFlags): boolean { +export function isDirty(sub: Subscriber, flags: SubscriberFlags): boolean { if (flags & SubscriberFlags.Dirty) { return true } else if (flags & SubscriberFlags.CheckRequired) { - if (checkDirty(sub.deps!)) { + if (_checkDirty(sub.deps!)) { sub.flags = flags | SubscriberFlags.Dirty return true } else { @@ -147,7 +264,7 @@ function isDirty(sub: Subscriber, flags: SubscriberFlags): boolean { } // See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions -function checkDirty(link: Link): boolean { +function _checkDirty(link: Link): boolean { let stack = 0 let dirty: boolean @@ -161,7 +278,7 @@ function checkDirty(link: Link): boolean { if ('update' in dep && dep.update()) { const subs = dep.subs! if (subs.nextSub !== undefined) { - shallowPropagate(subs) + _shallowPropagate(subs) } dirty = true } @@ -193,7 +310,7 @@ function checkDirty(link: Link): boolean { if (sub.update()) { if ((link = subSubs.prevSub!) !== undefined) { subSubs.prevSub = undefined - shallowPropagate(sub.subs!) + _shallowPropagate(sub.subs!) sub = link.sub as Computed } else { sub = subSubs.sub as Computed @@ -226,7 +343,7 @@ function checkDirty(link: Link): boolean { } while (true) } -function shallowPropagate(link: Link): void { +function _shallowPropagate(link: Link): void { do { const sub = link.sub const subFlags = sub.flags @@ -240,118 +357,14 @@ function shallowPropagate(link: Link): void { } while (link !== undefined) } -// See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions -function propagate(link: Link): void { - let targetFlag = SubscriberFlags.Dirty - let subs = link - let stack = 0 - - top: do { - const sub = link.sub - const subFlags = sub.flags - - if ( - (!( - subFlags & - (SubscriberFlags.Tracking | - SubscriberFlags.Recursed | - SubscriberFlags.Propagated) - ) && - ((sub.flags = subFlags | targetFlag), true)) || - (subFlags & SubscriberFlags.Recursed && - !(subFlags & SubscriberFlags.Tracking) && - ((sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag), - true)) || - (!(subFlags & SubscriberFlags.Propagated) && - _isValidLink(link, sub) && - ((sub.flags = subFlags | SubscriberFlags.Recursed | targetFlag), - (sub as Dependency).subs !== undefined)) - ) { - const subSubs = (sub as Dependency).subs - if (subSubs !== undefined) { - if (subSubs.nextSub !== undefined) { - subSubs.prevSub = subs - link = subs = subSubs - targetFlag = SubscriberFlags.CheckRequired - ++stack - } else { - link = subSubs - targetFlag = SubscriberFlags.CheckRequired - } - continue - } - if ('notify' in sub) { - if (queuedEffectsTail !== undefined) { - queuedEffectsTail.depsTail!.nextDep = sub.deps - } else { - queuedEffects = sub - } - queuedEffectsTail = sub - } - } else if (!(subFlags & (SubscriberFlags.Tracking | targetFlag))) { - sub.flags = subFlags | targetFlag - } else if ( - !(subFlags & targetFlag) && - subFlags & SubscriberFlags.Propagated && - _isValidLink(link, sub) - ) { - sub.flags = subFlags | targetFlag - } - - if ((link = subs.nextSub!) !== undefined) { - subs = link - targetFlag = stack ? SubscriberFlags.CheckRequired : SubscriberFlags.Dirty - continue - } - - while (stack) { - --stack - const dep = subs.dep - const depSubs = dep.subs! - subs = depSubs.prevSub! - depSubs.prevSub = undefined - if ((link = subs.nextSub!) !== undefined) { - subs = link - targetFlag = stack - ? SubscriberFlags.CheckRequired - : SubscriberFlags.Dirty - continue top - } - } - - break - } while (true) - - if (!batchDepth) { - processQueuedEffects() - } -} - -function _isValidLink(subLink: Link, sub: Subscriber): boolean { - const depsTail = sub.depsTail - if (depsTail !== undefined) { - let link = sub.deps! - do { - if (link === subLink) { - return true - } - if (link === depsTail) { - break - } - link = link.nextDep! - } while (link !== undefined) - } - return false -} - -function startTrack(sub: Subscriber): void { +export function startTrack(sub: Subscriber): void { sub.depsTail = undefined sub.flags = (sub.flags & ~(SubscriberFlags.Recursed | SubscriberFlags.Propagated)) | SubscriberFlags.Tracking } -function endTrack(sub: Subscriber): void { +export function endTrack(sub: Subscriber): void { const depsTail = sub.depsTail if (depsTail !== undefined) { const nextDep = depsTail.nextDep @@ -411,20 +424,3 @@ function _clearTrack(link: Link): void { link = nextDep! } while (link !== undefined) } - -import type { ComputedRefImpl as Computed } from './computed.js' -import type { ReactiveEffect as Effect } from './effect.js' - -let batchDepth = 0 - -export function startBatch(): void { - ++batchDepth -} - -export function endBatch(): void { - if (!--batchDepth) { - processQueuedEffects() - } -} - -export { endTrack, link, isDirty, propagate, processComputedUpdate, startTrack } From 9aba1efc280681e62f46ab95379b726f51d650c2 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Mon, 13 Jan 2025 02:51:14 +0800 Subject: [PATCH 27/30] Revert tsconfig.json --- tsconfig.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index e56ba6fff6c..9896083cf14 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,7 +13,7 @@ "noUnusedLocals": true, "experimentalDecorators": true, "resolveJsonModule": true, - // "isolatedModules": true, + "isolatedModules": true, "skipLibCheck": true, "esModuleInterop": true, "removeComments": false, @@ -26,7 +26,7 @@ "@vue/*": ["packages/*/src"], "vue": ["packages/vue/src"] }, - // "isolatedDeclarations": true, + "isolatedDeclarations": true, "composite": true }, "include": [ From acab975f5dca893e4d0dd340e94f5e8956769bff Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 15 Jan 2025 03:02:39 +0800 Subject: [PATCH 28/30] Sync v1.0.0 --- .../reactivity/__tests__/computed.spec.ts | 4 +- packages/reactivity/src/computed.ts | 24 +- packages/reactivity/src/debug.ts | 4 +- packages/reactivity/src/effect.ts | 33 +- packages/reactivity/src/effectScope.ts | 11 +- packages/reactivity/src/system.ts | 300 +++++++++--------- .../__tests__/apiSetupHelpers.spec.ts | 4 +- 7 files changed, 203 insertions(+), 177 deletions(-) diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index 847520a3177..db2984cc1ef 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -468,10 +468,10 @@ describe('reactivity/computed', () => { c2.value expect( - c1.flags & (SubscriberFlags.Dirty | SubscriberFlags.CheckRequired), + c1.flags & (SubscriberFlags.Dirty | SubscriberFlags.PendingComputed), ).toBe(0) expect( - c2.flags & (SubscriberFlags.Dirty | SubscriberFlags.CheckRequired), + c2.flags & (SubscriberFlags.Dirty | SubscriberFlags.PendingComputed), ).toBe(0) }) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 5950f021485..2d1be75b76d 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -14,11 +14,11 @@ import { type Link, type Subscriber, SubscriberFlags, - endTrack, - isDirty, + endTracking, link, processComputedUpdate, - startTrack, + startTracking, + updateDirtyFlag, } from './system' import { warn } from './warning' @@ -66,7 +66,7 @@ export class ComputedRefImpl implements Dependency, Subscriber { // Subscriber deps: Link | undefined = undefined depsTail: Link | undefined = undefined - flags: SubscriberFlags = SubscriberFlags.Dirty + flags: SubscriberFlags = SubscriberFlags.Computed | SubscriberFlags.Dirty /** * @internal @@ -89,13 +89,21 @@ export class ComputedRefImpl implements Dependency, Subscriber { } // for backwards compat get _dirty(): boolean { - return isDirty(this, this.flags) + const flags = this.flags + if ( + flags & SubscriberFlags.Dirty || + (flags & SubscriberFlags.PendingComputed && + updateDirtyFlag(this, this.flags)) + ) { + return true + } + return false } set _dirty(v: boolean) { if (v) { this.flags |= SubscriberFlags.Dirty } else { - this.flags &= ~(SubscriberFlags.Dirty | SubscriberFlags.CheckRequired) + this.flags &= ~(SubscriberFlags.Dirty | SubscriberFlags.PendingComputed) } } @@ -148,7 +156,7 @@ export class ComputedRefImpl implements Dependency, Subscriber { update(): boolean { const prevSub = activeSub setActiveSub(this) - startTrack(this) + startTracking(this) try { const oldValue = this._value const newValue = this.fn(oldValue) @@ -159,7 +167,7 @@ export class ComputedRefImpl implements Dependency, Subscriber { return false } finally { setActiveSub(prevSub) - endTrack(this) + endTracking(this) } } } diff --git a/packages/reactivity/src/debug.ts b/packages/reactivity/src/debug.ts index d6db3438019..7e96f24ea2f 100644 --- a/packages/reactivity/src/debug.ts +++ b/packages/reactivity/src/debug.ts @@ -71,9 +71,9 @@ function setupFlagsHandler(target: Subscriber): void { if ( !( (target as any)._flags & - (SubscriberFlags.CheckRequired | SubscriberFlags.Dirty) + (SubscriberFlags.PendingComputed | SubscriberFlags.Dirty) ) && - !!(value & (SubscriberFlags.CheckRequired | SubscriberFlags.Dirty)) + !!(value & (SubscriberFlags.PendingComputed | SubscriberFlags.Dirty)) ) { onTrigger(this) } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index c411928d0d8..a77c4bf2b18 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -6,9 +6,9 @@ import { type Link, type Subscriber, SubscriberFlags, - endTrack, - isDirty, - startTrack, + endTracking, + startTracking, + updateDirtyFlag, } from './system' import { warn } from './warning' @@ -46,17 +46,17 @@ export enum EffectFlags { /** * ReactiveEffect only */ - ALLOW_RECURSE = 1 << 6, - PAUSED = 1 << 7, - NOTIFIED = 1 << 8, - STOP = 1 << 9, + ALLOW_RECURSE = 1 << 7, + PAUSED = 1 << 8, + NOTIFIED = 1 << 9, + STOP = 1 << 10, } export class ReactiveEffect implements ReactiveEffectOptions { // Subscriber deps: Link | undefined = undefined depsTail: Link | undefined = undefined - flags: number = 0 + flags: number = SubscriberFlags.Effect /** * @internal @@ -119,7 +119,7 @@ export class ReactiveEffect implements ReactiveEffectOptions { cleanupEffect(this) const prevSub = activeSub setActiveSub(this) - startTrack(this) + startTracking(this) try { return this.fn() @@ -131,7 +131,7 @@ export class ReactiveEffect implements ReactiveEffectOptions { ) } setActiveSub(prevSub) - endTrack(this) + endTracking(this) if ( this.flags & SubscriberFlags.Recursed && this.flags & EffectFlags.ALLOW_RECURSE @@ -144,8 +144,8 @@ export class ReactiveEffect implements ReactiveEffectOptions { stop(): void { if (this.active) { - startTrack(this) - endTrack(this) + startTracking(this) + endTracking(this) cleanupEffect(this) this.onStop && this.onStop() this.flags |= EffectFlags.STOP @@ -153,7 +153,14 @@ export class ReactiveEffect implements ReactiveEffectOptions { } get dirty(): boolean { - return isDirty(this, this.flags) + const flags = this.flags + if ( + flags & SubscriberFlags.Dirty || + (flags & SubscriberFlags.PendingComputed && updateDirtyFlag(this, flags)) + ) { + return true + } + return false } } diff --git a/packages/reactivity/src/effectScope.ts b/packages/reactivity/src/effectScope.ts index de84be62d98..b7d43152867 100644 --- a/packages/reactivity/src/effectScope.ts +++ b/packages/reactivity/src/effectScope.ts @@ -2,9 +2,8 @@ import { EffectFlags, type ReactiveEffect } from './effect' import { type Link, type Subscriber, - SubscriberFlags, - endTrack, - startTrack, + endTracking, + startTracking, } from './system' import { warn } from './warning' @@ -14,7 +13,7 @@ export class EffectScope implements Subscriber { // Subscriber: In order to collect orphans computeds deps: Link | undefined = undefined depsTail: Link | undefined = undefined - flags: number = SubscriberFlags.None + flags: number = 0 /** * @internal @@ -122,8 +121,8 @@ export class EffectScope implements Subscriber { stop(fromParent?: boolean): void { if (this.active) { this.flags |= EffectFlags.STOP - startTrack(this) - endTrack(this) + startTracking(this) + endTracking(this) let i, l for (i = 0, l = this.effects.length; i < l; i++) { this.effects[i].stop() diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index ba2f2d4ab6c..c5c59fff7b2 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -17,24 +17,19 @@ export interface Subscriber { export interface Link { dep: Dependency | Computed sub: Subscriber | Computed | Effect - // Reuse to link prev stack in checkDirty - // Reuse to link prev stack in propagate prevSub: Link | undefined nextSub: Link | undefined - // Reuse to link next released link in linkPool - // Reuse to link notify effect in queuedEffects nextDep: Link | undefined } export const enum SubscriberFlags { - None = 0, - Tracking = 1 << 0, - // Notified = 1 << 1, - Recursed = 1 << 2, - // PendingInnerEffects = 1 << 3, - CheckRequired = 1 << 4, + Computed = 1 << 0, + Effect = 1 << 1, + Tracking = 1 << 2, + Recursed = 1 << 4, Dirty = 1 << 5, - Propagated = CheckRequired | Dirty, + PendingComputed = 1 << 6, + Propagated = Dirty | PendingComputed, } let batchDepth = 0 @@ -48,27 +43,11 @@ export function startBatch(): void { export function endBatch(): void { if (!--batchDepth) { - processQueuedEffects() + processEffectNotifications() } } -function processQueuedEffects(): void { - while (queuedEffects !== undefined) { - const effect = queuedEffects - const depsTail = effect.depsTail! - const queuedNext = depsTail.nextDep - if (queuedNext !== undefined) { - depsTail.nextDep = undefined - queuedEffects = queuedNext.sub as Effect - } else { - queuedEffects = undefined - queuedEffectsTail = undefined - } - effect.notify() - } -} - -export function link(dep: Dependency, sub: Subscriber): void { +export function link(dep: Dependency, sub: Subscriber): Link | undefined { const currentDep = sub.depsTail if (currentDep !== undefined && currentDep.dep === dep) { return @@ -82,56 +61,13 @@ export function link(dep: Dependency, sub: Subscriber): void { if ( depLastSub !== undefined && depLastSub.sub === sub && - _isValidLink(depLastSub, sub) + isValidLink(depLastSub, sub) ) { return } - _linkNewDep(dep, sub, nextDep, currentDep) -} - -function _linkNewDep( - dep: Dependency, - sub: Subscriber, - nextDep: Link | undefined, - depsTail: Link | undefined, -): void { - let newLink: Link - - if (linkPool !== undefined) { - newLink = linkPool - linkPool = newLink.nextDep - newLink.nextDep = nextDep - newLink.dep = dep - newLink.sub = sub - } else { - newLink = { - dep, - sub, - nextDep, - prevSub: undefined, - nextSub: undefined, - } - } - - if (depsTail === undefined) { - sub.deps = newLink - } else { - depsTail.nextDep = newLink - } - - if (dep.subs === undefined) { - dep.subs = newLink - } else { - const oldTail = dep.subsTail! - newLink.prevSub = oldTail - oldTail.nextSub = newLink - } - - sub.depsTail = newLink - dep.subsTail = newLink + return linkNewDep(dep, sub, nextDep, currentDep) } -// See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions export function propagate(link: Link): void { let targetFlag = SubscriberFlags.Dirty let subs = link @@ -154,7 +90,7 @@ export function propagate(link: Link): void { ((sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag), true)) || (!(subFlags & SubscriberFlags.Propagated) && - _isValidLink(link, sub) && + isValidLink(link, sub) && ((sub.flags = subFlags | SubscriberFlags.Recursed | targetFlag), (sub as Dependency).subs !== undefined)) ) { @@ -163,35 +99,37 @@ export function propagate(link: Link): void { if (subSubs.nextSub !== undefined) { subSubs.prevSub = subs link = subs = subSubs - targetFlag = SubscriberFlags.CheckRequired + targetFlag = SubscriberFlags.PendingComputed ++stack } else { link = subSubs - targetFlag = SubscriberFlags.CheckRequired + targetFlag = SubscriberFlags.PendingComputed } continue } - if ('notify' in sub) { + if (subFlags & SubscriberFlags.Effect) { if (queuedEffectsTail !== undefined) { queuedEffectsTail.depsTail!.nextDep = sub.deps } else { - queuedEffects = sub + queuedEffects = sub as Effect } - queuedEffectsTail = sub + queuedEffectsTail = sub as Effect } } else if (!(subFlags & (SubscriberFlags.Tracking | targetFlag))) { sub.flags = subFlags | targetFlag } else if ( !(subFlags & targetFlag) && subFlags & SubscriberFlags.Propagated && - _isValidLink(link, sub) + isValidLink(link, sub) ) { sub.flags = subFlags | targetFlag } if ((link = subs.nextSub!) !== undefined) { subs = link - targetFlag = stack ? SubscriberFlags.CheckRequired : SubscriberFlags.Dirty + targetFlag = stack + ? SubscriberFlags.PendingComputed + : SubscriberFlags.Dirty continue } @@ -204,7 +142,7 @@ export function propagate(link: Link): void { if ((link = subs.nextSub!) !== undefined) { subs = link targetFlag = stack - ? SubscriberFlags.CheckRequired + ? SubscriberFlags.PendingComputed : SubscriberFlags.Dirty continue top } @@ -214,57 +152,131 @@ export function propagate(link: Link): void { } while (true) if (!batchDepth) { - processQueuedEffects() + processEffectNotifications() } } -function _isValidLink(subLink: Link, sub: Subscriber): boolean { +export function startTracking(sub: Subscriber): void { + sub.depsTail = undefined + sub.flags = + (sub.flags & ~(SubscriberFlags.Recursed | SubscriberFlags.Propagated)) | + SubscriberFlags.Tracking +} + +export function endTracking(sub: Subscriber): void { const depsTail = sub.depsTail if (depsTail !== undefined) { - let link = sub.deps! - do { - if (link === subLink) { - return true - } - if (link === depsTail) { - break - } - link = link.nextDep! - } while (link !== undefined) + const nextDep = depsTail.nextDep + if (nextDep !== undefined) { + clearTracking(nextDep) + depsTail.nextDep = undefined + } + } else if (sub.deps !== undefined) { + clearTracking(sub.deps) + sub.deps = undefined + } + sub.flags &= ~SubscriberFlags.Tracking +} + +export function updateDirtyFlag( + sub: Subscriber, + flags: SubscriberFlags, +): boolean { + if (checkDirty(sub.deps!)) { + sub.flags = flags | SubscriberFlags.Dirty + return true + } else { + sub.flags = flags & ~SubscriberFlags.PendingComputed + return false } - return false } export function processComputedUpdate( computed: Computed, flags: SubscriberFlags, ): void { - if (isDirty(computed, flags)) { + if (flags & SubscriberFlags.Dirty) { if (computed.update()) { const subs = computed.subs if (subs !== undefined) { - _shallowPropagate(subs) + shallowPropagate(subs) + } + } + } else if (flags & SubscriberFlags.PendingComputed) { + if (checkDirty(computed.deps!)) { + if (computed.update()) { + const subs = computed.subs + if (subs !== undefined) { + shallowPropagate(subs) + } } + } else { + computed.flags = flags & ~SubscriberFlags.PendingComputed } } } -export function isDirty(sub: Subscriber, flags: SubscriberFlags): boolean { - if (flags & SubscriberFlags.Dirty) { - return true - } else if (flags & SubscriberFlags.CheckRequired) { - if (_checkDirty(sub.deps!)) { - sub.flags = flags | SubscriberFlags.Dirty - return true +export function processEffectNotifications(): void { + while (queuedEffects !== undefined) { + const effect = queuedEffects + const depsTail = effect.depsTail! + const queuedNext = depsTail.nextDep + if (queuedNext !== undefined) { + depsTail.nextDep = undefined + queuedEffects = queuedNext.sub as Effect } else { - sub.flags &= ~SubscriberFlags.CheckRequired + queuedEffects = undefined + queuedEffectsTail = undefined } + effect.notify() } - return false } -// See https://github.com/stackblitz/alien-signals#about-propagate-and-checkdirty-functions -function _checkDirty(link: Link): boolean { +function linkNewDep( + dep: Dependency, + sub: Subscriber, + nextDep: Link | undefined, + depsTail: Link | undefined, +): Link { + let newLink: Link + + if (linkPool !== undefined) { + newLink = linkPool + linkPool = newLink.nextDep + newLink.nextDep = nextDep + newLink.dep = dep + newLink.sub = sub + } else { + newLink = { + dep, + sub, + nextDep, + prevSub: undefined, + nextSub: undefined, + } + } + + if (depsTail === undefined) { + sub.deps = newLink + } else { + depsTail.nextDep = newLink + } + + if (dep.subs === undefined) { + dep.subs = newLink + } else { + const oldTail = dep.subsTail! + newLink.prevSub = oldTail + oldTail.nextSub = newLink + } + + sub.depsTail = newLink + dep.subsTail = newLink + + return newLink +} + +function checkDirty(link: Link): boolean { let stack = 0 let dirty: boolean @@ -274,24 +286,29 @@ function _checkDirty(link: Link): boolean { if ('flags' in dep) { const depFlags = dep.flags - if (depFlags & SubscriberFlags.Dirty) { - if ('update' in dep && dep.update()) { + if ( + (depFlags & (SubscriberFlags.Computed | SubscriberFlags.Dirty)) === + (SubscriberFlags.Computed | SubscriberFlags.Dirty) + ) { + if ((dep as Computed).update()) { const subs = dep.subs! if (subs.nextSub !== undefined) { - _shallowPropagate(subs) + shallowPropagate(subs) } dirty = true } - } else if (depFlags & SubscriberFlags.CheckRequired) { - if ('update' in dep) { - const depSubs = dep.subs! - if (depSubs.nextSub !== undefined) { - depSubs.prevSub = link - } - link = dep.deps! - ++stack - continue + } else if ( + (depFlags & + (SubscriberFlags.Computed | SubscriberFlags.PendingComputed)) === + (SubscriberFlags.Computed | SubscriberFlags.PendingComputed) + ) { + const depSubs = dep.subs! + if (depSubs.nextSub !== undefined) { + depSubs.prevSub = link } + link = dep.deps! + ++stack + continue } } @@ -310,7 +327,7 @@ function _checkDirty(link: Link): boolean { if (sub.update()) { if ((link = subSubs.prevSub!) !== undefined) { subSubs.prevSub = undefined - _shallowPropagate(sub.subs!) + shallowPropagate(sub.subs!) sub = link.sub as Computed } else { sub = subSubs.sub as Computed @@ -318,7 +335,7 @@ function _checkDirty(link: Link): boolean { continue } } else { - sub.flags &= ~SubscriberFlags.CheckRequired + sub.flags &= ~SubscriberFlags.PendingComputed } if ((link = subSubs.prevSub!) !== undefined) { @@ -343,13 +360,13 @@ function _checkDirty(link: Link): boolean { } while (true) } -function _shallowPropagate(link: Link): void { +function shallowPropagate(link: Link): void { do { const sub = link.sub const subFlags = sub.flags if ( - (subFlags & (SubscriberFlags.CheckRequired | SubscriberFlags.Dirty)) === - SubscriberFlags.CheckRequired + (subFlags & (SubscriberFlags.PendingComputed | SubscriberFlags.Dirty)) === + SubscriberFlags.PendingComputed ) { sub.flags = subFlags | SubscriberFlags.Dirty } @@ -357,29 +374,24 @@ function _shallowPropagate(link: Link): void { } while (link !== undefined) } -export function startTrack(sub: Subscriber): void { - sub.depsTail = undefined - sub.flags = - (sub.flags & ~(SubscriberFlags.Recursed | SubscriberFlags.Propagated)) | - SubscriberFlags.Tracking -} - -export function endTrack(sub: Subscriber): void { +function isValidLink(checkLink: Link, sub: Subscriber): boolean { const depsTail = sub.depsTail if (depsTail !== undefined) { - const nextDep = depsTail.nextDep - if (nextDep !== undefined) { - _clearTrack(nextDep) - depsTail.nextDep = undefined - } - } else if (sub.deps !== undefined) { - _clearTrack(sub.deps) - sub.deps = undefined + let link = sub.deps! + do { + if (link === checkLink) { + return true + } + if (link === depsTail) { + break + } + link = link.nextDep! + } while (link !== undefined) } - sub.flags &= ~SubscriberFlags.Tracking + return false } -function _clearTrack(link: Link): void { +function clearTracking(link: Link): void { do { const dep = link.dep const nextDep = link.nextDep diff --git a/packages/runtime-core/__tests__/apiSetupHelpers.spec.ts b/packages/runtime-core/__tests__/apiSetupHelpers.spec.ts index 5cc5a21caf0..aa37bc4f49f 100644 --- a/packages/runtime-core/__tests__/apiSetupHelpers.spec.ts +++ b/packages/runtime-core/__tests__/apiSetupHelpers.spec.ts @@ -454,11 +454,11 @@ describe('SFC