Vue3 响应式系统核心执行器:Reactive Effect 与依赖调度机制

js 复制代码
```js
 ┌─────────────────┐
 │ reactive.ts     │   ← 负责对象/数组/Ref 的响应式代理
 │ - track()   → 收集依赖
 │ - trigger() → 触发更新
 └─────────┬───────┘
           │ 调用
           ▼
 ┌─────────────────┐
 │ dep.ts          │   ← 管理单个依赖 Dep(存放订阅它的 effect)
 │ - Link 双向链表(Dep.deps 与 Effect.deps 双向绑定)
 │ - addSub()     → 添加 effect
 │ - removeSub()  → 移除 effect
 └─────────┬───────┘
           │ 调用
           ▼
 ┌───────────────────────────────────────────────┐
 │ effect.ts                                      │
 │   核心:ReactiveEffect 类,封装副作用函数逻辑     │
 │                                                │
 │ ReactiveEffect 类                               │
 │  - run()             → 执行副作用函数,触发依赖收集 │
 │  - trigger()         → 外部触发更新,决定是否重新运行 │
 │  - notify()          → 将 effect 加入批处理队列     │
 │  - stop()            → 停止 effect,解绑所有依赖     │
 │  - pause()/resume()  → 暂停/恢复依赖收集            │
 │                                                │
 │ 工具函数                                         │
 │  - prepareDeps()     → 标记当前依赖,等待收集       │
 │  - cleanupDeps()     → 清理失效的依赖关系          │
 │  - cleanupEffect()   → 执行注册的清理回调函数       │
 │  - isDirty()         → 判断 computed 是否过期       │
 │  - removeSub()       → 从 dep 移除 effect           │
 │  - removeDep()       → 从 effect 移除 dep           │
 │                                                │
 │ 调度机制                                         │
 │  - batch() / endBatch() → 批量执行 effect           │
 │  - runIfDirty()       → 脏检查后决定是否执行        │
 │                                                │
 │ 追踪控制                                         │
 │  - pauseTracking()    → 暂停依赖收集               │
 │  - enableTracking()   → 开启依赖收集               │
 │  - resetTracking()    → 恢复追踪栈状态             │
 │                                                │
 │ 外部接口                                         │
 │  - effect(fn)         → 创建并运行 ReactiveEffect   │
 │  - stop(runner)       → 停止某个 effect             │
 │  - onEffectCleanup(fn)→ 注册 effect 销毁时回调      │
 └─────────┬─────────────────────────────────────┘
           │ 关联
           ▼
 ┌──────────────────────────┐
 │ computed.ts              │ ← 基于 effect 实现的特殊 effect
 │ - ComputedRefImpl        → 使用 ReactiveEffect 封装 getter
 │ - refreshComputed()      → 调用 effect.run() 刷新缓存
 └──────────────────────────┘
kotlin 复制代码
```js
// -----------------------------
// Imports / Types
// -----------------------------

// EN: import helper utilities: `extend` to copy properties, `hasChanged` to compare values.
// CN: 导入工具函数:`extend` 用于合并/复制属性,`hasChanged` 用于比较值是否变化。
import { extend, hasChanged } from '@vue/shared'

// EN: import the ComputedRefImpl type to allow refreshComputed to access internals.
// CN: 导入 ComputedRefImpl 类型,使 refreshComputed 能访问 computed 的内部字段。
import type { ComputedRefImpl } from './computed'

// EN: import types for track/trigger op kinds (used in debugger event types).
// CN: 导入 track/trigger 操作类型,用于调试器事件类型定义。
import type { TrackOpTypes, TriggerOpTypes } from './constants'

// EN: Link type and globalVersion used by dep implementation.
// CN: Link 类型与 globalVersion,供 dep 实现使用。
import { type Link, globalVersion } from './dep'

// EN: activeEffectScope is used to register created effects to a scope for cleanup.
// CN: activeEffectScope 用于将创建的 effect 注册到当前的 effectScope,便于后续集中清理。
import { activeEffectScope } from './effectScope'

// EN: warn helper for dev-only warnings.
// CN: 开发环境使用的警告函数。
import { warn } from './warning'

// -----------------------------
// Type aliases & interfaces
// -----------------------------

// EN: Scheduler type for custom scheduling of effect runs.
// CN: effect 调度器类型,允许用户自定义何时执行 effect。
export type EffectScheduler = (...args: any[]) => any

