Vite から Next.js への移行方法
このガイドでは、既存の Vite アプリケーションを Next.js に移行する方法を説明します。
移行する理由
Vite から Next.js に切り替えるべき理由はいくつかあります:
初期ページ読み込みが遅い
React 用のデフォルト Vite プラグインでアプリケーションを構築した場合、それは純粋なクライアントサイドアプリケーションとなります。クライアントサイドのみのアプリケーション(シングルページアプリケーション、SPA)は、初期ページの読み込みが遅くなる傾向があります。これには以下の理由があります:
- ブラウザは、データ読み込みリクエストを送信できるようになる前に、React コードとアプリケーション全体のバンドルがダウンロードされ実行されるのを待つ必要がある
- 新機能や依存関係を追加するたびにアプリケーションコードが肥大化する
自動コード分割がない
読み込み時間の問題は、コード分割である程度緩和できます。しかし手動でコード分割を行うと、かえってパフォーマンスが悪化する可能性があります。手動でのコード分割では、ネットワークのウォーターフォールが発生しやすいです。Next.js にはルーターに組み込まれた自動コード分割機能があります。
ネットワークウォーターフォール
パフォーマンス低下の一般的な原因は、アプリケーションがデータ取得のためにクライアントとサーバー間で順次リクエストを行うことです。SPA でのデータ取得の一般的なパターンは、最初にプレースホルダーをレンダリングし、コンポーネントがマウントされた後にデータを取得する方法です。残念ながら、これにより、データを取得する子コンポーネントは、親コンポーネントが自身のデータの読み込みを完了するまでデータ取得を開始できません。
Next.js ではクライアント側でのデータ取得もサポートされていますが、サーバー側にデータ取得を移行するオプションも提供されており、クライアント-サーバー間のウォーターフォールを解消できます。
高速で意図的なローディング状態
React Suspense を使ったストリーミングの組み込みサポートにより、UI のどの部分をどの順序で最初に読み込むかを意図的に制御でき、ネットワークウォーターフォールを発生させずに済みます。
これにより、読み込みが速くレイアウトシフトのないページを構築できます。
データ取得戦略の選択
Next.js では、ページやコンポーネントごとにデータ取得戦略を選択できます。ビルド時、サーバーでのリクエスト時、クライアント側での取得など、ニーズに応じて選択可能です。例えば、CMS からデータを取得してブログ投稿をビルド時にレンダリングし、CDN で効率的にキャッシュできます。
ミドルウェア
Next.js ミドルウェアを使用すると、リクエストが完了する前にサーバー上でコードを実行できます。これは特に、認証が必要なページにユーザーがアクセスした際に未認証コンテンツが一瞬表示されるのを防ぎ、ログインページにリダイレクトする場合に有用です。また、実験や国際化にも役立ちます。
組み込み最適化
画像、フォント、サードパーティスクリプトはアプリケーションのパフォーマンスに大きな影響を与えます。Next.js にはこれらを自動的に最適化する組み込みコンポーネントが備わっています。
移行手順
この移行の目的は、できるだけ早く動作する Next.js アプリケーションを取得し、その後 Next.js の機能を段階的に採用できるようにすることです。最初は、既存のルーターを移行せずに純粋なクライアントサイドアプリケーション(SPA)として維持します。これにより、移行プロセス中に問題が発生する可能性を最小限に抑え、マージコンフリクトを減らせます。
ステップ 1: Next.js 依存関係のインストール
最初に next
を依存関係としてインストールします:
ステップ 2: Next.js 設定ファイルの作成
プロジェクトルートに next.config.mjs
を作成します。このファイルには Next.js の設定オプションが含まれます。
知っておくと便利: Next.js 設定ファイルには
.js
または.mjs
が使用できます。
ステップ 3: TypeScript 設定の更新
TypeScript を使用している場合、Next.js と互換性を持たせるために tsconfig.json
ファイルを以下のように更新します。TypeScript を使用していない場合はこのステップをスキップできます。
tsconfig.node.json
へのプロジェクト参照を削除include
配列に./dist/types/**/*.ts
と./next-env.d.ts
を追加exclude
配列に./node_modules
を追加compilerOptions
のplugins
配列に{ "name": "next" }
を追加:"plugins": [{ "name": "next" }]
esModuleInterop
をtrue
に設定:"esModuleInterop": true
jsx
をpreserve
に設定:"jsx": "preserve"
allowJs
をtrue
に設定:"allowJs": true
forceConsistentCasingInFileNames
をtrue
に設定:"forceConsistentCasingInFileNames": true
incremental
をtrue
に設定:"incremental": true
以下はこれらの変更を加えた tsconfig.json
の例です:
TypeScript の設定について詳しくは Next.js ドキュメントを参照してください。
ステップ 4: ルートレイアウトの作成
Next.js の App Router アプリケーションには、アプリケーション内のすべてのページをラップする React Server Component である ルートレイアウト ファイルを含める必要があります。このファイルは app
ディレクトリの最上位レベルで定義されます。
Vite アプリケーションでルートレイアウトファイルに最も近いのは、<html>
、<head>
、<body>
タグを含む index.html
ファイルです。
このステップでは、index.html
ファイルをルートレイアウトファイルに変換します:
src
フォルダ内に新しいapp
ディレクトリを作成app
ディレクトリ内に新しいlayout.tsx
ファイルを作成:
知っておくと便利: レイアウトファイルには
.js
、.jsx
、.tsx
拡張子が使用可能
index.html
ファイルの内容を先ほど作成した<RootLayout>
コンポーネントにコピーし、body.div#root
とbody.script
タグを<div id="root">{children}</div>
に置き換え:
- Next.js にはデフォルトで meta charset と meta viewport タグが含まれているため、
<head>
から安全に削除可能:
favicon.ico
、icon.png
、robots.txt
などの メタデータファイル は、app
ディレクトリの最上位に配置されている限り、自動的にアプリケーションの<head>
タグに追加されます。サポートされているファイル をすべてapp
ディレクトリに移動した後、<link>
タグを安全に削除できます:
- 最後に、Next.js は Metadata API を使用して最後の
<head>
タグを管理できます。最終的なメタデータ情報をエクスポートされたmetadata
オブジェクト に移動します:
上記の変更により、index.html
ですべてを宣言する方法から、フレームワークに組み込まれた規約ベースのアプローチ (Metadata API) を使用する方法に移行しました。このアプローチにより、ページの SEO とウェブ共有性をより簡単に向上させられます。
ステップ5: エントリポイントページの作成
Next.jsでは、page.tsx
ファイルを作成することでアプリケーションのエントリポイントを宣言します。Viteにおけるmain.tsx
ファイルに相当するものです。このステップでは、アプリケーションのエントリポイントを設定します。
app
ディレクトリ内に[[...slug]]
ディレクトリを作成
このガイドではまずNext.jsをSPA(シングルページアプリケーション)として設定することを目的としているため、アプリケーションのすべてのルートをキャッチするページエントリポイントが必要です。そのために、app
ディレクトリ内に新しい[[...slug]]
ディレクトリを作成してください。
このディレクトリはオプショナルキャッチオールルートセグメントと呼ばれるものです。Next.jsはファイルシステムベースのルーターを使用しており、フォルダを使ってルートを定義します。この特別なディレクトリにより、アプリケーションのすべてのルートが含まれるpage.tsx
ファイルに誘導されます。
app/[[...slug]]
ディレクトリ内に新しいpage.tsx
ファイルを作成し、以下の内容を記述:
豆知識: ページファイルには
.js
、.jsx
、.tsx
拡張子が使用できます。
このファイルはサーバーコンポーネント (Server Component)です。next build
を実行すると、このファイルは静的アセットにプリレンダリングされます。動的コードは必要ありません。
このファイルはグローバルCSSをインポートし、generateStaticParams
に対して/
のインデックスルートのみを生成するように指示しています。
次に、クライアント側のみで実行されるViteアプリケーションの残りを移動させましょう。
このファイルは'use client'
ディレクティブで定義されたクライアントコンポーネント (Client Component)です。クライアントコンポーネントは、クライアントに送信される前にサーバー側でHTMLにプリレンダリングされます。
最初はクライアントのみのアプリケーションにしたいので、App
コンポーネント以下でプリレンダリングを無効にするようにNext.jsを設定できます。
次に、エントリポイントページを更新して新しいコンポーネントを使用します:
ステップ6: 静的画像インポートの更新
Next.jsは静的画像のインポートをViteとは少し異なる方法で処理します。Viteでは画像ファイルをインポートすると、その公開URLが文字列として返されます:
Next.jsでは、静的画像をインポートするとオブジェクトが返されます。このオブジェクトはNext.jsの<Image>
コンポーネントと直接使用するか、既存の<img>
タグでオブジェクトのsrc
プロパティを使用できます。
<Image>
コンポーネントには自動画像最適化という追加の利点があります。<Image>
コンポーネントは、画像の寸法に基づいて結果の<img>
のwidth
とheight
属性を自動的に設定します。これにより、画像が読み込まれる際のレイアウトシフトを防ぎます。ただし、片方の寸法のみがスタイリングされ、もう一方がauto
にスタイリングされていない画像がアプリに含まれている場合、問題が発生する可能性があります。auto
にスタイリングされていない場合、寸法は<img>
の寸法属性の値にデフォルト設定され、画像が歪んで表示される可能性があります。
<img>
タグを維持すると、アプリケーションの変更量を減らし、上記の問題を防ぐことができます。その後、必要に応じてローダーを設定して<Image>
コンポーネントに移行するか、自動画像最適化が組み込まれたデフォルトのNext.jsサーバーに移行することで、画像を最適化できます。
/public
からインポートされた画像の絶対パスを相対パスに変換:
<img>
タグに画像オブジェクト全体ではなくsrc
プロパティを渡す:
または、ファイル名に基づいて画像アセットの公開URLを参照することもできます。例えば、public/logo.png
はアプリケーションで/logo.png
として画像を提供し、これがsrc
の値になります。
警告: TypeScriptを使用している場合、
src
プロパティにアクセスすると型エラーが発生する可能性があります。今のところこれらは無視して構いません。このガイドの最後までに修正されます。
ステップ7: 環境変数の移行
Next.jsはViteと同様に.env
環境変数をサポートしています。主な違いは、クライアント側で環境変数を公開するために使用されるプレフィックスです。
VITE_
プレフィックスのあるすべての環境変数をNEXT_PUBLIC_
に変更してください。
Viteは特別なimport.meta.env
オブジェクト上でいくつかの組み込み環境変数を公開していますが、これらはNext.jsではサポートされていません。次のように使用法を更新する必要があります:
import.meta.env.MODE
⇒process.env.NODE_ENV
import.meta.env.PROD
⇒process.env.NODE_ENV === 'production'
import.meta.env.DEV
⇒process.env.NODE_ENV !== 'production'
import.meta.env.SSR
⇒typeof window !== 'undefined'
Next.jsは組み込みのBASE_URL
環境変数も提供していません。ただし、必要に応じて設定できます:
.env
ファイルに以下を追加:
next.config.mjs
ファイルでbasePath
をprocess.env.NEXT_PUBLIC_BASE_PATH
に設定:
import.meta.env.BASE_URL
の使用箇所をprocess.env.NEXT_PUBLIC_BASE_PATH
に更新
ステップ8: package.json
のスクリプトを更新
Next.jsへの移行が成功したかどうかをテストするために、アプリケーションを実行できるようになるはずです。しかしその前に、package.json
のscripts
をNext.js関連のコマンドで更新し、.next
とnext-env.d.ts
を.gitignore
に追加する必要があります:
npm run dev
を実行し、http://localhost:3000
を開いてください。Next.jsで動作するアプリケーションが表示されるはずです。
例: ViteアプリケーションをNext.jsに移行した動作例はこのプルリクエストで確認できます。
ステップ9: クリーンアップ
Vite関連のファイルをコードベースから削除できます:
main.tsx
を削除index.html
を削除vite-env.d.ts
を削除tsconfig.node.json
を削除vite.config.ts
を削除- Viteの依存関係をアンインストール
次のステップ
すべてが計画通りに進んだ場合、シングルページアプリケーションとして動作するNext.jsアプリケーションができあがっています。しかし、まだNext.jsの利点のほとんどを活用していませんが、これから段階的な変更を加えてすべての利点を得ることができます。次に行いたいこと: