From: daiwei Date: Fri, 4 Jul 2025 05:52:08 +0000 (+0800) Subject: chore: Merge branch 'main' into minor X-Git-Tag: v3.6.0-alpha.1~20 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=be178f876caad694ebbb1fd0a88a2c7cdabe361e;p=thirdparty%2Fvuejs%2Fcore.git chore: Merge branch 'main' into minor --- be178f876caad694ebbb1fd0a88a2c7cdabe361e diff --cc packages/reactivity/__tests__/readonly.spec.ts index 9acd5c6491,b035779f85..5c70b6082f --- a/packages/reactivity/__tests__/readonly.spec.ts +++ b/packages/reactivity/__tests__/readonly.spec.ts @@@ -520,3 -522,16 +522,16 @@@ describe('reactivity/readonly', () => expect(r.value).toBe(ro) }) }) + -test('should be able to trigger with triggerRef', () => { ++test.todo('should be able to trigger with triggerRef', () => { + const r = shallowRef({ a: 1 }) + const ror = readonly(r) + let dummy + effect(() => { + dummy = ror.value.a + }) + r.value.a = 2 + expect(dummy).toBe(1) + triggerRef(ror) + expect(dummy).toBe(2) + }) diff --cc packages/reactivity/src/dep.ts index 184964c17b,8ad8a389ba..3fd81514b6 --- a/packages/reactivity/src/dep.ts +++ b/packages/reactivity/src/dep.ts @@@ -1,34 -1,233 +1,40 @@@ -import { extend, isArray, isIntegerKey, isMap, isSymbol } from '@vue/shared' -import type { ComputedRefImpl } from './computed' +import { isArray, isIntegerKey, isMap, isSymbol } from '@vue/shared' import { type TrackOpTypes, TriggerOpTypes } from './constants' +import { onTrack, triggerEventInfos } from './debug' +import { activeSub } from './effect' import { - type DebuggerEventExtraInfo, - EffectFlags, - type Subscriber, - activeSub, + type Dependency, + type Link, endBatch, - shouldTrack, + link, + propagate, startBatch, -} from './effect' +} from './system' -/** - * Incremented every time a reactive change happens - * This is used to give computed a fast path to avoid re-compute when nothing - * has changed. - */ -export let globalVersion = 0 - -/** - * Represents a link between a source (Dep) and a subscriber (Effect or Computed). - * Deps and subs have a many-to-many relationship - each link between a - * dep and a sub is represented by a Link instance. - * - * A Link is also a node in two doubly-linked lists - one for the associated - * sub to track all its deps, and one for the associated dep to track all its - * subs. - * - * @internal - */ -export class Link { - /** - * - Before each effect run, all previous dep links' version are reset to -1 - * - During the run, a link's version is synced with the source dep on access - * - After the run, links with version -1 (that were never used) are cleaned - * up - */ - version: number - - /** - * Pointers for doubly-linked lists - */ - nextDep?: Link - prevDep?: Link - nextSub?: Link - prevSub?: Link - prevActiveLink?: Link - - constructor( - public sub: Subscriber, - public dep: Dep, - ) { - this.version = dep.version - this.nextDep = - this.prevDep = - this.nextSub = - this.prevSub = - this.prevActiveLink = - undefined - } -} - -/** - * @internal - */ -export class Dep { - version = 0 - /** - * Link between this dep and the current active effect - */ - activeLink?: Link = undefined - - /** - * Doubly linked list representing the subscribing effects (tail) - */ - subs?: Link = undefined - - /** - * Doubly linked list representing the subscribing effects (head) - * DEV only, for invoking onTrigger hooks in correct order - */ - subsHead?: Link - - /** - * For object property deps cleanup - */ - map?: KeyToDepMap = undefined - key?: unknown = undefined - - /** - * Subscriber counter - */ - sc: number = 0 +class Dep implements Dependency { + _subs: Link | undefined = undefined + subsTail: Link | undefined = undefined + /** + * @internal + */ + readonly __v_skip = true + // TODO isolatedDeclarations ReactiveFlags.SKIP + - constructor(public computed?: ComputedRefImpl | undefined) { - if (__DEV__) { - this.subsHead = undefined - } - } - - track(debugInfo?: DebuggerEventExtraInfo): Link | undefined { - if (!activeSub || !shouldTrack || activeSub === this.computed) { - return - } - - let link = this.activeLink - if (link === undefined || link.sub !== activeSub) { - link = this.activeLink = new Link(activeSub, this) - - // add the link to the activeEffect as a dep (as tail) - if (!activeSub.deps) { - activeSub.deps = activeSub.depsTail = link - } else { - link.prevDep = activeSub.depsTail - activeSub.depsTail!.nextDep = link - activeSub.depsTail = link - } - - addSub(link) - } else if (link.version === -1) { - // reused from last run - already a sub, just sync version - link.version = this.version - - // If this dep has a next, it means it's not at the tail - move it to the - // tail. This ensures the effect's dep list is in the order they are - // accessed during evaluation. - if (link.nextDep) { - const next = link.nextDep - next.prevDep = link.prevDep - if (link.prevDep) { - link.prevDep.nextDep = next - } - - link.prevDep = activeSub.depsTail - link.nextDep = undefined - activeSub.depsTail!.nextDep = link - activeSub.depsTail = link - - // this was the head - point to the new head - if (activeSub.deps === link) { - activeSub.deps = next - } - } - } - - if (__DEV__ && activeSub.onTrack) { - activeSub.onTrack( - extend( - { - effect: activeSub, - }, - debugInfo, - ), - ) - } - - return link - } - - trigger(debugInfo?: DebuggerEventExtraInfo): void { - this.version++ - globalVersion++ - this.notify(debugInfo) - } + constructor( + private map: KeyToDepMap, + private key: unknown, + ) {} - notify(debugInfo?: DebuggerEventExtraInfo): void { - startBatch() - try { - if (__DEV__) { - // subs are notified and batched in reverse-order and then invoked in - // original order at the end of the batch, but onTrigger hooks should - // be invoked in original order here. - for (let head = this.subsHead; head; head = head.nextSub) { - if (head.sub.onTrigger && !(head.sub.flags & EffectFlags.NOTIFIED)) { - head.sub.onTrigger( - extend( - { - effect: head.sub, - }, - debugInfo, - ), - ) - } - } - } - for (let link = this.subs; link; link = link.prevSub) { - if (link.sub.notify()) { - // if notify() returns `true`, this is a computed. Also call notify - // on its dep - it's called here instead of inside computed's notify - // in order to reduce call stack depth. - ;(link.sub as ComputedRefImpl).dep.notify() - } - } - } finally { - endBatch() - } + get subs(): Link | undefined { + return this._subs } -} -function addSub(link: Link) { - link.dep.sc++ - if (link.sub.flags & EffectFlags.TRACKING) { - const computed = link.dep.computed - // computed getting its first subscriber - // enable tracking + lazily subscribe to all its deps - if (computed && !link.dep.subs) { - computed.flags |= EffectFlags.TRACKING | EffectFlags.DIRTY - for (let l = computed.deps; l; l = l.nextDep) { - addSub(l) - } - } - - const currentTail = link.dep.subs - if (currentTail !== link) { - link.prevSub = currentTail - if (currentTail) currentTail.nextSub = link - } - - if (__DEV__ && link.dep.subsHead === undefined) { - link.dep.subsHead = link + set subs(value: Link | undefined) { + this._subs = value + if (value === undefined) { + this.map.delete(this.key) } - - link.dep.subs = link } }