// EN: Debugger event contains the effect (subscriber) plus extra info about the operation.
// CN: 调试事件,包含 effect(subscriber)以及关于操作的额外信息。
export type DebuggerEvent = { effect: Subscriber } & DebuggerEventExtraInfo

export type DebuggerEventExtraInfo = {
  target: object
  type: TrackOpTypes | TriggerOpTypes
  key: any
  newValue?: any
  oldValue?: any
  oldTarget?: Map<any, any> | Set<any>
}

// EN: Debugger hooks for track/trigger events.
// CN: 调试选项:在 track/trigger 时会调用的钩子。
export interface DebuggerOptions {
  onTrack?: (event: DebuggerEvent) => void
  onTrigger?: (event: DebuggerEvent) => void
}

// EN: Options for ReactiveEffect: scheduler, allowRecuse, onStop, plus debugger hooks.
// CN: ReactiveEffect 可配置项:scheduler、allowRecurse、onStop,以及调试钩子。
export interface ReactiveEffectOptions extends DebuggerOptions {
  scheduler?: EffectScheduler
  allowRecurse?: boolean
  onStop?: () => void
}

// EN: Runner function returned by `effect`, with a reference to its effect instance.
// CN: `effect` 返回的 runner 函数类型,同时包含 `.effect` 指向 ReactiveEffect 实例。
export interface ReactiveEffectRunner<T = any> {
  (): T
  effect: ReactiveEffect
}

// EN: The active subscriber (effect/computed) at runtime.
// CN: 当前处于活跃状态的订阅者(可能是 effect 或 computed)。
export let activeSub: Subscriber | undefined

// -----------------------------
// Effect Flags (bitmask) - compact state flags
// -----------------------------

// EN: Bit flags to track effect/computed state efficiently.
// CN: 使用位掩码表示 effect 的各种状态,便于快速判断与组合状态。
export enum EffectFlags {
  /** ReactiveEffect only */
  ACTIVE = 1 << 0,
  RUNNING = 1 << 1,
  TRACKING = 1 << 2,
  NOTIFIED = 1 << 3,
  DIRTY = 1 << 4,
  ALLOW_RECURSE = 1 << 5,
  PAUSED = 1 << 6,
  EVALUATED = 1 << 7,
}

// -----------------------------
// Subscriber interface (internal)
// -----------------------------

// EN: Subscriber represents anything that subscribes to deps (effects, computed).
// CN: Subscriber 表示订阅依赖的实体(例如 effect 或 computed)。
export interface Subscriber extends DebuggerOptions {
  /** Head of the doubly linked list representing the deps @internal */
  deps?: Link
  /** Tail of the same list @internal */
  depsTail?: Link
  /** @internal */
  flags: EffectFlags
  /** @internal */
  next?: Subscriber
  /** returning `true` indicates it's a computed that needs to call notify on its dep too @internal */
  notify(): true | void
}

// -----------------------------
// ReactiveEffect class
// -----------------------------

// EN: A set to collect effects that were paused and should be triggered once resumed.
// CN: 用于缓存处于 PAUSED 状态且等待恢复后触发的 effects 的集合(弱引用,方便 GC)。
const pausedQueueEffects = new WeakSet<ReactiveEffect>()

// EN: ReactiveEffect implements Subscriber and holds effect runtime data and methods.
// CN: ReactiveEffect 实现 Subscriber,保存运行时数据与方法(run/stop/trigger 等)。
export class ReactiveEffect<T = any> implements Subscriber, ReactiveEffectOptions {
  /** @internal */ deps?: Link = undefined
  /** @internal */ depsTail?: Link = undefined
  /** @internal */ flags: EffectFlags = EffectFlags.ACTIVE | EffectFlags.TRACKING
  /** @internal */ next?: Subscriber = undefined
  /** @internal */ cleanup?: () => void = undefined

  // EN: optional scheduler and lifecycle/debug hooks
  // CN: 可选的调度器与生命周期/调试钩子
  scheduler?: EffectScheduler = undefined
  onStop?: () => void
  onTrack?: (event: DebuggerEvent) => void
  onTrigger?: (event: DebuggerEvent) => void

  // EN: constructor registers effect to active effect scope if any, and stores fn.
  // CN: 构造函数:如存在 activeEffectScope,会将该 effect 注册进去;并保存传入的 fn。
  constructor(public fn: () => T) {
    if (activeEffectScope && activeEffectScope.active) {
      activeEffectScope.effects.push(this)
    }
  }

