🧊

Cloudflare WorkersでSSRできるフレームワークを求めて

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という挙動になる
    • 初回ロードは、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`を動的に後から挿入するとか・・
  • ハイドレーションの単位はページごと
  • ローカルでの開発

Remix

Repo: https://github.com/remix-run/remix
Docs: https://remix.run/docs

  • おなじくアダプタなる層でデプロイ先を選べる
  • `Link`コンポーネントでリンクすると、クライアントルーターが仕事するようになる
    • これを使わず`a`要素にした場合は、フルSSRできる
    • このへんはReact Routerがそのまま頑張ってるらしい
  • ページ単位で設定して、0JSにすることもできる
  • ハイドレーションの単位はページごと
    • ただしNested routesというページ内の一部だけをルーティングする機能があるので、その限りではなさそう
  • ローカルでの開発
    • Pagesのアダプタの場合: `wrangler@beta pages dev`を使う(ただし未だベータ)
    • Workersのアダプタの場合: `remix watch` + `miniflare -w`のセットで

Qwik

Repo: https://github.com/BuilderIO/qwik
Docs: https://github.com/BuilderIO/qwik/tree/main/docs

  • Workers Sitesのみ
  • 基本的にSSR
    • というか、現時点ではルーターどころか複数のページに分ける方法すらも未定という感じ
    • デモは単ページのものしかない
    • 自分で`req.url`をパースして、それぞれ`renderToString()`して返すなりする
  • ハイドレーションの単位はあらゆるパーシャルで遅延される
  • ローカルでの開発は、スターター通りなら`wrangler dev`
    • `miniflare`を使いたいなら自分でコマンドを指定する

Marko

Repo: https://github.com/marko-js/marko
Docs: https://markojs.com/docs

  • Workers Sitesのみ
  • 基本的にSSR
    • MPAを作るためのフレームワークなので
    • クライアントのルーターなんかなくても、チューニングされたSSRで速いという触れ込み
    • 同期のSSR以外に`stream()`というAPIもあって、レスポンスを`ReadableStream`で返せる
  • ハイドレーションの単位はコンポーネントらしい
  • ローカルでの開発は、スターター通りなら`wrangler dev`
    • `miniflare`を使いたいなら自分でコマンドを指定する

Nuxt3

なんかうまく動かせなくてちゃんと試せなかった。

Repo: https://github.com/nuxt/framework
Docs: https://v3.nuxtjs.org/docs/usage/data-fetching

おわりに

どれがいいか

ただの感想コーナーです。

  • 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`を使う必要があるという・・。

Cloudflare Pages Functionsを試す - console.lealog();

他にも、

  • Workersで見れたメトリクスが見れなかったり
  • 管理画面でしか環境変数が設定できなかったり
  • デプロイ環境のセットアップが2分くらいかかったり

現状のPagesのイケてないところはちらちらある。

Workersの場合は、ブランチプレビューがないことと、KV依存なところ以外は文句ないかなーといった感じ。

ローカルでの開発を`miniflare`でいい感じにやるためのボトルネックになるのは、各フレームワークとどう協調させるかの部分。フレームワーク側のコンパイルが遅かったりするとDXが良くない・・。

かといって`miniflare`を使わない場合、KVやDOにつなげなかったり不便に感じるかもしれない。まあ割り切ってエッジWorkerにはデプロイしたいけど、Cloudflare特有の機能は使わない!っていう割り切りも、ロックイン回避としてはアリかなって気はする。APIは既存のやつがありますんで・・ってパターンも普通にあると思うし。