这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情
到上一篇为止,我们还是只使用了 createSignal 这一个 API,在真实的应用中这肯定是远远不够的,副作用的处理也是非常重要的能力。在 react 中有 useEffect 来处理副作用,在 solid.js 中也提供了 createEffect API,不过这里仍然与 react 不同,createEffect 同样也是基于 solid 的响应式原理实现的,本篇就来看一下 effect 的处理逻辑。
首先来看 createEffect 的使用:
const [a, setA] = createSignal(0);
createEffect(() => console.log(a()));
这里每次调用 setA 修改 a 的值,a 都会打印出来,这里的 createEffect 会自动收集 a 作为依赖并监听,不需要像 react 一样手动声明依赖。
createEffect 的另一个能力是可以返回一个值,每次 effect 触发是可以获取到上一次的值作为参数,这样可以对比 effect 发生前后两次的变化,在这一场景下,createEffect 可以添加第二个参数作为初始值:
createEffect((prev) => {
const sum = a() + b();
if (sum !== prev) console.log(sum);
return sum;
}, 0);
createEffect 的主要用法就这么多,接下来来看一下 createEffect 的实现:
export function createEffect<Next, Init>(
fn: EffectFunction<Init | Next, Next>,
value?: Init,
options?: EffectOptions
): void {
runEffects = runUserEffects;
const c = createComputation(fn, value!, false, STALE, "_SOLID_DEV_" ? options : undefined),
s = SuspenseContext && lookup(Owner, SuspenseContext.id);
if (s) c.suspense = s;
c.user = true;
Effects ? Effects.push(c) : updateComputation(c);
}
本身的逻辑依旧很简洁,主要就是创建和更新 computation。Computation 在前面也接触过,它封装的是一个需要计算的行为,前面添加的 observer 内容就是一个 Computation 对象。在 solid 中凡是需要收集依赖并在依赖变化时做响应更新的事物都可以描述成一个 Computation,显然这里的 effect 满足概念要求。因此这里首先是构建 Effect 对应的 Computation 对象。
在构建好 Computation 之后,如果存在 Effects 队列,Computation 就会被放入队列中等待执行,如果不存在队列,这里会调用 updateComputation。
Effects 队列的执行同样也在 completeUpdates 阶段,这里会对 effect 队列执行遍历操作,对每一项进行 runTop 处理。effect 的实际执行时机比较复杂,它与 suspense 有很强的关联,这部分在后面阅读 suspense 部分时还会接触,这里只是看响应式处理相关的部分。