SolidJS + TailwindCSS + pnpm + Vite & Astro を試してみる

## はじめに

SolidJS と呼ばれる JavaScript フレームワークが個人的に話題です。
一体どんなものなのか、実際にコードを動かしてみました。
また、SolidJS を学ぶ過程で遭遇した TailwindCSS, pnpm, Vite, Astro というような最新ライブラリについても触れていきます。

[2022.stateofjs.com](https://2022.stateofjs.com/en-US/libraries/) によると、今回取り上げるツールらは全て TIER S と堂々たる満足度を獲得しています。（SolidJS と Astro はまだユーザー数が少なく表からは除外されているようですが、スコアは 90%を超えており間違いなく S 級です。CSS は対象外。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343913/0ca4b95e-81cd-181f-eb47-eb25f950887e.png)

## 各ツールについて

### SolidJS

<iframe width="1364" height="665" src="https://www.youtube.com/embed/hw3Bx5vxKl0" title="Solid in 100 Seconds" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

https://www.solidjs.com/

SolidJS は、React や Vue のような宣言的 JavaScript フレームワークの一種です。
2018 年に公開され、2021 年には React, Vue を抜いて[満足度 No.1](https://2022.stateofjs.com/en-US/libraries/front-end-frameworks/)を獲得しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343913/4a1b50e4-20af-0d7e-e6ad-c9bcac762146.png)

#### 特徴

##### 高速

SolidJS は Vanilla JS と遜色ない速度で処理することができます。
仮想 DOM を利用しないことで実現しているそうです。
（元々、高速化のために利用されてきた仮想 DOM を手放したんですねぇ）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343913/5681542f-9030-5634-1512-ec577661720d.png)

##### React 風

SolidJS は、特に React と Knockout という巨人の肩の上に立っています。
そのため React が馴染みのある方は SolidJS も抵抗なく学習することができるでしょう。
（筆者は React を触ったことがないので、その辺りの感覚は分かりません。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343913/a071f7a5-61b2-c2ec-cbad-c542e708d1ef.png)

### Tailwind CSS

https://tailwindcss.com/

Tailwind CSS はユーティリティクラスを利用した CSS フレームワークです。
（ユーティリティクラスとは、クラス名がそのまま css のプロパティとその値を表現するクラスです。）
これにより、class を管理したり、命名したりする手間を省くことができます。
Bootstrap を味気ないな、と感じている方にも気に入ってもらえること間違いなし。

```html
<p class="text-lg font-medium">
    “Tailwind CSS is the only framework that I've seen scale
    on large teams. It’s easy to customize, adapts to any design,
    and the build size is tiny.”
</p>
```

以下のサイトで、コードを実際に動かしてみることができます。

https://play.tailwindcss.com/0lwX7g6OLo

### pnpm

https://pnpm.io/ja/

pnpm は高速、かつディスク容量効率が良いパッケージマネージャーです。
pnpm では、複数プロジェクト間で共通するファイルを一元管理しています。
そのため、インストールが高速で、かつ容量消費を抑えることができます。

