Signals:フロントエンド・リアクティビティの新潮流

6 分

1. リアクティブモデルの進化

フロントエンド開発の本質は「状態を UI に対応づけること」です。長年にわたり、リアクティブモデルは絶えず進化してきました。手動の DOM 操作から MVC/MVVM パターンへ、そして React が普及させた仮想 DOM(Virtual DOM)へと移り変わってきたのです。VDOM はコンポーネント単位で状態変更をまとめて処理し、Diffing を行うことで、UI 開発を大幅に簡素化しました。

しかし、VDOM は万能薬ではありません。コンポーネント単位の再レンダリングと Diffing のコストは、動的な処理が特に多い場面ではパフォーマンスのボトルネックになり得ます。近年、より古いパラダイムを現代のコンパイラで最適化した「きめ細かなリアクティビティ(Fine-Grained Reactivity)」が再び表舞台に戻り、その中核を担っているのが Signals です。

2. Signals とは?

Signal は、値を包み込み、その値が変化したときにすべての依存先へ自動的に通知できるリアクティブプリミティブ(Reactive Primitive)です。コンポーネント単位で変化を追跡する VDOM フレームワークとは異なり、Signal はデータが読み取られる段階で依存関係グラフを自動的に構築します。

Signal の値が更新されると、その Signal に直接または間接的に依存する計算(Memo)や副作用(Effect)だけが再実行されます。これにより「外科手術」のように正確な DOM 更新が可能になり、VDOM の Diffing はまったく必要ありません。

その中核は通常、次の 3 つのプリミティブで構成されます。

  • createSignal(value): リアクティブな state の単位を作成します。
  • createEffect(() => {}): 依存関係を自動追跡し、変更に反応する副作用を作成します。
  • createMemo(() => {}): 派生した、キャッシュ付きのリアクティブな計算を作成します。
// SolidJS 语法示例
const [count, setCount] = createSignal(0);

const doubleCount = createMemo(() => count() * 2);

createEffect(() => {
  console.log(`The double count is: ${doubleCount()}`);
});

setCount(1); // 这将触发 memo 重新计算,并触发 effect 重新执行

重要なメンタルモデルは、SolidJS のような Signal ベースのフレームワークでは、コンポーネント関数そのものは初期化時に一度だけ実行されるという点です。その後の更新はコンポーネントの再レンダリングではなく、リアクティブシステムによって完全に駆動されます。

3. エコシステム全体での幅広い採用

Signal はまったく新しい概念ではなく、その考え方は Knockout.js など初期のフレームワークにまで遡れます。しかし、現代の JavaScript コンパイラによって新たな活力を得て、主要なフレームワークに幅広く採用されています。

  • SolidJS & Qwik: Signal を全面的な基盤として構築されており、このパラダイムの模範的な実装です。
  • Preact: @preact/signals パッケージによって公式サポートを導入し、既存の Preact/React プロジェクトにも統合できます。
  • Vue: Composition API の refcomputed は、本質的には Signal の一種の実装です。
  • Svelte 5: まもなく登場する「Runes」アップデートは、Svelte チームによるリアクティブモデルの再構築であり、その中核では Signal パラダイムを参考にして実装しています。
  • Angular: 最近のバージョンでは、新しいリアクティブプリミティブとして Signals が導入されています。

このようにフレームワークをまたいで同じ方向へ収束していることは、効率的で予測可能なリアクティブモデルとしての Signal の価値を証明しています。

4. 利点とトレードオフ

利点:

  1. 優れたパフォーマンス: VDOM とコンポーネント単位の再レンダリングを回避するため、ベンチマークでは通常、優秀な結果を示します。
  2. 予測可能性: 状態更新の流れが明確で、理解やデバッグが容易です。
  3. 少ないメモリ使用量: コンポーネントツリー全体の仮想表現を保持する必要がありません。

トレードオフ:

  1. メンタルモデルの転換: React のレンダリングサイクルに慣れた開発者は、「コンポーネントは一度しか実行されない」という新しいモデルに適応する必要があります。
  2. エコシステムとの統合: Signals 自体は高性能ですが、巨大な React VDOM エコシステム(たとえば一部の UI ライブラリ)と深く統合するには、追加の適応作業が必要になる場合があります。

まとめ

Signals の復興は、フロントエンドにおける状態管理パラダイムの重要な進化を示しています。開発者の意識を「コンポーネントがどのようにレンダリングされるか」から、より本質的な「状態がどのように流れるか」という問題へと引き戻しました。コンパイラとの深い統合により、Signal は優れた開発体験を提供しながら、Web アプリケーションのパフォーマンス限界に新たな可能性をもたらします。