SolidJSの面白いところ、気になるところ

スマホのブラウザタブにいつなんで開いたかはわからないのだけどSolidJSのページがあって、妙に閉じずに見たほうがいいような気がしたので調べたり触ったりした。

とりあえず2022-03-13現在のメモを書く。なおバージョンはv1.3.0が最新なので1系について。

内容は個人的なものであるため、ここに書いてあることが絶対正しいということは決してありません。

あとAPIを網羅的に触れたりもしていません。サンプルコードは基本的に公式ガイドのものを借用しています。

公式ガイドが充実しており日本語訳もあるので公式に委ねられる部分はすべてそちらを見たほうがいいと思うためです。

またSolidJSと並んでSvelteの話題がちょこちょこ出ますが思想と立ち位置が似ているためです。

SolidJS とは

位置づけ的にはReactとかVueとかと同じ宣言的UIのツールというかフレームワーク。めちゃくちゃ新しいわけではなく初期は2016年らしいので、意外と長いことやっている。

github.com

https://www.solidjs.com/

基本のAPIはReactのAPIに似ていて、書き心地もReactっぽさがある。

じゃあなんで存在してるかというと、技術アプローチが違っていてパフォーマンスがよいことを売りにしている。

React/Vue/Angularなど既存フレームワークとの違い

ゆえに

バンドルサイズが小さく動作パフォーマンスがよい

というメリットがあるとしているらしい。

Svelteもあるよね

宣言的UIフレームワークの後続といえばSvelteもある。

というか、SolidJSのアプローチはプリコンパイルするコンパイラであるという点で根っこの思想はSvelteと同じアプローチだと思われる。

現にSolidJSの作者はブログでSvelteとの比較を書いている。

JavaScript UI Compilers: Comparing Svelte and Solid | by Ryan Carniato | Medium

Svelteがv3系・SolidJSがv1系の現在では、SolidJSの作者的にはSvelteがバンドルサイズで優れていて、SolidJSは動作パフォーマンスが優れているだろうという意見らしい。

面白いところ

思想

公式HPのリアクティブのページにこう書かれており、思想がわかる。

https://www.solidjs.com/guides/reactivity#4.-%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E3%81%AF%E3%82%A4%E3%83%BC%E3%82%B8%E3%83%BC%E3%81%AB%E5%8B%9D%E3%82%8B

Image from Gyazo

シンプルはいいし意識として持っていることは非常に印象よく感じる。

一方で、シンプルを維持することは難しいし、シンプルにやっていたつもりがいつのまにかイージーになっていたりということはこの世では往々にしてあるものなので、今後に注視という感じ。

とっつきやすい

Svelteも素晴らしいと思いつつも個人的にSyntaxが本当に好きになれなくて二の足を踏んでいるところがある。

Hello world • Svelte Examples

Svelteのexampleとかtutorialとかを見るとわかると思うけれど、あまりにクセが強く、JavaScriptでフロントエンドを表現する以上のなにかが混在していると感じてしまう自分がいる。if-elseとかeachとかのシンタックスを見ると、懐かしきPugやEJSの記憶を思い出してしまう……。

VueのEvan氏にその昔"SvelteScript"と揶揄されていた様子もある。ReactだろうがVueだろうがなんだろうが、ピュアなJS以上のシンタックスAPIの概念が何かしら出ることは致し方ないとは思うものの、Svelteのそれはちょっと度が過ぎていると個人的には感じる。

一方SolidJSは、JSXで書けAPIがReactチックであるため随分と馴染みやすさを感じる。もちろんReactのそれと同じではないものの、Svelteよりは随分ととっつきやすい気がするし、JSの域で書けている感覚がある。

気になるところ

SolidJSのルールが多く"書いたとおりに動くわけではない"

レンダリングにJSXの他にテンプレートリテラル・HyperScriptをサポートしている中で、おすすめはJSXとしている。なので基本的にJSXで書くものと思ったほうがいい。

JSXを採用した場合、上述の通り書き味はReactに近いものの、真のリアクティブを実現するためのSolidJS側の都合が多く、Reactのコンポーネントを書いているノリで書くといろいろとうまく動かない部分が出てくる。

Reactならびに従来の仮想DOMを使うフレームワークは、「書いたように動く(パフォーマンスやバンドルサイズは捨て置くが)」という感じなので、適当に書いてもSyntaxさえ間違っていなければ動くという世界観。

一方、真のリアクティブのためのお作法があるのがSolidJSなので、お作法に従ってちゃんと「動くように書かないと動かない」というトレードオフがある。パフォーマンスを得るために課せられたハードルという感じがある。

いくつか以下にとりあげる。

props

SolidJSのガイドを見ていて一番「エッ!」と思ったところ。詳しくは公式ガイドのPropsの説明を読んでほしいが、Reactコンポーネントのように書くと異常なハマりが発生する。

https://www.solidjs.com/guides/rendering#props

  • propsを分割代入して取り出してはいけない
  • ローカル変数でデータを作って表現してはいけない
// これはダメ
const BasicComponent = ({value}) => {
  return <div>{value || "default"}</div>;
};

// これもダメ
const BasicComponent = (props) => {
  const value = props.value || "default";

  return <div>{value}</div>;
};