  // EN: Mark the effect as paused (it will not run immediately on trigger).
  // CN: 将 effect 标记为 PAUSED(触发时不会立即运行,改为放入 pausedQueueEffects 中)。
  pause(): void {
    this.flags |= EffectFlags.PAUSED
  }

  // EN: Resume the effect: if it was queued while paused, run it now.
  // CN: 恢复 effect;若此前因为暂停被加入 pausedQueueEffects,会立刻触发一次。
  resume(): void {
    if (this.flags & EffectFlags.PAUSED) {
      this.flags &= ~EffectFlags.PAUSED
      if (pausedQueueEffects.has(this)) {
        pausedQueueEffects.delete(this)
        this.trigger()
      }
    }
  }

  /** @internal */
  // EN: notify is called by deps to schedule this effect for execution.
  // CN: notify 由 dep 调用以安排 effect 的执行(通过批处理机制或 scheduler)。
  notify(): void {
    if (
      this.flags & EffectFlags.RUNNING &&
      !(this.flags & EffectFlags.ALLOW_RECURSE)
    ) {
      // EN: if currently running and recursion not allowed, skip.
      // CN: 若正在运行且不允许递归,则忽略这次通知,避免无限递归。
      return
    }
    if (!(this.flags & EffectFlags.NOTIFIED)) {
      // EN: mark as notified and add to batch queue
      // CN: 标记为 NOTIFIED 并加入批处理队列
      batch(this)
    }
  }

  // EN: Run the effect function immediately (collect deps). Returns the result of fn().
  // CN: 立即执行 effect 函数并收集依赖,返回 fn 的执行结果。
  run(): T {
    // TODO cleanupEffect

    // EN: if effect is not active (stopped), still run fn but without tracking.
    // CN: 若 effect 已停止(ACTIVE 标志清除),则直接运行 fn(不进行依赖收集)。
    if (!(this.flags & EffectFlags.ACTIVE)) {
      // stopped during cleanup
      return this.fn()
    }

    // EN: mark running, prepare and cleanup deps before/after run
    // CN: 标记为 RUNNING,清理上次遗留的 effect 外部状态,准备 deps 以便收集。
    this.flags |= EffectFlags.RUNNING
    cleanupEffect(this)
    prepareDeps(this)
    const prevEffect = activeSub
    const prevShouldTrack = shouldTrack
    activeSub = this
    shouldTrack = true

    try {
      return this.fn()
    } finally {
      // EN: restore active effect and tracking state; cleanup any deps marked unused.
      // CN: 恢复先前的 activeSub 与 shouldTrack,清理未被使用的 deps,并清除 RUNNING 标记。
      if (__DEV__ && activeSub !== this) {
        warn(
          'Active effect was not restored correctly - ' +
            'this is likely a Vue internal bug.',
        )
      }
      cleanupDeps(this)
      activeSub = prevEffect
      shouldTrack = prevShouldTrack
      this.flags &= ~EffectFlags.RUNNING
    }
  }

  // EN: stop the effect and remove it from all deps; call onStop hook.
  // CN: 停止 effect:从所有 dep 中移除自己,执行 cleanup 与 onStop 回调,并清除 ACTIVE 标志。
  stop(): void {
    if (this.flags & EffectFlags.ACTIVE) {
      for (let link = this.deps; link; link = link.nextDep) {
        removeSub(link)
      }
      this.deps = this.depsTail = undefined
      cleanupEffect(this)
      this.onStop && this.onStop()
      this.flags &= ~EffectFlags.ACTIVE
    }
  }

  // EN: when triggered, either queue (if paused), call scheduler, or runIfDirty.
  // CN: trigger 时的行为:若 PAUSED,则排队;若有 scheduler 用 scheduler;否则按脏检查执行。
  trigger(): void {
    if (this.flags & EffectFlags.PAUSED) {
      pausedQueueEffects.add(this)
    } else if (this.scheduler) {
      this.scheduler()
    } else {
      this.runIfDirty()
    }
  }

  /** @internal */
  // EN: run effect only if it's considered dirty by `isDirty`.
  // CN: 仅当 isDirty 返回 true(依赖发生变化或版本不同等)时调用 run。
  runIfDirty(): void {
    if (isDirty(this)) {
      this.run()
    }
  }

