这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情
这一篇来看一些工具函数,这些函数与 solid 的响应式流程控制有关,有一些也在 solid 的源码内部被大量使用,这些函数被导出作为工具函数,在 solid 应用中有时会用到。
首先来看 untrack,这个函数之前的文章中提到过,它的效果是忽略依赖执行逻辑。由于 solid 默认是响应式自动追踪依赖的,会存在某些时候我们就不需要某个依赖,我们想跳过默认的依赖,此时就可以借助 untrack 的能力,把这部分逻辑放在 untrack 的回调函数中执行就会忽略依赖。untrack 的实现如下:
export function untrack<T>(fn: Accessor<T>): T {
let result: T,
listener = Listener;
Listener = null;
result = fn();
Listener = listener;
return result;
}
非常简单,因为 solid 的监听是被添加到 Listener 中的,因此这里临时把 Listener 清空就可以不带任何依赖了,执行完成后再还原 Listener,就不会影响其他逻辑的执行。
接下来看 batch,batch 的效果是批量更新,这个概念熟悉 react 就很容易理解,react 中一项很重要的内部优化就是 batch update,当你调用 setState 之后并不能直接获取到更新后的结果,需要等待下次刷新后才能拿到新值,setState 只是把更新推入了待更新队列,在每个更新周期进行更新。
在 solid 中也有 batch,添加到 batch 中的逻辑不会立刻触发更新,会等到当前作用域结束统一更新,batch 在 solid 的源码中也有很多使用,它本身的实现也很简单:
export function batch<T>(fn: Accessor<T>): T {
if (Pending) return fn();
let result;
const q: SignalState<any>[] = (Pending = []);
try {
result = fn();
} finally {
Pending = null;
}
runUpdates(() => {
for (let i = 0; i < q.length; i += 1) {
const data = q[i];
if (data.pending !== NOTPENDING) {
const pending = data.pending;
data.pending = NOTPENDING;
writeSignal(data, pending);
}
}
}, false);
return result;
}
接下来一个是 on,on 在 solid 源码中也有使用,on 的作用是实现显式监听更新,举个例子:
createEffect(on(a, (v) => console.log(v, b())));
上面的逻辑和下面的是等价的:
createEffect(() => {
const v = a();
untrack(() => console.log(v, b()));
});
on 后面还可以通过设置 defer 来限制只在更改时才计算,源码实现如下:
export function on<S, Next extends Prev, Prev = Next>(
deps: AccessorArray<S> | Accessor<S>,
fn: OnEffectFunction<S, undefined | NoInfer<Prev>, Next>,
options?: OnOptions
): EffectFunction<undefined | NoInfer<Next>> {
const isArray = Array.isArray(deps);
let prevInput: S;
let defer = options && options.defer;
return prevValue => {
let input: S;
if (isArray) {
input = Array(deps.length) as unknown as S;
for (let i = 0; i < deps.length; i++) (input as unknown as TODO[])[i] = deps[i]();
} else input = deps();
if (defer) {
defer = false;
return undefined;
}
const result = untrack(() => fn(input, prevInput, prevValue));
prevInput = input;
return result;
};
}
这里就是利用 untrack,手动管理数据值的变化过程,可以用在想要手动处理更新逻辑的某些场景下。
最后再来看一个 observable 函数,这个函数在一个单独的 observable 文件中,它可以把 signal 转化为 Observable 对象。由于 signal 本身就是基于响应式设计的,因此它可以很方便地和响应式编程库一起使用,其中的代表就是 rxjs:
import { from } from "rxjs";
const [s, set] = createSignal(0);
const obsv$ = from(observable(s));
obsv$.subscribe((v) => console.log(v));
接入 rxjs 后可以更方便数据处理,对于 rxjs 用户很友好,这里的源码实现也很简单,感兴趣可以去阅读一下,在此不做展开了。