2022年現在に、Cloudflare Workersで(CDNエッジでWorkerで)SSRする方法は2つある。
- Cloudflare Pages(w/ Functions)
- Cloudflare Workers(w/ Workers Sites)
静的サイトホスティングサービスであるPages + Functionsで動的な部分を追加するというアプローチか、動的なWorkers + 静的なアセットを配信するためのWorkers Sitesという機能を使うかの2択。
で、この記事で調べたいのは、どっちのアプローチでもいいけどこの動的な部分でいわゆるSSRができるフレームワークたち。
誰だってある日突然、エッジでSSRしたい気持ちに駆られることってあるじゃないですか。というわけで、独断と偏見で調べたものをまとめた。
SvelteKit
Repo: https://github.com/sveltejs/kit
Docs: https://kit.svelte.dev/docs
- アダプタなる層によって、どこにデプロイするかを選べる
- SvelteKitは、そのまま使うとSSR+CSRという挙動になる
- `router`と`hydrate`の2つのフラグを、ページ単位/グローバルで指定して調整できる
- `router: false`にすると、クライアントルーターを使わずページ遷移が発生するようになりフルSSRできる
- `Link`コンポーネントみたいなものはなく、`a`要素を自動で拡張してくれる
- `hydrate: false`にすると、そのページでJSを使わないようになる
- `false`にしない場合、SSRで初期データを取得していたとしても、ハイドレーション時に再取得しようとするデザイン
- `fetch`の結果はインライン化されてるのでネットワークアクセスなしで返るけど
- `router: false` + `hydrate: false`にすると、0JSにできる
- ちょっとでもJSが欲しい場合、`hydrate: false`を諦めてハイドレーションするしかない
- もしくは、`script`を動的に後から挿入するとか・・
- ハイドレーションの単位はページごと
- ローカルでの開発
- Pagesのアダプタの場合、`miniflare`を使うには、それ用のコマンドを自作する必要がある
- 例: https://github.com/lukeed/pages-fullstack/commit/6e53dce1a638f128e86614b4d1e37c2fa95179b6
Remix
Repo: https://github.com/remix-run/remix
Docs: https://remix.run/docs
- おなじくアダプタなる層でデプロイ先を選べる
- Workers/Pagesを選べる
- https://github.com/remix-run/remix/tree/main/packages/remix-cloudflare-pages
- https://github.com/remix-run/remix/tree/main/packages/remix-cloudflare-workers
- `remix`コマンドの拡張といった立ち位置で、デプロイしたいプラットフォーム用に自分でバンドルしろというスタイル
- `Link`コンポーネントでリンクすると、クライアントルーターが仕事するようになる
- これを使わず`a`要素にした場合は、フルSSRできる
- このへんはReact Routerがそのまま頑張ってるらしい
- ページ単位で設定して、0JSにすることもできる
- 各Routeで`const handle = { hydrate };`というフラグを返して、`root`のコンポーネントで`script`を挿入しないように分岐させる
- https://remix.run/docs/en/v1/guides/disabling-javascript
- ハイドレーションの単位はページごと
- ただしNested routesというページ内の一部だけをルーティングする機能があるので、その限りではなさそう
- ローカルでの開発
Qwik
Repo: https://github.com/BuilderIO/qwik
Docs: https://github.com/BuilderIO/qwik/tree/main/docs
- Workers Sitesのみ
- https://github.com/BuilderIO/qwik/tree/main/starters/servers/cloudflare
- スターターで選べば、Cloudflare Workers用のエントリーとそれをビルドする一式が作られる
- 基本的にSSR
- というか、現時点ではルーターどころか複数のページに分ける方法すらも未定という感じ
- デモは単ページのものしかない
- 自分で`req.url`をパースして、それぞれ`renderToString()`して返すなりする
- ハイドレーションの単位はあらゆるパーシャルで遅延される
- ローカルでの開発は、スターター通りなら`wrangler dev`
- `miniflare`を使いたいなら自分でコマンドを指定する
Marko
Repo: https://github.com/marko-js/marko
Docs: https://markojs.com/docs
- Workers Sitesのみ
- https://markojs.com/docs/cloudflare-workers/
- これもアダプタが用意されてるというか、環境に依存しないのでよしなにやれというスタンス
- https://github.com/marko-js/examples/tree/master/examples/vite-cloudflare
- ここを見るに、自分でルーター書いて好きなようにレンダリングして返せという感じ
- 基本的にSSR
- ハイドレーションの単位はコンポーネントらしい
- ローカルでの開発は、スターター通りなら`wrangler dev`
- `miniflare`を使いたいなら自分でコマンドを指定する
Nuxt3
なんかうまく動かせなくてちゃんと試せなかった。
Repo: https://github.com/nuxt/framework
Docs: https://v3.nuxtjs.org/docs/usage/data-fetching
- デプロイ先は、Nitroというサーバーエンジンがよしなにするらしい
- その中に、Cloudflare(PagesではなくWorkers(Sites))のプリセットがある
- https://github.com/nuxt/framework/blob/main/packages/nitro/src/presets/cloudflare.ts
- https://v3.nuxtjs.org/docs/deployment/cloudflare/
- `NuxtLink`コンポーネントでリンクすると、クライアントルーターが動き出す
おわりに
どれがいいか
ただの感想コーナーです。
- SvelteKit
- Svelteなのはいい
- `load()`などデータ取得まわりが小難しい印象
- SSRメインというよりか、やはり`#TransitionalApps`を目指してるのだなあ
- Remix
- クラサバにばっさり分ける思想は好き
- ただしReactとReactRouter・・
- Reactである必要はないみたいな発言があった気がするけど、実際無理では?とも思う
- Qwik
- 他と比較するようなものではないかもしれないが、期待してる
- 今の時点では発展途上すぎるので、もう少し様子見
- Marko
- (伏兵だったが)すごく良くできてると思う
- なんで流行ってないの?時代はまだSPAを求めてるんか?
- 独自シンタックスの敷居が高いのは気になるけど、Svelteも同じようなもんやしな
- TypeScriptは半ば諦めモードかもしれない?
みんな違ってみんな良いので単純な比較は難しいな〜。書き味の他にも、SSR自体のパフォーマンスとか、バンドルサイズとか、プログレッシブエンハンスメントな具合とか、気にすべきポイントはいろいろあるし。
やっぱり技術選定の段階になったら、あらゆる選択肢でPoCを作ってみて最終選別するのが妥当かなーと。特定のどれかの熱狂的なファンでないなら。
Pages or Workers?
PagesかWorkersか?って比較になると、だいたいのケースでPagesを使いたいはず。
しかしその場合、未だベータな`wrangler pages dev`を使う必要があるという・・。
他にも、
- Workersで見れたメトリクスが見れなかったり
- 管理画面でしか環境変数が設定できなかったり
- デプロイ環境のセットアップが2分くらいかかったり
現状のPagesのイケてないところはちらちらある。
Workersの場合は、ブランチプレビューがないことと、KV依存なところ以外は文句ないかなーといった感じ。
ローカルでの開発を`miniflare`でいい感じにやるためのボトルネックになるのは、各フレームワークとどう協調させるかの部分。フレームワーク側のコンパイルが遅かったりするとDXが良くない・・。
かといって`miniflare`を使わない場合、KVやDOにつなげなかったり不便に感じるかもしれない。まあ割り切ってエッジWorkerにはデプロイしたいけど、Cloudflare特有の機能は使わない!っていう割り切りも、ロックイン回避としてはアリかなって気はする。APIは既存のやつがありますんで・・ってパターンも普通にあると思うし。