  // EN: query property to check if effect is dirty.
  // CN: 通过 isDirty 检查 effect 是否"脏"(需要重新执行)。
  get dirty(): boolean {
    return isDirty(this)
  }
}

// -----------------------------
// Batching and queue handling
// -----------------------------

// EN: depth of nested batches; batching defers triggers until matching endBatch.
// CN: 批处理深度计数;允许嵌套批处理,只有当最外层 endBatch 被调用时才真正触发队列中的 effects。
let batchDepth = 0
let batchedSub: Subscriber | undefined
let batchedComputed: Subscriber | undefined

// EN: Add a subscriber to the batch queue. Computed effects are queued separately.
// CN: 将 subscriber 加入批处理队列。computed 类型单独队列以保证更新顺序/语义。
export function batch(sub: Subscriber, isComputed = false): void {
  sub.flags |= EffectFlags.NOTIFIED
  if (isComputed) {
    sub.next = batchedComputed
    batchedComputed = sub
    return
  }
  sub.next = batchedSub
  batchedSub = sub
}

/** @internal */
// EN: Start a batch: increase nesting depth.
// CN: 开始一个批处理(深度 +1)。
export function startBatch(): void {
  batchDepth++
}

/** Run batched effects when all batches have ended @internal */
// EN: End batch: if outermost, flush queued computed first (clear notified) then normal effects.
// CN: 结束批处理:若为最外层,则先处理 batchedComputed(仅清理状态),再处理 batchedSub 并触发正常 effects。
export function endBatch(): void {
  if (--batchDepth > 0) {
    return
  }

  if (batchedComputed) {
    let e: Subscriber | undefined = batchedComputed
    batchedComputed = undefined
    while (e) {
      const next: Subscriber | undefined = e.next
      e.next = undefined
      e.flags &= ~EffectFlags.NOTIFIED
      e = next
    }
  }

  let error: unknown
  while (batchedSub) {
    let e: Subscriber | undefined = batchedSub
    batchedSub = undefined
    while (e) {
      const next: Subscriber | undefined = e.next
      e.next = undefined
      e.flags &= ~EffectFlags.NOTIFIED
      if (e.flags & EffectFlags.ACTIVE) {
        try {
          // ACTIVE flag is effect-only
          ;(e as ReactiveEffect).trigger()
        } catch (err) {
          if (!error) error = err
        }
      }
      e = next
    }
  }

  if (error) throw error
}

// -----------------------------
// Dep tracking helpers
// -----------------------------

// EN: prepareDeps marks existing links with version -1 and saves previous activeLink
//     so that after running the effect we can detect which deps were not re-used.
// CN: prepareDeps 将现有依赖链(link)的 version 标为 -1,并保存原先的 activeLink。
//     运行后通过版本判断哪些依赖未被重新使用(用于移除过时订阅)。
function prepareDeps(sub: Subscriber) {
  // Prepare deps for tracking, starting from the head
  for (let link = sub.deps; link; link = link.nextDep) {
    // set all previous deps' (if any) version to -1 so that we can track
    // which ones are unused after the run
    // 将之前的每个 link 的 version 设为 -1,以便在执行后识别未使用的 dep
    link.version = -1
    // store previous active sub if link was being used in another context
    // 保存之前 link.dep 的 activeLink(如果该 link 被其他 subscriber 使用)
    link.prevActiveLink = link.dep.activeLink
    // 将 dep 的 activeLink 指向当前 link,以标识当前 link 为激活状态
    link.dep.activeLink = link
  }
}

// EN: cleanupDeps traverses from tail to head and removes links whose version remained -1
//     (i.e., not used in latest run). Also restores dep.activeLink to previous.
// CN: cleanupDeps 从尾向头遍历;对于仍为 -1 的 link(未在此次运行中使用),从 dep 的订阅列表移除。
//     同时恢复 dep.activeLink 为之前保存的值。
function cleanupDeps(sub: Subscriber) {
  // Cleanup unsued deps
  let head
  let tail = sub.depsTail
  let link = tail
  while (link) {
    const prev = link.prevDep
    if (link.version === -1) {
      if (link === tail) tail = prev
      // unused - remove it from the dep's subscribing effect list
      // 未使用:从 dep 的订阅者列表中移除
      removeSub(link)
      // also remove it from this effect's dep list
      // 也从当前 effect 的 dep 双链表中移除该 link
      removeDep(link)
    } else {
      // The new head is the last node seen which wasn't removed
      // 新的 head 为最后一个未被移除的节点(从尾向前)
      head = link
    }

    // restore previous active link if any
    // 恢复 link.dep 的 activeLink 为保存的 prevActiveLink
    link.dep.activeLink = link.prevActiveLink
    link.prevActiveLink = undefined
    link = prev
  }
  // set the new head & tail
  // 更新 effect 的 deps head / tail 引用
  sub.deps = head
  sub.depsTail = tail
}

