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
をinclude
array に追加してください。./node_modules
をexclude
array に追加してください{ "name": "next" }
をcompilerOptions
のplugins
配列 に追加してください:"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: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 情報をエクスポートしたmetadata
objectに移動します:
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 を提供することになり、これはsrc
value となるでしょう。
警告: 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_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
環境 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 の設定を更新してください