let currentSubscriber = null;
function createSignal(initialValue) {
let value = initialValue;
const subscribers = new Set();
function getter() {
if (currentSubscriber) {
subscribers.add(currentSubscriber);
}
return value;
}
function setter(newValue) {
if (value === newValue) return; // if the new value is not different, do not notify dependent effects and memos
value = newValue;
for (const subscriber of subscribers) {
subscriber(); //
}
}
return [getter, setter];
}
function createEffect(fn) {
const previousSubscriber = currentSubscriber; // Step 1
currentSubscriber = fn; // 将当前 effect 设为全局变量
fn(); // 执行 effect 函数
currentSubscriber = previousSubscriber; // 重置当前 effect
}
createEffect(() => {
console.log(count()); // 这里会捕获 `count()` 作为依赖
});
这样,这个新创建的函数就带有所谓的响应属性了,创建的这个函数会被当作依赖添加到 subscribers 中去。
这里的小用例忽略了 Solid 中一个较为重要的机制,它会使用 Transition 来实现所谓的批量更新,减少对于 DOM 过于频繁的操作。
Transition
主要通过以下几个步骤来优化状态更新和依赖追踪:
Transition
会将某些状态更新(即依赖更新)延迟执行,而不是立即应用。这意味着当 Transition
激活时,依赖的更新不会立即导致副作用(effect
)的重新执行。通过这种方式,可以在一个批次内处理多个状态更新,减少不必要的中间更新。Transition
中,所有状态(例如信号的值)会被标记为“暂时更新”,直到过渡结束,才会正式应用这些变化。这种机制避免了在过渡期间反复渲染 UI 的情况。Transition
机制通常用于如过渡动画、批量更新等场景。例如,在动画期间,多个状态的更新会被暂时推迟,直到动画结束时再统一处理,从而避免动画过程中由于状态变化导致的闪烁和性能问题。这是通过全局变量完成的,对于处在这个状态中的 State 会被加入一个列表,最后一起更新。
注意到对于:
import { render } from "solid-js/web";
import { createSignal } from "solid-js";
function Counter() {
const [count, setCount] = createSignal(0);
const doubleCount = () => count() * 2;
setInterval(() => setCount(count() + 1), 1000);
return <div>Count: {doubleCount()}</div>;
}
render(() => <Counter />, document.getElementById('app'));
会被编译成:
import { template as _$template } from "solid-js/web";
import { createComponent as _$createComponent } from "solid-js/web";
import { insert as _$insert } from "solid-js/web";
const _tmpl$ = /*#__PURE__*/_$template(`<div>Count: `);
import { render } from "solid-js/web";
import { createSignal } from "solid-js";
function Counter() {
const [count, setCount] = createSignal(0);
const doubleCount = () => count() * 2;
setInterval(() => setCount(count() + 1), 1000);
return (() => {
const _el$ = _tmpl$(),
_el$2 = _el$.firstChild;
_$insert(_el$, doubleCount, null);
return _el$;
})();
}
render(() => _$createComponent(Counter, {}), document.getElementById('app'));
这里的 insert 会将 doubleCount 和对应的 DOM 节点作为 count 的依赖。