// EN: isDirty determines if a subscriber should re-run by checking deps' version or computed status.
// CN: isDirty 检查 subscriber 是否"脏":遍历其 deps,若 dep.version 与 link.version 不匹配,或者对应的 computed 需要刷新则为脏。
function isDirty(sub: Subscriber): boolean {
  for (let link = sub.deps; link; link = link.nextDep) {
    if (
      link.dep.version !== link.version ||
      (link.dep.computed &&
        (refreshComputed(link.dep.computed) ||
          link.dep.version !== link.version))
    ) {
      return true
    }
  }
  // @ts-expect-error only for backwards compatibility where libs manually set
  // this flag - e.g. Pinia's testing module
  // EN: backward-compat check for libraries that manually set `_dirty`.
  // CN: 向后兼容:某些库(如 Pinia 测试模块)可能手动设置 `_dirty`。
  if (sub._dirty) {
    return true
  }
  return false
}

// -----------------------------
// Computed refresh logic
// -----------------------------

/**
 * Returning false indicates the refresh failed
 * @internal
 */
export function refreshComputed(computed: ComputedRefImpl): undefined {
  // EN: If computed is tracking and not marked dirty, no need to refresh.
  // CN: 若 computed 正在 TRACKING 且未标记为 DIRTY,则无需刷新。
  if (
    computed.flags & EffectFlags.TRACKING &&
    !(computed.flags & EffectFlags.DIRTY)
  ) {
    return
  }
  // EN: clear DIRTY flag since we are about to recompute/validate.
  // CN: 清除 DIRTY 标志(准备刷新或验证)。
  computed.flags &= ~EffectFlags.DIRTY

  // EN: fast path: if no global reactive change (globalVersion unchanged), skip.
  // CN: 全局版本快速路径:若 computed.globalVersion 与全局 globalVersion 相同,说明自上次计算后没有全局 reactive 变更,直接跳过。
  if (computed.globalVersion === globalVersion) {
    return
  }
  computed.globalVersion = globalVersion

  // EN: SSR / no-subscriber / already evaluated handling:
  //    If there's no SSR and computed was evaluated before and either has no deps
  //    or isn't dirty, we can skip re-evaluation.
  // CN: SSR 情况(服务端渲染没有 render effect)、无 deps 或已经评估且不脏时可以跳过重新评估。
  if (
    !computed.isSSR &&
    computed.flags & EffectFlags.EVALUATED &&
    ((!computed.deps && !(computed as any)._dirty) || !isDirty(computed))
  ) {
    return
  }

  // EN: mark as running and set up activeSub/shouldTrack to collect deps while evaluating.
  // CN: 将 computed 标记为 RUNNING,并设置 activeSub、shouldTrack 以便在求值时收集依赖。
  computed.flags |= EffectFlags.RUNNING

  const dep = computed.dep
  const prevSub = activeSub
  const prevShouldTrack = shouldTrack
  activeSub = computed
  shouldTrack = true

  try {
    // EN: evaluate computed.fn with previous value (some computed accept oldValue).
    // CN: 执行 computed.fn,可带入旧值作为参数(实现细节依赖具体 computed 实现)。
    prepareDeps(computed)
    const value = computed.fn(computed._value)
    // EN: if first time (dep.version===0) or value changed, set EVALUATED and update _value.
    // CN: 若是首次或值发生变化,设置 EVALUATED,将新值赋给 _value,并增加 dep.version。
    if (dep.version === 0 || hasChanged(value, computed._value)) {
      computed.flags |= EffectFlags.EVALUATED
      computed._value = value
      dep.version++
    }
  } catch (err) {
    // EN: ensure version increments on error to avoid silent stale caches.
    // CN: 出错时也增加 dep.version,避免缓存停滞导致数据不更新。
    dep.version++
    throw err
  } finally {
    // EN: restore active effect and tracking state, cleanup deps, clear RUNNING flag.
    // CN: 恢复 activeSub 与 shouldTrack,清理 deps,并清除 RUNNING 标志。
    activeSub = prevSub
    shouldTrack = prevShouldTrack
    cleanupDeps(computed)
    computed.flags &= ~EffectFlags.RUNNING
  }
}

