一、effect 函数的作用

Vue 的响应式系统是其核心特性之一,它使得开发者能够轻松地构建动态且响应迅速的用户界面。在这个系统中,effect 函数起到了至关重要的作用

effect 函数用于注册一个副作用函数(effect),这个函数会在依赖的响应式数据发生变化时自动重新执行,从而实现数据驱动的视图更新

(packages/reactivity/src/effect.ts):

 

effect 的主要作用是自动跟踪在 fn 函数中访问的所有响应式属性,并在这些属性发生变化时自动重新执行 fn

fn: 需要跟踪响应式更新的函数。它将在 effect 创建时立即执行一次,并且每当它依赖的响应式属性发生变化时,会再次执行

options: 控制 effect 行为的选项

scheduler: 允许自定义 effect 的调度策略

1export interface ReactiveEffectOptions extends DebuggerOptions {
2  lazy?: boolean
3  scheduler?: EffectScheduler
4  scope?: EffectScope
5  allowRecurse?: boolean
6  onStop?: () => void
7}
8
9export interface ReactiveEffectRunner<T = any> {
10  (): T
11  effect: ReactiveEffect
12}
13
14/**
15 * Registers the given function to track reactive updates.
16 *
17 * The given function will be run once immediately. Every time any reactive
18 * property that's accessed within it gets updated, the function will run again.
19 *
20 * @param fn - The function that will track reactive updates.
21 * @param options - Allows to control the effect's behaviour.
22 * @returns A runner that can be used to control the effect after creation.
23 */
24export function effect<T = any>(
25  fn: () => T,
26  options?: ReactiveEffectOptions,
27): ReactiveEffectRunner {
28  /// ...
29
30  const _effect = new ReactiveEffect(fn, NOOP, () => {
31    if (_effect.dirty) {
32      _effect.run()
33    }
34  })
35  /// ...
36  if (!options || !options.lazy) {
37    _effect.run()
38  }
39  const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
40  runner.effect = _effect
41  return runner
42}

 

二、ReactiveEffectRunner 接口

effect 函数会返回一个运行器(runner)对象。通过 runner,你可以手动运行或重新运行 effect,即手动控制副作用执行或者获取 effect 实例

1export interface ReactiveEffectRunner<T = any> {
2  (): T
3  effect: ReactiveEffect
4}

 

三、ReactiveEffect 类

ReactiveEffect 类它负责管理副作用的执行、依赖的跟踪,以及生命周期的控制

 

activeEffect: 这是一个全局变量,存储当前正在运行的 effect。通过这个变量,Vue 可以在执行 effect 函数时,正确地收集依赖关系

scheduler: 一个可选的调度函数,控制 effect 的执行时机

deps: 一个依赖数组,存储了 effect 所依赖的所有响应式数据。从另一个角度来说,每当这些依赖的数据发生变化时,effect 会被重新执行

run():是 effect 的核心执行逻辑。当 effect 运行时,它会设置相关的全局状态,以确保在执行 fn 时可以正确收集依赖

try...finally :保证在 fn 执行完后,无论结果如何,都会恢复之前的状态

1export type Dep = Map<ReactiveEffect, number> & {
2  cleanup: () => void
3  computed?: ComputedRefImpl<any>
4}
5
6export let activeEffect: ReactiveEffect | undefined
7
8export class ReactiveEffect<T = any> {
9  /// ...
10  deps: Dep[] = []
11  /// ...
12
13  constructor(
14    public fn: () => T,
15    public trigger: () => void,
16    public scheduler?: EffectScheduler,
17    scope?: EffectScope,
18  ) {
19    /// ...
20  }
21
22  public get dirty() {
23    /// ...
24  }
25
26  public set dirty(v) {
27    /// ...
28  }
29
30  run() {
31    /// ...
32    let lastEffect = activeEffect
33    try {
34      shouldTrack = true
35      activeEffect = this
36      this._runnings++
37      preCleanupEffect(this)
38      return this.fn()
39    } finally {
40      postCleanupEffect(this)
41      this._runnings--
42      activeEffect = lastEffect
43      shouldTrack = lastShouldTrack
44    }
45  }
46
47  stop() {
48    /// ...
49  }
50}

 

四、其他

学习的记录,仅供参考