SolidJSとは
https://www.solidjs.com/
仮想DOMを使わない、Reactライクに書ける新しいフロントエンドのフレームワークです。
同じく仮想DOMを使わないフレームワークとしてSvelteがありますが、Svelteと違ってReactと近い書き方で記述できるのが特徴です。
今回はReactもSvelteもがっつり使っている私が思うSolidJSの良いところ・もうちょっと改善してほしいところを挙げていきます。
SolidJSの良いところ
高パフォーマンス
SolidJSの最大のポイントは仮想DOMを使わずに直接DOMを更新することで高パフォーマンスを発揮することです。
Svelteと同様にコンパイル時点で、このステートが更新されたらこのDOMを更新するというのを紐づけることで仮想DOMいらずにReactのようなアプリケーションを作ることが出来ます。
公式にパフォーマンス比較の画像が載っていますが、Vanillaとほぼ変わらないパフォーマンスが出るそうです。(Svelteよりも高速)
createSignalが便利
SolidJSではステートの扱いがより柔軟になります。
その一例として、createSignal
の柔軟さが挙げられます。
このcreateSignal
はReactでいうところのuseState
に近いですが、大きな違いとしてコンポーネントの外でも使えるというところがあります。
import { createSignal } from 'solid-js';
const [outerState, setOuterState] = createSignal('');
const Comp = () => {
const [innerState, setInnerState] = createSignal('');
return (
<div>
{outerState()}, {innerState()}
</div>
);
};
こちらの例では、outerState
はコンポーネントの外、innerState
はコンポーネントの中で定義していますがどちらも同じように扱うことができます。
二つの違いはこのComp
というコンポーネントが複数回呼ばれた場合に、outerState
の方は一つのステートを複数のコンポーネントが参照する形になりますが、innerState
の場合はそれぞれのコンポーネントに特有のステートが生成されます。
ReactでいうRedux
やContext
のようにグローバルに近いレイヤーでのステート宣言が可能になります。
(SolidJSの場合はこれとは別にStore
やContext
の機能があり、また別の使い分けをすることが出来ます。)
createEffectも便利
もう一つ便利な点として、ステートが変更された場合にコールバックを実行するcreateEffect
という関数が、ReactのuseEffect
と違って二つ目の引数(依存関係の配列)を必要としません。
こちらも柔軟に設定が可能で、例えばb
というステートの変更は検知しなくていいが処理の中では使いたいという場合にはuntrack
という関数を使うことで可能になります。
(Svelteではこの辺が非常にめんどくさいので、SolidJSはよく考えられて作られていることが伺えます。)
const [a, setA] = createSignal('');
const [b, setB] = createSignal('');
createEffect(() => {
console.log(a());
});
createEffect(() => {
const v = a();
untrack(() => console.log(v, b()));
});
カスタムディレクディブ
Vue/Svelteにあるようなdirective機能もSolidJSでは使えます。
以下の例ではよく実装するようなデータバインディングをしてくれるmodel
というdirectiveを作成しています。
const [name, setName] = createSignal('');
function model(el: HTMLInputElement, value: () => Signal<string>) {
const [field, setField] = value();
createRenderEffect(() => (el.value = field()));
el.addEventListener('input', (e) => setField(e.target.value));
}
<input type="text" use:model={[name, setName]} />;
JSXの利便性
SvelteやVueと違い独自の拡張子/フォーマットで書くのではなくJSXでコンポーネントを書けるも便利な点です。
1ファイル1コンポーネントの制約がないので、1ファイルでいくつもコンポーネントを書けます。
またReactで便利だったHOC(高階コンポーネント)のようなことも実装することができます。
const C: Component<{
testSignal: Signal<string>;
}> = ({}) => {
return <></>;
};
const wrap = (
C: Component<{
testSignal: Signal<string>;
}>
) => {
const WrappedComponent: Component<{}> = () => {
const testSignal = createSignal('');
return <C testSignal={testSignal} />;
};
return WrappedComponent;
};
export const Wrapped = wrap(C);
useRefが必要ない
SolidJSの場合はReactのuseRef
が必要なく、以下のように直感的に書くことができます。
const CompRef = () => {
let el: HTMLDivElement | undefined = undefined;
return <div ref={el}></div>;
};
ErrorBoundary/Portal
Reactにもあった便利なコンポーネントがSolidJSでも標準でそろっています。
例えばコンポーネント内のエラーをキャッチしてくれるErrorBoundary
やDOMツリーの外にコンポーネントをレンダリングするときに使えるPortal
などがあります。
これらの機能はSvelteでは存在しなくて困っていいたので非常に助かりました。
その他細かい良い点
- チュートリアルがしっかりしている
- 日本語がやや怪しいけど充分読める
- 入門までは以下のURLから簡単にできる
- https://www.solidjs.com/guides/getting-started
- ライフサイクル(onMount, onCleanup, onError)
- この辺はほとんどReactと同じ
SolidJSの改善点
周辺ライブラリが少ない
まだ発展途上のフレームワークではあるので周辺ライブラリは圧倒的に少ないです。
特にアプリケーションを作るうえでは必須とも言っていいUIコンポーネントのライブラリはほとんど存在せず、solid-bootstrapというものだけ見つかりました。
https://solid-libs.github.io/solid-bootstrap/
この辺はライブラリが発展していくとともに充実していくことを期待しています。
props周りがややこしい
props周りでReactでやっていたような扱いをするとSolidJSのReactivityが失われるので注意です。
例えば次のような書き方では意図通りに動きません。
const C: Component<{ loading: boolean }> = (props) => {
const { loading } = props;
return <>{loading}</>;
};
ここでは親コンポーネントがloading
というプロパティを変更すれば、子コンポーネントの中身が変更されることを期待しますが、実際にはそうはなりません。
変更されるのはあくまでもprops
だけになっていしまうのでconst { loading } = props;
とした時点で、loading
のReactivityは失われます。
Reactivityを保持するには、props.xxx
と書くか、関数として定義するかの2択になります。
const C: Component<{ loading: boolean }> = (props) => {
return <>{props.loading}</>;
};
const C: Component<{ loading: boolean }> = (props) => {
const loading = () => props.loading;
return <>{loading()}</>;
};
この辺がややこしいので少し改善されるととっつきやすくなるかと思います。
styleのcamelCaseが出来ない
細かい点ですが、styleを指定する際にfont-size
のように途中にハイフンが入るような場合、ReactだとfontSize
という風にcamelCaseで指定できたのが、SolidJSだと'font-size'
という風にする必要があるのが地味に面倒だったりします。
オブジェクトで指定できるだけSvelteよりはマシなんですが、、、
const C: Component<{}> = (props) => {
return (
<div
style={{
'font-size': 12
}}
/>
);
};
最後に
少し前にSvelteに出会った際にすごい!と思いましたが、だんだん使っていくとReactだとこれができたのになーと思う部分もありました。
SolidJSはReactとSvelteの良いところどりをしているような感じがしたのでこれからもっと使っていこうと思います。