nazo6 notememo

solidjsを試してみる

作成:2021/07/24

更新:2021/07/24

日本語ドキュメントができました (2021-07-24)

なのでこんなスクラップを見るよりこっちを見ましょう
https://www.solidjs.com/docs/latest/api?lang=ja

https://www.solidjs.com/guides/getting-started

www.solidjs.com

SolidJS の日本語ドキュメントが公開されました

zenn.dev

(2021-06-03)

仮想DOMはパフォーマンスが悪いみたいな記事を見てへーと思って非仮想DOMのなにかを試してみようと思った
この分野だと間違いなくSvelteが一番ポピュラーで人気上昇中だがどうしてもあのテンプレート構文的なのが好きになれない・・・
というわけで見つけたのが
https://github.com/solidjs/solid
Reactと同じようにjsxが使えてそれでいて仮想DOMを使わないライブラリとのこと。もうすぐ1.0リリースらしい。もうrcに到達してる

(2021-06-03)

情報があまりなかったのでためらっていたが絶賛制作中の公式サイトを見つけた
https://github.com/solidjs/solid-site/tree/dev
のでそこにあるチュートリアルを一足先にやっていく
おそらく1.0に合わせて公開される気がするのでもうすぐなにもしなくても
https://solidjs.com
から見れるようになる気がする

(2021-06-03)

