Quick Start を走らせるだけとまではいきませんが、AIの支援により1時間足らずで移行できました。
今までこのブログは Cloud Run で動作していました。
Next.js を OpenNext で動かすと、パフォーマンスが大幅に悪化するためです。
しかし、Cloud Run で動かすと Cloudflare を介さず直接接続できてしまいます。
Compute Engine で Cloudflare Tunnel を動かして内部アクセスすることもできますが、月数ドルかかってしまうため、そのままにしていました。
そんなところに2月24日、Cloudflare が vinext をリリースしました。
Vite をベースに Next.js の公開APIを再実装することで、工数を抑えながらビルド速度改善やバンドルサイズの削減を実現しています。
また、Next.js の破壊的変更にも追従しやすいとして期待されています。
Next.js の充実したテストスイートを通過することを目標にAIに実装させたところ、1人のエンジニアによってわずか1週間で開発できたことも注目に値します。
非常に興味が湧いたので、どれだけ簡単に vinext を導入できるか、そしてどれだけのパフォーマンスが得られるかをこのブログを用いて検証しました。
導入
プロジェクトの README を見ると、Agent Skill とプロンプトが最初に目に入ります。
もはや人間が環境構築する必要はないようです。
これを実行したところ、
- パッケージのインストール
- npm スクリプトの追加
- ESLint ルールの無効化
が行われました。
しかし、一発ではデプロイできず、次の問題が発生しました。
Radix UI を 'use client' で再exportしないといけない
<code>1:20:09 PM [vite] Internal server error: React.createContext is not a function
at eval
(/home/user/projects/blog-v3/node_modules/@radix-ui/react-direction/src/direction.tsx:4:32)
at async ESModulesEvaluator.runInlinedModule
(file:///home/user/projects/blog-v3/node_modules/vite/dist/node/module-runner.js:913:3)
at async ModuleRunner.directRequest
(file:///home/user/projects/blog-v3/node_modules/vite/dist/node/module-runner.js:1146:59)
at async ModuleRunner.cachedRequest
(file:///home/user/projects/blog-v3/node_modules/vite/dist/node/module-runner.js:1053:73)
at async eval (/home/user/projects/blog-v3/node_modules/radix-ui/src/index.ts:10:1)
at async ESModulesEvaluator.runInlinedModule
(file:///home/user/projects/blog-v3/node_modules/vite/dist/node/module-runner.js:913:3)
at async ModuleRunner.directRequest
(file:///home/user/projects/blog-v3/node_modules/vite/dist/node/module-runner.js:1146:59)
at async ModuleRunner.cachedRequest
(file:///home/user/projects/blog-v3/node_modules/vite/dist/node/module-runner.js:1053:73)
at async eval
(/home/user/projects/blog-v3/node_modules/@radix-ui/themes/src/components/accessible-icon.tsx:1:1)
at async ESModulesEvaluator.runInlinedModule
(file:///home/user/projects/blog-v3/node_modules/vite/dist/node/module-runner.js:913:3)</code> これは RSC では React.createContext が使用できないためです。
次のようなファイルを実装して、そのコンポーネントを使用する必要がありました。
<code class="language-typescript">'use client';
export {
Badge,
Box,
Button,
Card,
Flex,
Grid,
Heading,
Inset,
Text,
Theme,
} from '@radix-ui/themes'; </code> generateMetadata の images を配列にしないといけない
<code>1:25:52 PM [vite] Internal server error: imgList is not iterable
at resolveErrorDev (/home/user/projects/blog-v3/node_modules/.vite/deps_ssr/react-server-dom-
webpack_client__edge.js:4022:106)
at processFullBinaryRow (/home/user/projects/blog-v3/node_modules/.vite/deps_ssr/react-server
-dom-webpack_client__edge.js:4813:29)
at progress (/home/user/projects/blog-v3/node_modules/.vite/deps_ssr/react-server-dom-webpack
_client__edge.js:5009:20)</code> Next.js では単体値で動作していましたが、vinext では配列にする必要がありました。
wrangler.jsonc に assets.directory を指定しないといけない
vinext deploy により生成された wrangler.jsonc をそのまま使用すると
<code>The `assets` property in your configuration is missing the required `directory` property.</code> というエラーが発生しました。
assets.directory に dist/client を設定する必要がありました。
virtual:vinext-rsc-entry を解決できない
@cloudflare/vite-plugin を導入する必要がありました。
パッケージをインストールして、`vite.config.ts` の plugins に
<code class="language-typescript">cloudflare({
viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] }
})</code> を追加します。
そして、wrangler.jsonc の main に vinext/server/app-router-entry を設定します。
修正は Claude Code が一発で行ってくれましたが、大規模なプロジェクトでは大変かもしれないです。
パフォーマンス
OpenNext ではパフォーマンスが課題でしたが、vinext では大きく改善しています。
実際に操作しても、とてもスムーズに動作します。
まとめ
とても長い間 Next.js を Vercel 以外で快適に動かすことが困難でしたが、ようやく道が開けたように感じます。
とはいえ、今後の Next.js のアップデートに追従できるかどうかが重要です。
これからのプロジェクトの進行に注目していきたいと思います。