创建信号量

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 主要通过以下几个步骤来优化状态更新和依赖追踪:

这是通过全局变量完成的,对于处在这个状态中的 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 的依赖。