// -----------------------------
// Helpers to remove subscriptions / deps
// -----------------------------

// EN: removeSub unlinks a subscription link from both dep and the subscriber.
//     `soft` indicates not to decrement sc or remove entry from dep.map (used when computed unsubscribes soft).
// CN: removeSub 将 link 从 dep 的订阅链表和 subscriber 的链表中解开。
//     soft 参数用于 computed 的"软取消订阅"(不减少 dep.sc,也不从 dep.map 删除),以便 computed 能被 GC。
function removeSub(link: Link, soft = false) {
  const { dep, prevSub, nextSub } = link
  if (prevSub) {
    prevSub.nextSub = nextSub
    link.prevSub = undefined
  }
  if (nextSub) {
    nextSub.prevSub = prevSub
    link.nextSub = undefined
  }
  if (__DEV__ && dep.subsHead === link) {
    // was previous head, point new head to next
    // EN: dev-only: 更新 dep.subsHead(如果需要)。
    // CN: 开发模式:若 link 是旧的 head,更新 head 指向 next。
    dep.subsHead = nextSub
  }

  if (dep.subs === link) {
    // was previous tail, point new tail to prev
    // EN: 更新 dep.subs(tail 指针)。
    // CN: 若 link 是 tail(dep.subs),将 tail 指向 prevSub。
    dep.subs = prevSub

    if (!prevSub && dep.computed) {
      // EN: if computed and no tail after removal, unsubscribe it from all deps so it can be GCed.
      // CN: 如果这是 computed 并且没有 prevSub(意味着 dep 变为空),对 computed 进行彻底"软取消订阅"以便 GC。
      dep.computed.flags &= ~EffectFlags.TRACKING
      for (let l = dep.computed.deps; l; l = l.nextDep) {
        // here we are only "soft" unsubscribing because the computed still keeps
        // referencing the deps and the dep should not decrease its sub count
        // EN: 软取消订阅:不减 dep.sc
        // CN: 软取消订阅(不减少 dep.sc),以保持 computed 仍引用 deps 的结构一致性。
        removeSub(l, true)
      }
    }
  }

  if (!soft && !--dep.sc && dep.map) {
    // #11979
    // property dep no longer has effect subscribers, delete it
    // this mostly is for the case where an object is kept in memory but only a
    // subset of its properties is tracked at one time
    // EN: 如果非 soft,且 subscriber count 减为 0,并且 dep.map 存在,则从 dep.map 删除该键,节省内存。
    // CN: 当某属性再无订阅者时(并且 dep.map 存在),删除该属性的 dep 条目以释放内存。
    dep.map.delete(dep.key)
  }
}

// EN: removeDep unlinks a link from the subscriber's dep double-linked list.
// CN: removeDep 从 effect/computed 的 deps(双向链)中移除 link 链接。
function removeDep(link: Link) {
  const { prevDep, nextDep } = link
  if (prevDep) {
    prevDep.nextDep = nextDep
    link.prevDep = undefined
  }
  if (nextDep) {
    nextDep.prevDep = prevDep
    link.nextDep = undefined
  }
}

// -----------------------------
// Public API: effect / stop
// -----------------------------

export interface ReactiveEffectRunner<T = any> {
  (): T
  effect: ReactiveEffect
}

export function effect<T = any>(
  fn: () => T,
  options?: ReactiveEffectOptions,
): ReactiveEffectRunner<T> {
  // EN: If fn is already a runner (i.e., we passed the runner itself), extract its effect.fn
  // CN: 允许把已有 runner 直接传入 effect,若传入的是 runner,则取出其内部的 fn。
  if ((fn as ReactiveEffectRunner).effect instanceof ReactiveEffect) {
    fn = (fn as ReactiveEffectRunner).effect.fn
  }

  // EN: create new ReactiveEffect, extend with options (scheduler, hooks...), run immediately.
  // CN: 创建 ReactiveEffect 实例,扩展 options(合并到实例上),并立即执行一次以收集初始 deps。
  const e = new ReactiveEffect(fn)
  if (options) {
    extend(e, options)
  }
  try {
    e.run()
  } catch (err) {
    e.stop()
    throw err
  }
  const runner = e.run.bind(e) as ReactiveEffectRunner
  runner.effect = e
  return runner
}

