🪨

SolidjsでSSRとかSSGとかできるフレームワーク、SolidStartをざっくり紹介

2022/09/25に公開

SolidjsとSolidStartが個人的にかなり来てるので、今回はSolidStartでSSGだったりSSRだったりをする方法を紹介します。

👇SolidStart
https://github.com/solidjs/solid-start

👇Solidjs
https://github.com/solidjs/solid

とりあえず動かしてみる

まずはとりあえずで動かします。
超ざっくり解説です。

Create Solid !!

Solidjsプロジェクトをつくります。
パッケージマネージャーはお好きなものを。今回はpnpmで行きます

pnpm create solid

テンプレートを選択します。今回はとりあえずbareで。

そのほかのオプションはこんな感じで行きます。
Server Side Rendering?... はSSRするかどうか。
Use TypeScript?... はTypeScriptを利用するかどうか。


ファイルが生成されて入れば成功です。

Run SolidStart !!

とりあえず開発サーバーを立ち上げてみます。

pnpm install
pnpm dev

こんな感じになれば成功です。

アクセスするとこんな感じに。

Build SolidStart !!

とりあえずビルドしてみます。

pnpm build

ログが流れて...

.solidディレクトリと、distディレクトリが生成されます。

Start SolidStart !!

とりあえず動かしてみます。

pnpm start

動きました。

くわしく

ちょこっと詳しく説明します。

SolidStartって?

This is the home of the Solid app framework. This is still a work in progress. Many features are missing or incomplete. Experimental status does not even mean beta status. Patch releases will break everything.

Solidjsのアプリケーションフレームワークです。まだまだ開発段階のもので、足りない機能だったり不完全な機能だったりがあります。めちゃめちゃ頻繁に更新されています。

👇主な機能はこんな感じ

  • ファイルシステムベースのルーティング
  • すべてのレンダリング モードをサポート(SSR, SSG, CSR)
  • すべての一般的なプラットフォームに展開するためのアダプター
  • CSS モジュール、SASS/SCSS サポート
  • TypeScriptファースト

もちろんCloudflareWorkersでもSSRできます。


アダプター

SolidStartは一般的なプラットフォーム(Vercel, Cloudflare Pages/Workers, ...)をサポートするアダプターを持っています。

アダプターの変更はvite.config.tsから

import solid from "solid-start/vite";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [solid({
    adapter: 'solid-start-node', // ここにアダプターの名前を
  })],
});

何も指定していない場合はインストールされているアダプターを検出して自動的に選択されます。

複数アダプタがある場合に何も指定されていない場合は...
気になる方は実験してみてください(?)

👇現在公開されているアダプタ

  • solid-start-cloudflare-pages
  • solid-start-cloudflare-workers
  • solid-start-deno
  • solid-start-netlify
  • solid-start-node
  • solid-start-static
  • solid-start-vercel

ちなみにSSGはsolid-start-staticを使えば簡単にできます。


root.tsx

next.jsでいう_document.tsxみたいな?

SolidStartはHtml, Head, Link, Meta... 等のコンポーネントを持っていて、それらを使ってHtmlを構築していきます。

solidjs/solid-start/examples/bare/src/root.tsx
// @refresh reload
import { Suspense } from "solid-js";
import {
  Body,
  ErrorBoundary,
  FileRoutes,
  Head,
  Html,
  Meta,
  Routes,
  Scripts,
  Title
} from "solid-start";
import "./root.css";

export default function Root() {
  return (
    <Html lang="en">
      <Head>
        <Title>SolidStart - Bare</Title>
        <Meta charset="utf-8" />
        <Meta name="viewport" content="width=device-width, initial-scale=1" />
      </Head>
      <Body>
        <Suspense>
          <ErrorBoundary>
            <a href="/">Index</a>
            <a href="/about">About</a>
            <Routes>
              <FileRoutes />
            </Routes>
          </ErrorBoundary>
        </Suspense>
        <Scripts />
      </Body>
    </Html>
  );
}

デフォルトだとこんな感じです。
<Scripts />はHydration用のスクリプト、<FileRoutes /> はnextjsの_document.tsxでいう<Main />ですね。src/routes配下の各ページのコンポーネントです。

おまけ

ちなみにstyled-componentのような、サーバー側でcssをあらかじめ書き出しておく必要がある場合、solid-js/web<Assets />を使うと便利です。

export default function Root() {
  return (
    <Html lang="en">
      <Head>
        <Title>SolidStart - Bare</Title>
        <Meta charset="utf-8" />
        <Meta name="viewport" content="width=device-width, initial-scale=1" />
+       <Assets>
+         {/* この時点では`extractCss`は呼び出されない */}
+         <style id="_goober" innerHTML={extractCss()} />
+       </Assets>
      </Head>
      <Body>
        <Suspense>
          <ErrorBoundary>
            <a href="/">Index</a>
            <a href="/about">About</a>
            <Routes>
              <FileRoutes />
            </Routes>
          </ErrorBoundary>
        </Suspense>
        <Scripts />
      </Body>
    </Html>
  );
}

<Assets />のドキュメントが見当たらなかったので正しい仕様は理解できていないのですが、これを使用するとHtml内のコンポーネントがすべてレンダリングされてから一番最後に<Assets />の内部がレンダリングされます。
どなたか詳しい方いたら教えてほしいです;;


routeData

nextjsでいうgetServerSideProps的な奴です。サーバー側で処理されます。

solidjs/solid-start/examples/with-auth/src/routes/index.tsx
import { useRouteData } from "@solidjs/router";
import { createServerAction$, createServerData$, redirect } from "solid-start/server";
import { getUser, logout } from "~/db/session";

export function routeData() {
  return createServerData$(async (_, { request }) => {
    const user = await getUser(request);

    if (!user) {
      throw redirect("/login");
    }

    return user;
  });
}

export default function Home() {
  const user = useRouteData<typeof routeData>();
  const [, { Form }] = createServerAction$((f: FormData, { request }) => logout(request));

  return (
    <main class="w-full p-4 space-y-2">
      <h1 class="font-bold text-3xl">Hello {user()?.username}</h1>
      <h3 class="font-bold text-xl">Message board</h3>
      <Form>
        <button name="logout" type="submit">
          Logout
        </button>
      </Form>
    </main>
  );
}

routeData関数内はサーバー側で実行され、useRouteDataを使って返り値を取得できます。

Dynamic Routes

nextjsのDynamic Routesとほぼ同じです。

たとえば

src/routex/pid/[pid].tsx
import { useParams } from "solid-start";

const Post = () => {
  const params = useParams()

  return <p>Post: {params.pid}</p>
}

export default Post

こんなのを作って、/pid/1にアクセスすると...

みたいになります。

まとめ

先日0.1.0がリリースされたので、ちょっとまとめてみました。
まだまだ開発段階ですので、今後もっと良くなっていく思うと、とても楽しみです。
Solidjsたのしい!!!!!

Discussion