Viteからの移行
このガイドは、既存のViteアプリケーションをNext.jsに移行するのに役立ちます。
なぜ切り替えるのか?
ViteからNext.jsに切り替えたい理由はいくつかあります。
初期ページの読み込み時間の遅さ
アプリケーションをReact用のデフォルトのViteプラグインで構築した場合、アプリケーションは純粋なクライアントサイドアプリケーションです。シングルページアプリケーション(SPA)とも呼ばれるクライアントサイドのみのアプリケーションは、初期ページの読み込みが遅くなることがよくあります。これにはいくつかの理由があります。
- ブラウザがReactコードとアプリケーション全体のバンドルをダウンロードして実行するのを待つ必要があり、その後にコードがデータを読み込むためのリクエストを送信できるようになります。
- アプリケーションコードは、新しい機能や追加の依存関係を追加するたびに増大します。
自動コード分割なし
以前の読み込み時間の遅さの問題は、コード分割である程度管理できます。しかし、手動でコード分割を行おうとすると、パフォーマンスが悪化することがよくあります。手動でコード分割を行うと、意図せずにネットワークウォーターフォールを導入しやすくなります。Next.jsは、ルーターに組み込まれた自動コード分割を提供します。
ネットワークウォーターフォール
パフォーマンスが低下する一般的な原因は、アプリケーションがデータをフェッチするためにクライアントとサーバー間で連続したリクエストを行う場合に発生します。SPAでのデータフェッチの一般的なパターンは、最初にプレースホルダーをレンダリングし、コンポーネントがマウントされた後にデータをフェッチすることです。残念ながら、これは、データをフェッチする子コンポーネントが、親コンポーネントが自身のデータの読み込みを終えるまでフェッチを開始できないことを意味します。
Next.jsではクライアントでのデータフェッチもサポートされていますが、データをサーバーに移行するオプションも提供されており、これによりクライアント-サーバー間のウォーターフォールを排除できます。
高速で意図的な読み込み状態
React Suspenseによるストリーミングの組み込みサポートにより、ネットワークウォーターフォールを導入することなく、UIのどの部分を最初に、どのような順序で読み込みたいかをより意図的に制御できます。
これにより、読み込みが速いページを構築し、レイアウトシフトを排除することができます。
データフェッチ戦略の選択
ニーズに応じて、Next.jsではページやコンポーネントごとにデータフェッチ戦略を選択できます。ビルド時、サーバーでのリクエスト時、またはクライアントでデータをフェッチするかどうかを決定できます。たとえば、CMSからデータをフェッチし、ビルド時にブログ投稿をレンダリングすることで、CDNに効率的にキャッシュできます。
ミドルウェア
Next.js Middlewareを使用すると、リクエストが完了する前にサーバー上でコードを実行できます。これは、ユーザーが認証済み専用ページにアクセスした際に、認証されていないコンテンツがちらつくのを避けるために、ユーザーをログインページにリダイレクトする際に特に役立ちます。ミドルウェアは、実験や国際化にも役立ちます。
組み込みの最適化
画像、フォント、およびサードパーティスクリプトは、アプリケーションのパフォーマンスに大きな影響を与えることがよくあります。Next.jsには、これらを自動的に最適化する組み込みコンポーネントが付属しています。
移行手順
この移行の目標は、可能な限り迅速に動作するNext.jsアプリケーションを構築し、その後Next.jsの機能を段階的に導入できるようにすることです。まず、既存のルーターを移行せずに、純粋なクライアントサイドアプリケーション(SPA)として維持します。これにより、移行プロセス中の問題発生の可能性を最小限に抑え、マージ競合を減らすことができます。
ステップ1: Next.jsの依存関係をインストールする
最初に行うべきことは、next
を依存関係としてインストールすることです。
npm install next@latest
ステップ2: Next.js設定ファイルを作成する
プロジェクトのルートにnext.config.mjs
を作成します。このファイルには、Next.jsの設定オプションが格納されます。
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export', // Outputs a Single-Page Application (SPA).
distDir: './dist', // Changes the build output directory to `./dist/`.
}
export default nextConfig
知っておくと良いこと: Next.jsの設定ファイルには、
.js
または.mjs
のいずれかを使用できます。
ステップ3: TypeScript設定を更新する
TypeScriptを使用している場合は、Next.jsと互換性を持たせるために、tsconfig.json
ファイルを以下の変更で更新する必要があります。TypeScriptを使用していない場合は、このステップをスキップできます。
tsconfig.node.json
へのプロジェクト参照を削除します。./dist/types/**/*.ts
と./next-env.d.ts
をinclude
配列に追加します。./node_modules
をexclude
配列に追加します。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
の例を以下に示します。
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"esModuleInterop": true,
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"incremental": true,
"plugins": [{ "name": "next" }]
},
"include": ["./src", "./dist/types/**/*.ts", "./next-env.d.ts"],
"exclude": ["./node_modules"]
}
TypeScriptの設定に関する詳細は、Next.jsドキュメントで確認できます。
ステップ4: ルートレイアウトを作成する
Next.jsのApp Routerアプリケーションには、アプリケーション内のすべてのページをラップするルートレイアウトファイル(Reactサーバーコンポーネント)を含める必要があります。このファイルは、app
ディレクトリの最上位に定義されます。
Viteアプリケーションにおけるルートレイアウトファイルに最も近いのは、<html>
、<head>
、および<body>
タグを含むindex.html
ファイルです。
このステップでは、index.html
ファイルをルートレイアウトファイルに変換します。
src
ディレクトリ内に新しいapp
ディレクトリを作成します。- その
app
ディレクトリ内に新しいlayout.tsx
ファイルを作成します。
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return '...'
}
知っておくと良いこと: レイアウトファイルには、
.js
、.jsx
、または.tsx
の拡張子を使用できます。
- 以前作成した
<RootLayout>
コンポーネントにindex.html
ファイルの内容をコピーし、body.div#root
とbody.script
タグを<div id="root">{children}</div>
に置き換えます。
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My App</title>
<meta name="description" content="My App is a..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
<title>My App</title>
<meta name="description" content="My App is a..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
favicon.ico
、icon.png
、robots.txt
などのメタデータファイルは、app
ディレクトリの最上位に配置されていれば、アプリケーションの<head>
タグに自動的に追加されます。サポートされているすべてのファイルをapp
ディレクトリに移動した後、対応する<link>
タグを安全に削除できます。
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<title>My App</title>
<meta name="description" content="My App is a..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
- 最後に、Next.jsはMetadata APIを使用して残りの
<head>
タグを管理できます。最終的なメタデータ情報をエクスポートされたmetadata
オブジェクトに移動します。
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'My App',
description: 'My App is a...',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
上記の変更により、index.html
ですべてを宣言するのではなく、Next.jsフレームワークに組み込まれた規約ベースのアプローチ(Metadata API)を使用するようになりました。このアプローチにより、ページのSEOとWeb共有性をより簡単に向上させることができます。
ステップ5: エントリポイントページを作成する
Next.jsでは、page.tsx
ファイルを作成することでアプリケーションのエントリポイントを宣言します。Viteにおけるこのファイルに最も近いのは、main.tsx
ファイルです。このステップでは、アプリケーションのエントリポイントを設定します。
app
ディレクトリに[[...slug]]
ディレクトリを作成します。
このガイドでは、まずNext.jsをSPA(シングルページアプリケーション)としてセットアップすることを目標としているため、アプリケーションのすべての可能なルートを捕捉するためにページのエントリポイントが必要です。そのため、app
ディレクトリに新しい[[...slug]]
ディレクトリを作成します。
このディレクトリは、オプションのキャッチオールルートセグメントと呼ばれます。Next.jsは、フォルダを使用してルートを定義するファイルシステムベースのルーターを使用します。この特別なディレクトリにより、アプリケーションのすべてのルートが、そのディレクトリに含まれるpage.tsx
ファイルに転送されるようになります。
app/[[...slug]]
ディレクトリ内に以下の内容で新しいpage.tsx
ファイルを作成します。
import '../../index.css'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return '...' // We'll update this
}
知っておくと良いこと: ページファイルには、
.js
、.jsx
、または.tsx
の拡張子を使用できます。
このファイルはサーバーコンポーネントです。next build
を実行すると、ファイルは静的アセットに事前レンダリングされます。動的なコードは一切必要ありません。
このファイルはグローバルCSSをインポートし、generateStaticParams
に、単一のルート(/
のインデックスルート)のみを生成することを伝えます。
次に、クライアント側のみで実行されるViteアプリケーションの残りの部分を移動しましょう。
'use client'
import React from 'react'
import dynamic from 'next/dynamic'
const App = dynamic(() => import('../../App'), { ssr: false })
export function ClientOnly() {
return <App />
}
このファイルは、'use client'
ディレクティブによって定義されるクライアントコンポーネントです。クライアントコンポーネントは、クライアントに送信される前にサーバー上でHTMLに事前レンダリングされます。
クライアントサイドのみのアプリケーションを開始したいため、Next.jsを構成して、App
コンポーネント以下からの事前レンダリングを無効にできます。
const App = dynamic(() => import('../../App'), { ssr: false })
次に、新しいコンポーネントを使用するようにエントリポイントページを更新します。
import '../../index.css'
import { ClientOnly } from './client'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return <ClientOnly />
}
ステップ6:静的画像のインポートを更新
Next.jsは、静的画像のインポートをViteとは少し異なる方法で処理します。Viteの場合、画像ファイルをインポートすると、その公開URLが文字列として返されます。
import image from './img.png' // `image` will be '/assets/img.2d8efhg.png' in production
export default function App() {
return <img src={image} />
}
Next.jsでは、静的画像のインポートはオブジェクトを返します。このオブジェクトはNext.jsの<Image>
コンポーネントで直接使用することも、オブジェクトのsrc
プロパティを既存の<img>
タグで使用することもできます。
<Image>
コンポーネントには、自動画像最適化という追加の利点があります。<Image>
コンポーネントは、結果として生成される<img>
のwidth
属性とheight
属性を、画像の寸法に基づいて自動的に設定します。これにより、画像の読み込み時のレイアウトシフトを防ぎます。ただし、アプリに、片方の寸法のみがスタイル設定されており、もう片方がauto
にスタイル設定されていない画像が含まれている場合、問題が発生する可能性があります。auto
にスタイル設定されていない場合、その寸法は<img>
の寸法属性の値にデフォルト設定され、画像が歪んで表示される可能性があります。
<img>
タグを維持することで、アプリケーションの変更量を減らし、上記の問題を防ぐことができます。その後、必要に応じて<Image>
コンポーネントに移行して、ローダーを設定することによる画像最適化、または自動画像最適化機能を備えたデフォルトのNext.jsサーバーに移行することによるメリットを活用できます。
/public
からインポートされた画像の絶対インポートパスを相対インポートに変換します。
// Before
import logo from '/logo.png'
// After
import logo from '../public/logo.png'
- 画像オブジェクト全体ではなく、画像の
src
プロパティを<img>
タグに渡します。
// Before
<img src={logo} />
// After
<img src={logo.src} />
または、ファイル名に基づいて画像アセットのパブリックURLを参照することもできます。例えば、public/logo.png
は、アプリケーションの/logo.png
として画像を提供し、それがsrc
の値となります。
警告:TypeScriptを使用している場合、
src
プロパティにアクセスする際に型エラーが発生する可能性があります。現時点では無視しても問題ありません。これらはこのガイドの最後に修正されます。
ステップ7:環境変数を移行
Next.jsは、Viteと同様に.env
環境変数をサポートしています。主な違いは、クライアントサイドで環境変数を公開するために使用されるプレフィックスです。
VITE_
プレフィックスを持つすべての環境変数をNEXT_PUBLIC_
に変更してください。
Viteは、Next.jsではサポートされていない特殊なimport.meta.env
オブジェクトにいくつかの組み込み環境変数を公開しています。それらの使用法を次のように更新する必要があります。
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_PUBLIC_BASE_PATH="/some-base-path"
next.config.mjs
ファイルでbasePath
をprocess.env.NEXT_PUBLIC_BASE_PATH
に設定します。
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export', // Outputs a Single-Page Application (SPA).
distDir: './dist', // Changes the build output directory to `./dist/`.
basePath: process.env.NEXT_PUBLIC_BASE_PATH, // Sets the base path to `/some-base-path`.
}
export default nextConfig
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
に追加する必要があります。
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}
# ...
.next
next-env.d.ts
dist
次に、npm run dev
を実行し、https://: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のほとんどの利点を活用できていませんが、ここから段階的に変更を加えてすべての利点を享受することができます。次に実行したいことは次のとおりです。
- React RouterからNext.js App Routerに移行して以下を獲得します。
<Image>
コンポーネントによる画像の最適化next/font
によるフォントの最適化<Script>
コンポーネントによるサードパーティスクリプトの最適化- ESLint設定をNext.jsルールをサポートするように更新
この情報は役に立ちましたか?