在本文中,我将通过三个案例来深入到 Solid 源码中解析 Signal 响应式的原理,并通过图解的方式更清晰的展示给大家。
在上一篇文章中,我也讲了该如何去调试 Solid 源码,这里将不再过多赘述,如果不知道的小伙伴,可以再去看一下。
Solid 之旅 —— 调试源码篇(其他框架也适用) | Serendipity
这里先提前讲一下 Signal
、Computation
、Memo
的类型,方便后续调试的时候理解其含义。
注:含
t
开头的属性(tValue
、tState
…),都是和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
本质上就是 Singal
和 Computation
的结合体。
// 用于 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;