[Benchmarks of JavaScript Package Managers](https://github.com/pnpm/benchmarks-of-javascript-package-managers/blob/main/README.md#lots-of-files) によるベンチマークは以下のようになっており、pnpm が高速であることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343913/0d383558-c038-bfa4-9931-d2b292dbf96d.png)
また、pnpm はパッケージ管理が厳格で、package.json に記載のあるものにしかアクセスできません。
[「ささいな間違いを犯さないようにすること (helps to avoid silly bugs) 」](https://pnpm.io/ja/pnpm-vs-npm)だそうです。
個人的には、とても嬉しい機能ですね。

### Vite

https://ja.vitejs.dev/guide/

Vite は、2020 年に公開された、より速く無駄のない開発体験を提供することを目的としたビルドツールです。
フランス語で「素早い」という意味の単語でヴィートのように発音します。
ビルドツールの有名なものとして 、webpack・Rollup・Parcel などがあります
Vite は Vue.js・Svelte・React・SolidJS など、さまざまなフレームワークで利用可能です。

Vite はモジュール分割の仕組みである「ES Modules」を利用して、修正や変更が加えられたソースだけをコンパイルしてブラウザにレンダリングします。
これにより、大規模プロジェクトでも起動や更新が高速で行えるようになっています。

### Astro

https://astro.build/

Astro は開発時に利用する JavaScript をできるだけ除外した HTML ファイルを作成してくれる静的サイトジェネレーターです。
静的サイトジェネレーターの有名なものとして Next.js ・ Gatsby ・ Hugo ・ NuxtJS ・ Jekyll などがあります。

JS を取り除くことで、高速なレンダリング・ロードデータサイズの削減が可能となっています。
[公式サイト](https://astro.build/)によると Next.js を利用したブログサイトを Astro を使ってビルドすれば、ページロードサイズが 1/10 以上削減できるようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343913/d15b5f69-6212-f6b4-b67f-83dd4988561b.png)
また、カルーセルやショッピングカードなどクライアント側で必要な JavaScript がある場合はコンポーネント単位で JavaScript を利用するかどうかを設定することが可能です。
Astro も Vite と同様に、Vue.js・Svelte・React・SolidJS など、さまざまなフレームワークで利用可能です。

Astro は Vite を利用してビルドしており、Astro チームは現在、エコシステムでの活発なプレイヤーであり、Vite の改良に貢献しているそうです。（[参考](https://ja.vitejs.dev/guide/comparisons.html#web-dev-server)）

## 実際にコードを動かしてみる

### SolidJS With Vite

まずは、SolidJS + TailwindCSS + pnpm + Vite な環境を構築してみます。
実験に利用したコードはこちらに置いています。

https://github.com/shoot16625/solidjs-sample/tree/main/vite

各種設定については Readme と個々の設定ファイルを見ていただけると幸いです。

要点は以下になります。

```tsx:index.tsx
import { createSignal, onCleanup } from "solid-js";
import { render } from "solid-js/web";

import { Header } from "./header";
import { FibonacciComponent } from "./fibonacci";
import { RepeatingComponent } from "./repeat";
import "./index.css"; // TailwindCSSを扱えるようにしている

// 1秒ごとにカウントするコンポーネント
const CountingComponent = () => {
  const [count, setCount] = createSignal(0);
  const interval = setInterval(() => setCount((count) => count + 1), 1000);
  onCleanup(() => clearInterval(interval));
  return <div class="text-red-500">Count value is {count()}</div>;
};

const App = () => {
  return (
    <>
      <Header />
      <CountingComponent />
      <FibonacciComponent />
      <RepeatingComponent />
    </>
  );
};

render(() => <App />, document.getElementById("root"));
```

`import { createSignal, onCleanup } from "solid-js"`の部分が SolidJS です。まんまですね笑。
`class="text-red-500"` の部分が Tailwind CSS のユーティリティクラスですね。後ろについている`500`は色の濃さを表しています。
サンプルコードは、`pnpm vite`で起動することができます。pnpm で必要なパッケージをインストールし、Vite で Typescript などをコンパイルしています。

Vite はソースコードの変更部分だけをコンパイルして高速化を図っていると紹介しました。実際に Vite の部分更新機能を感じたい場合は、例えば、`fibonacci.tsx`というフィボナッチ数列の値を返すコンポーネントをもつファイルを更新します。すると、`<FibonacciComponent />`の部分だけが html 上で更新され、1 秒毎にカウントを描画している`<CountingComponent />`の結果は、0 から始まること無く継続して描画されることがわかるでしょう。

### SolidJS With Astro

次に、SolidJS + TailwindCSS + pnpm + Astro な環境を構築してみます。
実験に利用したコードはこちらに置いています。

https://github.com/shoot16625/solidjs-sample/tree/main/astro

（実験当初は SolidJS + TailwindCSS + pnpm + Vite + Astro という構成になるのかな？と試行錯誤しておりました。 [Astro に Vite を追加する](https://docs.astro.build/ja/reference/configuration-reference/)項目があるので無くもないんでしょうが。）

まず、 Astro は開発環境が爆速で構築できてすごいです。
以下のコマンドだけで、Typescript も SolidJS も Tailwind CSS も準備完了です。
サンプルコードは、`pnpm astro dev`で起動することができます。

```sh
# create project
pnpm create astro@latest
pnpm astro add solid tailwind
```

要点は以下になります。

```tsx:index.astro
---
import Layout from '../layouts/Layout.astro';
import { CountingComponent } from "../components/counter";
import { FibonacciComponent } from "../components/fibonacci";
import { RepeatingComponent } from "../components/repeat";
import "../layouts/index.css";
---

<Layout title="Welcome to Astro.">
	<main>
		<h1 class="text-blue-400">Welcome to <span class="text-gradient">Astro</span></h1>
		<CountingComponent client:only="solid-js" />
		<FibonacciComponent client:only="solid-js" />
		<RepeatingComponent />
	</main>
</Layout>
```

Astro には `.astro`拡張子を用いた[astro 記法](https://docs.astro.build/ja/core-concepts/astro-components/)が存在します。
`---`で囲まれた部分をコンポーネントスクリプトと呼び、JS を記述する部分になります。
その下の部分をコンポーネントテンプレートと呼び、HTML の定義部分になります。
JSX と同じように、HTML と JavaScript の式を混ぜることも可能です。

Astro で JS を除去したくない場合は、`client:only="solid-js"`を設定する必要があります。（JS をガリガリ使う開発だと地味に面倒くさいかもしれませんね）
このような、一部のコンポーネントだけ JS を許可する仕組みは Partial Hydration と呼ばれています。

では、Astro が JS を剥いでいる様子を実際に確認してみましょう。
`<RepeatingComponent />`の中身は以下のコードのようになっていて、SolidJS の [For](https://www.solidjs.com/tutorial/flow_for) を使って li コンポーネントを複数生成しています。

```tsx:repeat.tsx
import { For, createSignal } from "solid-js";

export const RepeatingComponent = () => {
  const [cats, setCats] = createSignal([
    { id: "J---aiyznGQ", name: "Keyboard Cat" },
    { id: "z_AbfPXTKms", name: "Maru" },
    { id: "OUtn3pvWmpg", name: "Henri The Existential Cat" },
  ]);
  return (
    <ul>
      <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>
    </ul>
  );
};
```

こちらを実際に build してみると、一般的にはクライアント側で JS として処理し html に描画することになりますが、Astro の場合は index.html に展開されていることが確認できました。

```html:index.html
<main class="astro-7T72KUKR">
		<h1 class="text-blue-400 astro-7T72KUKR">Welcome to <span class="text-gradient astro-7T72KUKR">Astro</span></h1>
		<astro-island uid="Z1aC2eD" component-url="/counter.8bb4d4c5.js" component-export="CountingComponent" renderer-url="/client.085eaeb9.js" props="{&quot;class&quot;:[0,&quot;astro-7T72KUKR&quot;]}" ssr="" client="only" opts="{&quot;name&quot;:&quot;CountingComponent&quot;,&quot;value&quot;:&quot;solid-js&quot;}"></astro-island>
		<astro-island uid="Z25u9K5" component-url="/fibonacci.db277ddc.js" component-export="FibonacciComponent" renderer-url="/client.085eaeb9.js" props="{&quot;class&quot;:[0,&quot;astro-7T72KUKR&quot;]}" ssr="" client="only" opts="{&quot;name&quot;:&quot;FibonacciComponent&quot;,&quot;value&quot;:&quot;solid-js&quot;}"></astro-island>
ココ ->	<ul data-hk="0-0"><li data-hk="0-1-0"><a target="_blank" href="https://www.youtube.com/watch?v=J---aiyznGQ"><!--#-->1<!--/-->: <!--#-->Keyboard Cat<!--/--></a></li><li data-hk="0-1-1"><a target="_blank" href="https://www.youtube.com/watch?v=z_AbfPXTKms"><!--#-->2<!--/-->: <!--#-->Maru<!--/--></a></li><li data-hk="0-1-2"><a target="_blank" href="https://www.youtube.com/watch?v=OUtn3pvWmpg"><!--#-->3<!--/-->: <!--#-->Henri The Existential Cat<!--/--></a></li></ul>
</main>
```

## おわりに

筆者は、普段はバックエンド多めの活動をしているので、フロントエンドで触ったことがあるのは Vue, Nuxt, Typescript, Bootstrap, Onsen UI くらいです。そんなに強くはないです。
とりあえず、Typescript の環境構築が一瞬で感動です。。。
Astro と Vite は似たようなツールですが、どういった場面でどちらを使っていけばいいのか、悩ましそうですね。


## 参考サイト

- [SolidJS + vite + tailwindcss な環境を作る（Storybook, vitest 対応）](https://blog.hey3.dev/posts/create-solidjs-template)
- [宣言的 UI とは](https://zenn.dev/arei/articles/f59e263aa3edf2)
- [利用者爆増中 初めてでもわかる Tailwind CSS 入門 基礎編](https://reffect.co.jp/html/tailwindcss-for-beginners)
- [pnpm の特徴](https://zenn.dev/azukiazusa/articles/pnpm-feature)
- [Astro(Static Site Genarator) 好きなフレームワークで高速な WEB サイト構築](https://reffect.co.jp/html/astro)
- [Astro 使ってみたのでその所感](https://zenn.dev/yend724/articles/20220701-430vndv085eh6xn2)
- [@astrojs/solid-js Astro ドキュメント](https://docs.astro.build/ja/guides/integrations-guide/solid-js/)