公式サイトにももちろんsolidjsが使われていて、さらにバンドラーにはViteを使っているようだ。Vite用のプラグインもある。(https://github.com/solidjs/vite-plugin-solid)
ローカルで開発中のサイトを起動する
$ git clone https://github.com/solidjs/solid-site
$ git checkout dev
$ pnpm i
$ pnpm dev
https://localhost:3000 にアクセスするといい感じのサイトが立ち上がっている。
https://storage.googleapis.com/zenn-user-upload/2c2008f5a35a47dda3b0ed8e.png
やはりViteはビルドが爆速で素晴らしい

(2021-06-03)

トップページを見るといろいろよさげな事が書いてある。
リアクティブプログラミングや単方向データバインディングなどのReactの哲学に従いつつ仮想DOMではない方法でレンダリングをしているとのこと。
また、コンポーネントは1度しか実行されず、hooksとバインディング(effectとかのこと?)が再実行されるらしい。Reactはコンポーネントが再実行されるんだったような気がするからそこが違いなんだろうか
Resoucesのページにはsolidjsの作者の記事がいろいろ載っている。
https://dev.to/this-is-learning/components-are-pure-overhead-hpm

(2021-06-03)

まだ開発中のようだがTutorialがしっかり整備されていてMonacoエディタで快適にチュートリアルを進めることができる。
基本のカウンターアプリはこんな感じ。(以下のコードは全てチュートリアルからのコピペ)
import { render } from "solid-js/web";
import { createSignal } from "solid-js";

function Counter() {
const [count, setCount] = createSignal(0);

setInterval(() => setCount(count() + 1), 1000);

return <div>Count: {count()}</div>;
}

render(() => <Counter />, document.getElementById('app'));
createSignalはパッと見hooksだがsolidjsにはhooksのルールがないので自由な場所に書けるらしい。
またuseStateとは違い、値ではなくgetterが返ってくる。

(2021-06-03)

Effectもある
import { render } from 'solid-js/web';
import { createSignal, createEffect } from 'solid-js';

function Counter() {
const [count, setCount] = createSignal(0);
createEffect(() => {
console.log("The count is now", count());
});

return <button onClick={() => setCount(count() + 1)}>Click Me</button>;
}

render(() => <Counter />, document.getElementById('app'));
createMemoでメモ化もできる。useMemoと同じような感じかな?

制御フロー (2021-06-03)

ここでShowコンポーネントという見慣れない物が登場
<Show
when={loggedIn()}
fallback={<button onClick={toggle}>Log in</button>}
>
<button onClick={toggle}>Log out</button>
</Show>
仮想DOMを使わない代償として、jsxでそのままArray.prototype.mapとかやると全てのコンポーネントが再レンダリングされてしまうため、このような専用コンポーネントを用意しているようだ。
ただSolidのコンパイラは三項演算子をサポートしているとも書いてある。Showコンポーネントを使わなくてもいいということだろうか?

(2021-06-03)

Showコンポーネントは出し分けをするだけだが他にも制御構造用のコンポーネントがいろいろある。
For
<For each={cats()}>{(cat, i) =>
<li>
<a target="_blank" href={`https://www.youtube.com/watch?v=${cat.id}`}>
{i() + 1}: {cat.name}
</a>
</li>
}</For>
Index
<Index each={cats()}>{(cat, i) =>
<li>
<a target="_blank" href={`https://www.youtube.com/watch?v=${cat().id}`}>
{i + 1}: {cat().name}
</a>
</li>
}</Index>
ForとIndexの違いとして各アイテムとindexのプリミティブとgetterという関係が逆になっている。
恐らくForでは配列の中身に基いて再レンダリングが行われ、Indexではindexに基いて再レンダリングが行われている(日本語が下手)
なので非プリミティブな配列にはForを使い、プリミティブなものにはIndexを使うとよいとのこと。

(2021-06-03)

次にSwitchコンポーネント
<Switch fallback={<p>{x()} is between 5 and 10</p>}>
<Match when={x() > 10}>
<p>{x()} is greater than 10</p>
</Match>
<Match when={5 > x()}>
<p>{x()} is less than 5</p>
</Match>
</Switch>
正直いままでのもテンプレート構文味があって若干微妙に感じていたが、これに関してはtypescriptの型が正常に判定されないためかなりビミョーだと思った。
https://github.com/solidjs/solid/issues/199
一応ワークアラウンドはあるようだがいちいちこれを書くのかという感じでうーん

(2021-06-03)

Portalコンポーネント
<Portal>
<div class="popup">
<h1>Popup</h1>
<p>Some text you might need for something or other.</p>
</div>
</Portal>
これはReactのポータルと同じような感じかな
ErrorBoundaryコンポーネント
import { render } from "solid-js/web";
import { ErrorBoundary } from "solid-js";

const Broken = (props) => {
throw new Error("Oh No");
return <>Never Getting Here</>
}

function App() {
return (
<>
<div>Before</div>
<ErrorBoundary fallback={err => err}>
<Broken />
</ErrorBoundary>
<div>After</div>
</>
);
}

render(() => <App />, document.getElementById("app"));
ReactのError Boundaryと同じように例外をキャッチできるものだが、Reactのようにクラスコンポーネントでないと使えないみたいな制限はない。そもそもsolidjsには関数コンポーネントしかないはず

ライフサイクル (2021-06-03)

solidのライフサイクルメソッドにはonMountonCleanupがある。
特筆すべきことはなさそう?

バインディング (2021-06-04)

style

<div style={{
color: `rgb(${num()}, 180, ${num()})`,
"font-weight": 800,
"font-size": `${num()}px`}}
>
Some Text
</div>
Reactのようにstyleプロパティにオブジェクトを入れてスタイルを適用できるが、Reactと違いcamelCaseではなく、普通のhtmlで指定するのと同じkebab-caseを使う。
これの利点としてcss変数が使える。なるほど。
<div style={{ "--my-custom-color": state.themeColor }} />

(2021-06-04)

class

classNameではなく普通にclassが使える、またclassListというものもある。
<button
class={current() === 'foo' ? 'selected' : ''}
onClick={() => setCurrent('foo')}
>foo</button>

<button
classList={{selected: current() === 'foo'}}
onClick={() => setCurrent('foo')}
>foo</button>

ref/forwardRef (2021-06-04)

特に難しいことをしなくても使える。useRefみたいなのはいらない。
let myDiv;

<div ref={myDiv}>My Element</div>

カスタムディレクティブ (2021-06-04)

(element, valueAccesor)を受け取る関数をインポートして接頭辞としてuse:を関数名の前につけたディレクティブを設定することでカスタムディレクティブを使える。ReactにはないがVueとかにはあるようだ。
便利そうではあるが標準から外れているのでサンプルではdead codeとして削除されないようにしてたりするなどちょっと面倒臭いことがありそう。

Props (2021-06-04)

// あとで読む

🎉1.0 (2021-06-29)

公式サイトも公開されていた。これで使う人が増えるかも?
https://www.solidjs.com