Blog

SolidJSという選択肢

はじめに

こんにちは。ニフティ株式会社の山田です。

今回は、フロントエンドフレームワークとして最近SolidJSを試してみたので、その内容を紹介いたします。

そもそもSolidJSとは?

SolidJSとは、仮想DOMを使用しないJSX系宣言的UIフレームワークです。
宣言的UIフレームワークとしてはReactやVue.jsが有名ですが、この2つは差分レンダリングに仮想DOMを利用しています。
これは仮想DOMに一度書き込んだあと、差分を計算して実際のDOMに反映させるという動作を基本としています。プログラミングモデルが単純化される一方で、仕組みとしては重く、JavaScriptのサイズを肥大化させる原因ともなっています。仮想DOMへの書き込みを減らす仕組みも入っていますが、根本的な重さからは逃れられません。

一方で最近登場したのが、イベント駆動とトランスパイルを組み合わせるフレームワークです。

これらのフレームワークは、変数を「値変化イベントを発火させるオブジェクト」として取り扱うことで、その変数に関連するコンポーネントだけを再レンダリングするように動作します。
普通であればコーディングが複雑化しますが、ビルド時変換を多用することで極力簡単に書けるようになっています。

有名なフレームワークとしてSvelteが挙げられますが、SvelteはVue.jsに近いHTML/CSS/JavaScriptを1ファイルに書く独自文法を採用しています。

一方で、React同様にJSXを採用しているのがSolidJSです。

SolidJSは値変化の検出にsignalという仕組みを採用しています。この仕組みはknockout.jsで開発されたものですが、宣言的UIフレームワークに採用したのはSolidJSが初であり、その後PreactやVue.js、Svelte 5などでも採用が広がっています。

SolidJSのメリット

  • 独自文法を採用せず、JSXベースのためセットアップが容易
    • SvelteはLinterなどのセットアップが辛い…
    • Astroに部分的に組み込む〜 などがやりやすい
  • 読みにくい挙動部分が少ない
    • この辺りもReactの思想に近い
    • Svelteのように Easy >>> Simple な思想とは真逆
  • 仮想DOMを使用せず、動作速度・JSサイズともに軽量

デメリット

  • 利用者がまだ少ない…
    • React > Vue.js > Svelte >>>>> SolidJS くらいの肌感覚
  • 周辺ツールのサポートも少ない
    • Vitest Browser Mode 非対応なのが痛い
  • SSRフレームワーク(SolidStart)の歴史が浅い
    • 2024年4月にようやく1.0になったばかり

使い心地

基本的なコンポーネント

Reactに慣れている人であれば、

  • createSignal → useState
  • createEffect → useEffect

のように読み替えて理解ができるくらいにはそっくりです。

Reactでは状態変数をstateと呼びますが、Solidではsignalと呼びます。値の変化に応じて値変化イベントを発火させるオブジェクトになっています。

Reactとの違いとしては、以下となります。

  • コンポーネントは初期化時の1回しか実行されないこと
    • Reactのように再レンダリングの度に実行される、というモデルではない
    • signalが変化した場合、signalに関連する部分のコードだけを再実行する
    • 定数宣言してしまうと初期化時に値が固定されてしまう
      • signalに依存する別の変数を扱う場合は、関数として扱います
  • returnは必ず1つでなければならない
    • 複数あったとしても、使われるのは初期化時に使われた1つだけ
  • 変化する値は関数を通してアクセスする
    • 例えばcreateSignal()で返るのは定数ではなく、関数になっている
      • TypeScript的にはAccessor<T>という型になっている
  • createEffectに依存配列は必要ない
    • ReactのuseEffectとは明確に異なる
    • createEfffectの中で使われているsignalが変化したとき、自動で再実行される

props

propsもReact同様…ですが、オブジェクトの展開(destructuring)を行ってはいけないです。
(以下のように書くのはアウトとなります)

制御構文

「変数(signal)の変化に応じて再レンダリングする」という構造上、コンポーネントの出し分けに三項演算子やmap()を使用してしまうと無駄な再レンダリングを引き起こしてしまう可能性が高いです。(条件文の結果が変わらなくても、条件判定に使用するsignalが変わっていれば再レンダリングされるため)

というわけで、コンポーネントの出し分けには独自のコンポーネントが必要になります。

Show

if文や三項演算子の代わりになるのがShow。whenに条件文を渡して制御します。else相当のコンポーネントはfallbackに渡します。
残念な仕様として、whenによるtype narrowingが効かないです。このためasなどを使用せざるを得ません。

一応、子コンポーネントを「whenの値を渡す形の関数」として記述でき、null・undefined判定だけならこれで解決できます。

For / Index

forやmap()の代わりはForとなります。

同じ文法でIndexというものもあります。それぞれの使い分けはドキュメントに記載されていますのでそちらを参照してみてください。

createResource

データの非同期取得のために、createResouceが標準で用意されています。
ReactのSWRやTanstack Queryのような別ライブラリを必要とせずにデータフェッチを記載できます。

まとめ

Good Point

  • 挙動が理解しやすい
    • Svelteのような、どう動くのか理解不能な局面が少ない
  • 軽い
    • Reactで書くと数百KBになるものが数KBで済む
  • 運用が軽い
    • 破壊的変更が非常に少ない
      • Svelteは周辺ツールもふくめてそれなりに壊してくる & Svelte 5で超大規模変更が…
    • JSXなので大体のツールが標準対応しており、余計なパーサーなどの設定が不要
  • 罠はあるが、入門ドキュメントで覚えた程度で事足りる
  • 公式ドキュメントに日本語あり

Bad Point

  • Showが辛い
    • ここだけ型安全性が崩れる
  • SolidStartの歴史が流石に浅い
    • まだ半年程度…
    • ベースになっているvinxiがまだ0.4でドキュメントも貧弱
    • Astro + Solidのような組み合わせが現状は安牌

今回はSolidJSを試してみた所感を紹介しました。SolidJSや他フレームワークの比較をされている際に参考になれば幸いです。

ニフティでは、
さまざまなプロダクトへ挑戦する
エンジニアを絶賛募集中です!
ご興味のある方は以下の採用サイトより
お気軽にご連絡ください!

ニフティに興味をお持ちの方は
キャリア登録をぜひお願いいたします!

connpassでニフティグループに
参加いただくと
イベントの
お知らせが届きます!