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 +}