// これならOK
const BasicComponent = (props) => {
  return <div>{props.value || "default"}</div>;
};

// これもOK
const BasicComponent = (props) => {
  const value = () => props.value || "default";

  return <div>{value()}</div>;
};

仮想DOMなしに真のリアクティブを実現するために、プロパティの追跡をSolidJS側はしないといけないため、致し方ないとはいえなかなか渋い。分割代入するとObjectの参照が切れるからそれはそうなんだけど、props.ってまあまあ書きたくないし可読性も低くなるなと思う。

2022-03-18追記: 知人と会話していたところ、コンパイラのAST解釈の実装をシンプルに保つために意図的にこうしているのではないか?という意見を頂いた。確かにそうかもしれない。あとPropsの分割代入では参照は切れなかった。

Children

Reactと同じchildrenの概念があるものの、複数のchildrenに対してちゃんとデータ更新に反応するReactiveな状態を実現するには、ヘルパー関数を通さないとダメというのがある。

https://www.solidjs.com/guides/rendering#children

ガイドに書かれているとおりだが、childrenにattributesを加えるとか、それぞれが動的データに依存している場合にはこうしないといけない。

const List = (props) => {
  // childrenヘルパーを通す
  const getChildren = children(() => props.children);
  // 各childにクラス付与する
  createEffect(() => {
    const children = getChildren();
    children.forEach((c) => c.classList.add("list-child"))
  })
  return <ul>
    // childrenをrenderする
    <For each={getChildren()}>{item => <li>{item}</li>}</For>
  </ul>;

クセがあるなとは思う。ただReactではこの例のような場合だと「 <li> に ユニークな key を渡す必要がある」とか「VirtualizedListにしないとパフォーマンスが落ちる可能性がある」とかそういうのがあるので、覚えゲーな気もしている。

ちなみにしれっと登場しているが、リスト配列をmapで回すときのお作法として <For></For> とか <Index></Index> というものが組み込みAPIにある

ESLintである程度はカバーできる

SolidJSのクセに関してはESLintルールがあり、ある程度は弾ける。なので常に気をつけないとすぐバグるというほどではないだろうとは思う。でもLintでカバーできない部分もあるので、そこはやはり覚えゲーになるかもという気がしている。

GitHub - joshwilsonvu/eslint-plugin-solid: Solid-specific linting rules for ESLint.

独自のAPIが結構ある

https://www.solidjs.com/docs/latest/api

React書ける人ならすぐSolidJS書けるかというと難しいと思う。

それは仮想DOMがなくReactiveの実現のためのお作法を覚えないといけないから。補助するUtilityやPrimitiveAPIがいくつも提供されているが、SolidJSのお作法を支えるためのものなので、ここはイチから学習する必要がある。

ツールチェーン・エコシステム

たぶんライバル視しているSvelteのほうが2022-03-13現在だと若干勝っていそうという感じ。

SvelteにはNext.js的なことをするためのフレームワークとしてSvelteKitというものがある。

ちなみに以前はSapperというプロジェクトだった

SolidJSにはまだSvelteKit同様のものはなく、目下製作中らしい

https://www.solidjs.com/resources

ここのリソースのページからエコシステムやコミュニティライブラリなどが検索できて便利。見た感じそこそこあるので別にめちゃくちゃ劣っているわけでもなさそう。

SSRとかISRとか

アイソモーフィックJavaScriptの思想を受け継いでいるらしく、SSRできる。ISRはまだ無理そう。あんまりこの辺は詳しくないのでガイドに譲る。

https://www.solidjs.com/guides/server

○○とSolidJSの違いはなに?

https://www.solidjs.com/guides/comparison

だそうです。

コミュニティ

そこそこ人がいそうな様子。

https://www.solidjs.com/contributors

スポンサーもついているし、SolidHackという賞金ありのハッカソンも予定しているらしい。

https://hack.solidjs.com/

一方Svelteのリッチハリス氏はVercelにジョインしたらしく、Svelteはかなり勢いづいているようにも見える。

Svelte の開発を加速する (Accelerating Svelte's Development)

コミュニティの栄枯盛衰はいつの時代もどこにでもあるので、短期的な情報だけを見てもしかたないとは思う。あくまで余談。


その他所感

Reactないしは仮想DOMのパフォーマンス問題はたしかにあると個人的には思っていて、職人が頑張らないとどんどん小さなパフォーマンス劣化が積み上がって、アプリケーション全体で気づいたら結構重いとか妙なカクつきがあるとかが発生しがちだと思っている。仮想DOMは早いみたいなのがReact初期に盛り上がっていた気がするけど、実際に本番のアプリケーションになると意外と難しいとこあるなと何年も触れて思っているとこはある。

そういう意味でSolidJSとかSvelteのプリコンパイルするアプローチは気になっているけれどどうしてもクセが強くて、開発速度のパフォーマンスも加味すると仮想DOMのフレームワークのほうがまだバランスが取れているように感じる自分もいる。

SolidJSは馴染みやすい感じはあるので、今後PoCでなんか作るときや個人でなにか作るときがあったら採用してみるのはありかもしれないと思う。