在本文中,我将通过三个案例来深入到 Solid 源码中解析 Signal 响应式的原理,并通过图解的方式更清晰的展示给大家。

在上一篇文章中,我也讲了该如何去调试 Solid 源码,这里将不再过多赘述,如果不知道的小伙伴,可以再去看一下。

Solid 之旅 —— 调试源码篇(其他框架也适用) | Serendipity

前置

这里先提前讲一下 SignalComputationMemo 的类型,方便后续调试的时候理解其含义。

注:含 t 开头的属性(tValuetState …),都是和 Transition 相关的状态,本文暂时忽略。

类型

SignalState

export interface SignalState<T> {
  value: T;
  /**
   * signal 收集的 effect
   */
  observers: Computation<any>[] | null;
  /**
   * 这是对应 observers 中 effect 里 sources 对应的位置
   * (observers[i] as Signal).sources[observerSlots] -> 本身
   * signal 和 effect 两者的 observers、sources、observerSlots、sourceSlots 是一一对应的
   */
  observerSlots: number[] | null;
  /**
   * tValue 是用于 Transition 时,在加载数据时显示回退内容,即旧的 value
   */
  tValue?: T;
  /**
   * 用于判断是否需要重新渲染,通过 options.equals 去配置
   */
  comparator?: (prev: T, next: T) => boolean;
}

Computation

export type ComputationState = 0 | 1 | 2;

export interface Computation<Init, Next extends Init = Init> extends Owner {
  /**
   * 副作用函数
   */
  fn: EffectFunction<Init, Next>;
  /**
   * 标识 effect 当前状态,未设置(0)、STALE(1)、PENDING(2)
   */
  state: ComputationState;
  tState?: ComputationState;
  /**
   * effect 中依赖收集的 Signal
   */
  sources: SignalState<Next>[] | null;
  /**
   * 这是对应 sources 中 Signal 里 observers 对应的位置
   * (sources[i] as Signal).observers[sourceSlots] -> 本身
   * signal 和 effect 两者的 observers、sources、observerSlots、sourceSlots 是一一对应的
   */
  sourceSlots: number[] | null;
  /**
   * 用于 createMemo 这种特殊的 effect,存在返回值,只读 Signal
   */
  value?: Init;
  updatedAt: number | null;
  /**
   * TODO: pure 暂时的作用看是用作 memo 和 effect 做区分的
   * 同时它也是区分 Updates 和 Effects 的
   */
  pure: boolean;

  /**
   * TODO: 像是区分是否是用户手动定义的,例如 createEffect 时,user 为 true,而 createMemo 时,user 为 false
   */
  user?: boolean;
  suspense?: SuspenseContextType;
}

注:先解释一下,在 Solid 内部,类似 createEffect 这种计算函数(副作用)的对象统称为 Computation

简单点说,createEffect 就是返回值为空的计算函数,只需要执行其函数就可以了;像 createMemo,就是带返回值的 Computaion

这边只是简单解释一下 Computation 的含义,后面还会再详细解释。

Memo

export interface Memo<Prev, Next = Prev> extends SignalState<Next>, Computation<Next> {
  value: Next;
  tOwned?: Computation<Prev | Next, Next>[];
}

这里其实就能看出来,Memo 本质上就是 SingalComputation 的结合体。

全局变量

// 用于 Computation.state
// 标识当前 computation 值已过期,需要更新
const STALE = 1;
// 目前看到是在 memo 在 effect 中使用时,该 effect 会赋予 PENDING
const PENDING = 2;

// 全局变量
/**
 * Listener 指向当前执行的 effect,用于后续依赖收集 Signal
 */
let Listener: Computation<any> | null = null;
/**
 * 目前看的话,是把 Updates 和 Effects 看作两种优先级的队列,像 Updates,是存放 memo 等内置特殊处理的 pure effect
 * 而 Effects 是 createEffect 创建的普通 effect
 * 一次更新过程中,Updates > Effects,即先执行 Updates,再执行 Effects
 */
let Updates: Computation<any>[] | null = null;
let Effects: Computation<any>[] | null = null;