Cloudflare Workers と Client Side Rendering
ここでの前提は Cloudflare Workers であり、Cloudflare Pages ではない。どうしても既存の Cloudflare Workers を活かしつつ、複雑なフロントエンドページを作成する必要が出てきた時のためにアイディアを残す。
できることなら Cloudflare Pages の利用をお勧めします。
pnpm を使った workspace モノレポ
リポジトリルートから見たツリー
.
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── projects
├── frontend
└── api
frontend
frontend project は Vite を利用している。cd projects && npx create vite
で作成した React + TypeScript で初期化した。
vite.config.ts
のみ修正
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
build: {
outDir: "../api/assets/public",
},
});
リポジトリルートから pnpm -F frontend run vite
や rm -rf projects/api/assets/public && pnpm -F frontend run vite build
を実行してバンドルされた静的ファイルを配信する。
api
api はもちろん Cloudflare Workers である。静的ファイルを Workers Site の機能を使って配信する。
[site]
bucket = "./assets"
開発環境
Hono を使う。
フロントエンド側のファイルが更新されたら Vite の HMR を使って開発サーバーがリロードされるように少し工夫する。
フロントエンドのコードがビルドされるとプレビューも更新されるように HTML を編集する。
このコードは Vite の Backend Integration を参考にした。
ローカル環境では .dev.vars
を用意して VITE_SERVER=http://localhost:5173
のように変数を追加する。本番では VITE_SERVER
に値を追加しない。
type Env = {
VITE_SERVER?: string;
};
const app = new Hono<{ Bindings: Env; }>()
app.get('/public/*', serveStatic({ root: './' }));
app.get(
'/',
jsxRenderer(
({ children, viteServer }) => {
const clientScript = ViteReactScriptTags({ viteServer });
return (
<html>
<head>{clientScript}</head>
<body>{children}</body>
</html>
);
},
{ docType: true },
),
async (c) => {
return c.render(
<>
<div id="root"></div>
<script
type="module"
src={`${c.env.VITE_SERVER}/src/main.tsx`}
></script>
</>,
{
viteServer: c.env.VITE_SERVER,
},
);
},
);
const ViteReactScriptTags: FC<{ viteServer?: string }> = ({ viteServer }) => {
if (viteServer === undefined) {
return html``;
}
return html`<script type="module" src="${viteServer}/@vite/client"></script>
<script type="module">
import RefreshRuntime from '${viteServer}/@react-refresh';
RefreshRuntime.injectIntoGlobalHook(window);
window.$RefreshReg$ = () => {};
window.$RefreshSig$ = () => (type) => type;
window.__vite_plugin_react_preamble_installed__ = true;
</script>`;
};
これでは不十分で、assets から manifest.json
を読み取ってその内容に応じて script タグや style タグを動的に追加する必要がある。本番では VITE_SERVER
に値を追加しないので、VITE_SERVER === undefined
の時にこの動作を行うコードを追加する必要がある。
(割愛)
全然関係ないけど、 Workers Site は KV に静的ファイルが保存されるので、バンドルサイズに影響はない。
こんな感じ