/**
 * Stops the effect associated with the given runner.
 *
 * @param runner - Association with the effect to stop tracking.
 */
export function stop(runner: ReactiveEffectRunner): void {
  // EN: stop wrapper: call stop on the underlying ReactiveEffect instance.
  // CN: stop 的封装:调用 runner.effect.stop() 停止该 effect。
  runner.effect.stop()
}

// -----------------------------
// Global tracking toggle utilities
// -----------------------------

/** @internal */
export let shouldTrack = true
const trackStack: boolean[] = []

/** Temporarily pauses tracking. */
// EN: pause tracking: push current state and set shouldTrack=false
// CN: 暂时暂停依赖收集:把当前 shouldTrack 压入栈,然后置 false。
export function pauseTracking(): void {
  trackStack.push(shouldTrack)
  shouldTrack = false
}

/** Re-enables effect tracking (if it was paused). */
// EN: enable tracking: push current state and set shouldTrack=true
// CN: 开启依赖收集(也会保存先前状态以便 later reset)。
export function enableTracking(): void {
  trackStack.push(shouldTrack)
  shouldTrack = true
}

/** Resets the previous global effect tracking state. */
// EN: restore previous shouldTrack from stack (or default to true).
// CN: 重置为上一次的 shouldTrack 状态(若无则默认为 true)。
export function resetTracking(): void {
  const last = trackStack.pop()
  shouldTrack = last === undefined ? true : last
}

// -----------------------------
// onEffectCleanup / cleanupEffect
// -----------------------------

/**
 * Registers a cleanup function for the current active effect.
 * The cleanup function is called right before the next effect run, or when the
 * effect is stopped.
 *
 * Throws a warning if there is no current active effect. The warning can be
 * suppressed by passing `true` to the second argument.
 *
 * @param fn - the cleanup function to be registered
 * @param failSilently - if `true`, will not throw warning when called without
 * an active effect.
 */
// EN: Register a cleanup callback associated with the current active ReactiveEffect.
// CN: 为当前活跃的 ReactiveEffect 注册 cleanup 回调(在下一次运行前或 stop 时会执行)。
export function onEffectCleanup(fn: () => void, failSilently = false): void {
  if (activeSub instanceof ReactiveEffect) {
    activeSub.cleanup = fn
  } else if (__DEV__ && !failSilently) {
    warn(
      `onEffectCleanup() was called when there was no active effect` +
        ` to associate with.`,
    )
  }
}

// EN: Execute and clear effect cleanup (run without any active effect).
// CN: 执行 effect 的 cleanup(在无 activeEffect 的上下文中运行),然后清空 cleanup 引用。
function cleanupEffect(e: ReactiveEffect) {
  const { cleanup } = e
  e.cleanup = undefined
  if (cleanup) {
    // run cleanup without active effect
    // EN: run the cleanup with activeSub temporarily unset to avoid accidental tracking.
    // CN: 运行 cleanup 时临时清除 activeSub,防止 cleanup 内部访问响应式数据导致错误的依赖收集。
    const prevSub = activeSub
    activeSub = undefined
    try {
      cleanup()
    } finally {
      activeSub = prevSub
    }
  }
}
相关推荐
南玖i4 小时前
vue3 通过 Vue3DraggableResizable实现拖拽弹窗,可修改大小
前端·javascript·vue.js
excel4 小时前
Web发展与Vue.js导读
前端
YAY_tyy4 小时前
Three.js 开发实战教程(五):外部 3D 模型加载与优化实战
前端·javascript·3d·three.js
Zuckjet_7 小时前
开启 3D 之旅 - 你的第一个 WebGL 三角形
前端·javascript·3d·webgl
2401_863801467 小时前
探索 12 种 3D 文件格式:综合指南
前端·3d
珍宝商店8 小时前
前端老旧项目全面性能优化指南与面试攻略
前端·面试·性能优化
bitbitDown8 小时前
四年前端分享给你的高效开发工具库
前端·javascript·vue.js
gnip9 小时前
实现AI对话光标跟随效果
前端·javascript
脑花儿10 小时前
ABAP SMW0下载Excel模板并填充&&剪切板方式粘贴
java·前端·数据库