Migrating from Vite
このガイドは、既存の Vite アプリケーションを Next.js に移行するのに役立ちます。
Why Switch?
あなたが Vite から Next.js に切り替えたいと思ういくつかの理由があります:
初期ページの loading 時間が遅い
あなたがアプリケーションをdefault Vite plugin for React を使って作成した場合、あなたのアプリケーションは純粋に Client 側のアプリケーションです。Client 側のみのアプリケーションは、シングルページアプリケーション(SPA)とも呼ばれ、しばしば初期ページの loading 時間が遅くなります。これはいくつかの理由によって起こります:
- ブラウザは、React code とあなたのアプリケーションバンドル全体がダウンロードして実行されるまで待つ必要があります。それが完了すると、code がデータをロードするための Request を送信できるようになります。
- あなたのアプリケーション code は、新しい機能や追加の依存関係を加えることで、増大します。
いいえ、automatic code splitting
前回の問題である loading の遅さは code の分割を行うことである程度管理することができます。しかし、 code の分割を手動で行おうとすると、パフォーマンスが悪化することがよくあります。手動でコードを分割すると、ネットワークのウォーターフォールを無意識のうちに導入することが容易です。 Next.js は、 router に組み込まれた automatic code splitting を提供しています。
ネットワークのウォーターフォール
アプリケーションが fetch のデータを順番に Clientserver に Request するとき、よくパフォーマンスが悪くなる原因が発生します。SPA での一般的なデータフェッチのパターンは、placeholder を最初に render し、その後、component がマウントされた後に fetch データを行います。残念ながら、これはデータをフェッチする子の component が、親の component が自分のデータを loading し終えるまで start できないことを意味します。
client 上でデータを取得することは Next.js でサポートされていますが、データの取得を server にシフトする選択肢も提供しています。これにより、クライアント-サーバーのウォーターフォールを排除することができます。
速く、意図的な loading 状態
React Suspense を通じた Streamingの組み込みサポートにより、ネットワークウォーターフォールを導入せずに、最初にロードしたい UI の一部とその順序をより意図的に管理できます。
これにより、ロードが速く、layout shifts を排除する build ページを作成することができます。
データフェッチングの strategy を選択
あなたのニーズに応じて、 Next.js はページや component ごとにデータ取得の strategy を選択することを許可します。あなたは、 build 時、 server 上での request 時、または client 上でデータを fetch することを決定できます。例えば、CMS からデータを fetch し、 build 時にブログの投稿を render することができ、これらは CDN 上で効率的にキャッシュすることができます。
Middleware
Next.js Middlewareは、request が完了する前に server 上で code を実行できるようにします。これは、ユーザーが認証専用のページにアクセスしたときに未認証の内容が一瞬表示されることを避けるために特に便利です。ユーザーをログインページに Redirect します。また、middleware は、実験や国際化にも有用です。
ビルトイン最適化
画像、フォント、そして第三者の scriptsは、アプリケーションのパフォーマンスに大きな影響を及ぼすことがよくあります。Next.js は、それらを自動的に最適化する組み込み Component が付属しています。
Migration Steps
このマイグレーションの目標は、できるだけ早く動作する Next.js アプリケーションを手に入れて、次第に Next.js の機能を導入できるようにすることです。まず、既存の router を移行せずに、純粋なクライアントサイドアプリケーション(SPA)として維持します。これにより、マイグレーションプロセス中に問題に遭遇する可能性を最小限に抑え、マージコンフリクトを減らすことができます。
ステップ 1:Next.js の依存関係をインストールする
まず最初に行うべきことは、next を依存関係としてインストールすることです:
npm install next@latest
ステップ 2:Next.js 設定ファイルを作成する
プロジェクトの root にnext.config.mjsを作成します。このファイルはあなたのNext.js 設定 optionsを保持します。
/** @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
Good to know: Next.js の設定ファイルには、
.jsまたは.mjsのいずれかを使用できます。
ステップ 3:TypeScript の設定を更新する
あなたが TypeScript を使用している場合、それを Next.js と互換性を持たせるためには、以下の変更で tsconfig.json ファイルを更新する必要があります。もし TypeScript を使用していない場合は、この手順をスキップできます。
- project reference を
tsconfig.node.jsonから削除してください ./dist/types/**/*.tsと./next-env.d.tsをincludearray に追加してください。./node_modulesをexcludearray に追加してください{ "name": "next" }をcompilerOptionsのplugins配列 に追加してください:"plugins": [{ "name": "next" }]esModuleInteropをtrueに設定します:"esModuleInterop": true"jsxをpreserveに設定します:"jsx": "preserve"allowJsをtrueに設定します:"allowJs": trueforceConsistentCasingInFileNamesをtrueに設定します:"forceConsistentCasingInFileNames": trueincrementalを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:Root Layout を作成する
Next.js App Routerアプリケーションには、アプリケーション内のすべてのページをラップするReact Server Componentであるroot layoutファイルを含める必要があります。このファイルは app ディレクトリの最上位で定義されます。
Vite アプリケーションにおける root layout ファイルの最も近い同等物は、<html>、<head>、そして <body> タグを含む index.html ファイル です。
このステップでは、index.htmlファイルを root layout ファイルに変換します。
- あなたの
srcディレクトリに新しいappディレクトリを作成してください。 - その
appディレクトリの中に新しいlayout.tsxファイルを作成します:
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return null
}
export default function RootLayout({ children }) {
return null
}
Good to know:
.js、.jsx、または.tsxの拡張子は、 Layout ファイルで使用できます。
- あなたの
index.htmlファイルの内容を前に作成した<RootLayout>component にコピーし、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 }) {
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>
)
}
- Next.js は default でメタ文字セット とメタ viewport のタグをすでに含んでいるため、それらをあなたの
<head>から安全に削除することができます。
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>
)
}
export default function RootLayout({ children }) {
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などのmetadata ファイルは、appディレクトリのトップレベルに配置してある限り、自動的にアプリケーションの<head>タグに追加されます。すべてのサポートされるファイルをappディレクトリに移動した後は、安全に<link>タグを delete することができます。
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>
)
}
export default function RootLayout({ children }) {
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 情報をエクスポートしたmetadataobjectに移動します:
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>
)
}
export const metadata = {
title: 'My App',
description: 'My App is a...',
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
上記の変更により、すべてを index.html で宣言することから、 Next.js のフレームワークに組み込まれた慣習ベースのアプローチ(Metadata API)を使用するようになりました。このアプローチにより、ページの SEO とウェブ共有性をより簡単に向上させることができます。
ステップ 5:エントリーポイントページを作成する
Next.js では、page.tsxファイルを作成することにより、アプリケーションのエントリーポイントを宣言します。このファイルの最も近い同等のものは、Vite 上のmain.tsxファイルです。このステップでは、アプリケーションのエントリーポイントを設定します。
appディレクトリ内に[[...slug]]ディレクトリを作成します。
このガイドではまず、 Next.js を SPA(シングルページアプリケーション)として設定することを目指しているため、アプリケーションのすべての routes を catch all するためのページエントリーポイントが必要です。そのためには、appディレクトリ内に新しい[[...slug]]ディレクトリを作成してください。
このディレクトリは、オプショナルな catch-all route セグメントと呼ばれるものです。 Next.js は、ディレクトリが routes を定義するために使用されるファイルシステムベースの router を使用します。この特別なディレクトリにより、アプリケーションのすべての routes がその包含する page.tsx ファイルに向けられるようになります。
- 次の内容で
app/[[...slug]]ディレクトリ内に新しいpage.tsxファイルを作成します:
import '../../index.css'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return '...' // We'll update this
}
import '../../index.css'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return '...' // We'll update this
}
Good to know: ページファイルには、
.js、.jsx、または.tsxの拡張子を使用することができます。
このファイルはServer Componentです。next buildを実行すると、このファイルは静的なアセットに事前レンダリングされます。それは全く require などの dynamic code を必要としません。
このファイルは、私たちのグローバルな CSS をインポートし、generateStaticParamsに対して、一つだけの route 、すなわち/にある index route を生成する予定であることを伝えます。
さあ、Client のみで動作する私たちの 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'
import React from 'react'
import dynamic from 'next/dynamic'
const App = dynamic(() => import('../../App'), { ssr: false })
export function ClientOnly() {
return <App />
}
このファイルは、'use client'ディレクティブによって定義されたClient Componentです。 Client Components は依然として、 server 上でHTML に事前レンダリングされ、その後 client に送信されます。
Client のみのアプリケーションを start することを望んでいるため、私たちは Next.js を設定して、App component 以下でのプレレンダリングを無効にすることができます。
const App = dynamic(() => import("../../App"), { ssr: false });
さあ、エントリーポイントページを更新して新しい component を使用するようにします:
import '../../index.css'
import { ClientOnly } from './client'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return <ClientOnly />
}
import '../../index.css'
import { ClientOnly } from './client'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return <ClientOnly />
}
ステップ 6:スタティックな Image インポートを更新
Next.js は、静的な image のインポートを Vite とは少し異なる方法で処理します。Vite では、 image ファイルをインポートすると、その public URL が string として返されます:
import image from './img.png' // `image` will be '/assets/img.2d8efhg.png' in production
export default function App() {
return <img src={image} />
}
Next.js を使用すると、静的な image のインポートは object を返します。その object は直接 Next.js の<Image> componentとして使用できるか、既存の<img>タグで object のsrcプロパティを使用できます。
<Image>component には、自動 image 最適化という追加の利点があります。<Image>component は、image の次元に基づいて結果の<img>のwidthとheightの属性を自動的に設定します。これにより、image が読み込まれたときの layout のシフトを防ぐことができます。ただし、autoに設定されていない次元がある image を含むアプリには問題を引き起こすことがあります。autoに設定されていない場合、次元は<img>の次元属性の value に default 設定され、これが image が歪んで表示される原因となる可能性があります。
<img>タグを保持することで、アプリケーションの変更量が減少し、上記の問題を防ぐことができます。その後、オプションで<Image>component に移行して、ローダーの設定による images の最適化を活用するか、自動的な images 最適化がある default の Next.jsserver に移行することができます。
/publicからインポートされた画像の絶対的な import paths を相対インポートに変換します:
// Before
import logo from "/logo.png";
// After
import logo from "../public/logo.png";
- **
<img>タグに対して全体の image object の代わりに image のsrcプロパティを渡します:**
// Before
<img src={logo} />
// After
<img src={logo.src} />
あるいは、ファイル名に基づいて image アセットのための public URL を参照することもできます。例えば、public/logo.pngはあなたのアプリケーションの /logo.png で image を提供することになり、これはsrcvalue となるでしょう。
警告: TypeScript を使用している場合、
srcにアクセスするときに type エラーが発生する可能性があります。 プロパティ。現時点ではそれらを安全に無視して構いません。それらはこのガイドの終わりまでに fixed されるでしょう。
ステップ 7:Environment Variables を移行します
Next.js は、Vite と同様に、.env environment variablesをサポートしています。主な違いは、クライアント側で environment variables を公開するために使用される接頭辞です。
- すべての environment variables を
VITE_プレフィックスからNEXT_PUBLIC_へ変更してください。
Vite は、特別なimport.meta.env object 上でいくつかの組み込みの environment variables を公開しています。これらは Next.js ではサポートされていません。その使用法を次のように更新する必要があります:
import.meta.env.MODE⇒process.env.NODE_ENVimport.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環境 variable を提供していません。しかし、必要であれば一つ設定することができます:
- 以下をあなたの
.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の中の Scripts を更新する
次に、 test としてアプリケーションを実行して、 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 を実行し、EXL0015 を開きます。これで、アプリケーションが Next.js 上で動作しているはずです。
例: このプルrequest をチェックしてみてください Vite アプリケーションが Next.js に移行した動作例。
ステップ 9:クリーンアップ
あなたは今、Vite 関連のアーティファクトから Code ベースをクリーニングすることができます:
- Delete
main.tsx - Delete
index.html - Delete
vite-env.d.ts - Delete
tsconfig.node.jsonを削除します。 - Delete
vite.config.tsを削除します - Vite の依存関係をアンインストールする
Next Steps
すべてが計画通りに進んだ場合、あなたは現在、シングルページアプリケーションとして動作する Next.js アプリケーションを手に入れているはずです。しかし、まだ Next.js の大部分の利点を活用していませんが、すぐに徐々に変更を加えてすべての利点を得ることができます。次にしたいことは次の通りです:
- React Router からNext.js App Routerへ移行して以下を得る:
- Automatic code splitting
<Image>component で画像を最適化するnext/fontでフォントを最適化する<Script>component を使用してサードパーティの scripts を最適化する- Next.js のルールをサポートするために、 ESLint の設定を更新してください