Create React App からの移行
このガイドでは、既存の Create React App サイトを Next.js に移行する方法について説明します。
なぜ移行するのか?
Create React App から Next.js に切り替える理由はいくつか あります:
ページの初期読み込み時間が遅い
Create React App は純粋にクライアントサイドの React を使用します。クライアントサイドのみのアプリケーション、別名シングルページアプリケーション(SPA)は、しばしば初期ページの読み込み時間が遅くなります。これにはいくつかの理由があります:
- ブラウザは、React のコードとアプリケーション全体のバンドルをダウンロードして実行し、コードがデータのリクエストを送るようになるまで待たなければなりません。
- 新しい機能や依存関係を追加するたびに、アプリケーションのコードが増えます。
自動的なコード分割がない
遅い読み込み時間の問題は、コードの分割を使ってある程度管理できます。しかし、手動でコード分割を試みると、パフォーマンスをさらに悪化させることがよくあります。手動でコードを分割すると、ネットワークのウォーターフォールを無意識のうちに導入するしてしまいます。Next.js は、Router により自動コード分割を提供します。
ネットワークウォ ーターフォール
アプリケーションがクライアントとサーバー間で連続的にリクエストを行ってデータを取得するとき、パフォーマンス低下をもたらす原因があります。SPA でのデータ取得の一般的なパターンは、最初にプレースホルダをレンダリングし、その後にコンポーネントがマウントされた後にデータを取得することです。残念ながら、これはデータを取得する子コンポーネントが、親コンポーネントが自身のデータのロードを終えるまで取得を開始できないことを意味します。
Next.js ではクライアントでのデータ取得もサポートされていますが、データ取得をサーバーにシフトするオプションも提供し、クライアントとサーバーのウォーターフォールを排除できます。
迅速かつ意図的なローディング状態
React Suspense を通じたストリーミングのビルトインサポートにより、UI のどの部分を最初に、そしてどの順序でロードするかをより意図的に決定できます。これにより、ネットワークウォーターフォールを導入せずに、読み込みが高速なページを構築し、レイアウトシフトを排除できます。
データフェッチ・ストラテジー
ニーズに応じて、Next.js ではページやコンポーネントごとにデータフェッチ・ストラテジーを選ぶことができます。ビルド時、サーバー上のリクエスト時、またはクライアント上でデータを取得することを選択できます。たとえば、CMS からデータを取得してブログ投稿をビルド時にレンダリングし、それを CDN 上で効率的にキャッシュできます。
Middleware
Next.js の Middlewareを使用すると、リクエストが完了する前にサーバー上でコードを実行できます。これは、ユーザーの認証が必要なページにアクセスしたとき、認証されていないコンテンツのフラッシュを避けるためにログインページへリダイレクトする場合などに特に便利です。Middleware は、実験的機能の実装や国際化にも役立ちます。
ビルドインの最適化
画像、フォント、およびサードパーティのスクリプトは、アプリケーションのパフォーマンスに大きな影響を及ぼすことがよくあります。Next.js には、それらを自動的に最適化するためのビルトインのコンポーネントがあります。
移行手順
移行の目標は、できるだけ早く、Next.js アプリケーションとして動作させ、その後、Next.js の機能を段階的に採用することです。 まず、既存の Router を移行せずに純粋なクライアントサイドアプリケーション(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', // シングルページアプリケーション(SPA)を出力します。
distDir: './dist', // ビルド出力ディレクトリを `./dist/` に変更します。
}
export default nextConfig
Good to know: Next.js の設定ファイルには、
.js
もしくは.mjs
を使用できます。
ステップ 3: TypeScript 設定の更新
TypeScript を使用している場合、tsconfig.json
ファイルを次のように更新して、Next.js との互換性を確保します:
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"baseUrl": ".",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"strictNullChecks": true
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"./dist/types/**/*.ts"
],
"exclude": ["node_modules"]
}
TypeScript の設定についての詳細は、Next.js のドキュメントで見つけることができます。
ステップ 4: ルートレイアウトの作成
Next.js のApp Router アプリケーションには、アプリケーション内のすべてのページをラップするルートレイアウトファイルが含まれていなければなりません。このファイルは、app
ディレクトリのトップレベルで定義されます。
CRA アプリケーションでルートレイアウトファイルにもっとも近いものは、<html>
、<head>
、および <body>
タグを含む index.html
ファイルです。
このステップでは、index.html
ファイルをルートレイアウトファイルに変換します:
src
ディレクトリ内に新しいapp
ディレクトリを作成します- その
app
ディレクトリ内に新しいlayout.tsx
ファイルを作成します:
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return null
}
Good to know: レイアウトファイルには
.js
,.jsx
,.tsx
の拡張子が使用できます。
index.html
ファイルの内容を先ほど作成した<RootLayout>
コンポーネントにコピーし、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" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React App</title>
<meta name="description" content="Web site created..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
Good to know: マニフェストファイル、favicon以外のアイコン、および テストの設定を今回は無視しますが、必要な場合、Next.js はこれらのオプションもサポートしています。
- Next.js はデフォルトで meta charset と meta viewport タグを含んでいるため、これらを
<head>
から安全に削除できます:
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<title>React App</title>
<meta name="description" content="Web site created..." />
</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>React App</title>
<meta name="description" content="Web site created..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
- 最後に、メタデータ API を使用して、最終的な
<head>
タグを Next.js で管理できます。最終的なメタデータ情報をエクスポートするmetadata
オブジェクト に移動します:
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'React App',
description: 'Web site created with Next.js.',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
上記の変更により、index.html
内ですべてを宣言することから、フレームワークに内蔵された Next.js の規約ベースのアプローチ(
メタデータ API)に移行されました。このアプローチにより、ページの SEO と Web 共有性をより簡単に向上させることができます。
ステップ 5: エントリーポイントページの作成
Next.js では、アプリケーションのエントリーポイントを page.tsx
ファイルで宣言します。CRA でのもっとも近いファイルは src/index.tsx
ファイルです。このステップでは、アプリケーションのエントリーポイントを設定します。
app
ディレクトリ内に[[...slug]]
ディレクトリを作成します。
このガイドでは、まず Next.js を SPA(Single Page Application)として設定することを目指しています。そのため、アプリケーションの可能なすべてのルートをキャッチするために、app
ディレクトリ内に新しい [[...slug]]
ディレクトリを作成する必要があります。
このディレクトリは オプションのキャッチオール Segment と呼ばれます。Next.js はファイルシステムベースの Router を使用しており、ディレクトリはルートを定義 します。この特殊なディレクトリは、アプリケーションのすべてのルートを含む page.tsx
ファイルに直接することを保証します。
app/[[...slug]]
ディレクトリ内に以下の内容で新しいpage.tsx
ファイルを作成します:
import '../../src/index.css'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return '...' // このコンテンツは更新します
}
Good to know: ページファイルには
.js
,.jsx
,.tsx
の拡張子が使用できます。
このファイルはServer Componentsです。next build
を実行すると、ファイルは静的アセットとして事前にレンダリングされます。動的なコードを必要としません。
このファイルは、グローバルCSSをインポートし、generateStaticParams
に対して、/
でのインデックスルートのみを生成することを伝えます。
それでは、クライアントのみで実行される CRA アプリケーションの残りの部分を移動しましょう。
'use client'
import React from 'react'
import dynamic from 'next/dynamic'
const App = dynamic(() => import('../../src/App'), { ssr: false })
export function ClientOnly() {
return <App />
}
このファイルはClient Componentsで、'use client'
ディレクティブによって定義されています。Client Component は、クライアントへ送信される前にサーバー上でHTMLに事前レンダリングされます。
クライアントのみのアプリケーションを開始したいのであれば、App
コンポーネントから事前レンダリングを無効にするように Next.js を設定できます。
const App = dynamic(() => import('../../App'), { ssr: false })
新しいコンポーネントを使用するようにエントリポイントページを更新してください。
import '../../src/index.css'
import { ClientOnly } from './client'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return <ClientOnly />
}
ステップ 6: 静的画像インポートの更新
Next.js は CRA とは少し異なり、静的画像インポートを扱います。CRAでは、画像ファイルをインポートすると、その公開 URL が文字列として返されます。
import image from './img.png'
export default function App() {
return <img src={image} />
}
Next.js での静的画像インポートはオブジェクトを返し、Next.js の<Image>
コンポーネントで直接使用するか、またはオブジェクトのsrc
プロパティを既存の<img>
タグで使用できます。
<Image>
コンポーネントには自動で画像最適化される利点があります。<Image>
コンポーネントは、画像の寸法に基づいて生成された <img>
の width
と height
属性を自動的に設定します。これにより、画像が読み込まれる際のレイアウトシフトが防止されます。しかし、これは、その他の寸法が auto
にスタイルされていない画像がアプリケーションに含まれている場合、問題を引き起こす可能性があります。auto
にスタイルされていない場合、寸法は <img>
の寸法属性の値がデフォルトになり、画像が歪んで見える可能性があります。
<img>
タグを保持することで、アプリケーションの変更を最小限に抑え、上記の問題を防ぐことができます。その後、ローダーを設定する、または自動的な画像最適化を持つデフォルトの Next.js サーバーに移行することにより、画像を最適化するために <Image>
コンポーネントへの移行を選択できます。
/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はCRAと同様に、.env
環境変数をサポートしています。
主な違いは、クライアント側で環境変数を公開するために使用されるプレフィックスです。REACT_APP_
プレフィックスを持つすべての環境変数をNEXT_PUBLIC_
に変更します。