Create React App からの移行
このガイドは、既存のCreate React AppサイトをNext.jsに移行する手助けをします。
なぜ切り替えるのか?
Create React AppからNext.jsに切り替えたい理由はいくつかあります:
初期ページの読み込み時間が遅い
Create React Appは純粋にクライアントサイドのReactを使用しています。クライアントサイドのみのアプリケーション、別名シングルページアプリケーション(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
を依存関係としてインストールすることです:
npm install next@latest
ステップ2: Next.jsの設定ファイルを作成する
next.config.mjs
をプロジェクトのルートに作成します。このファイルにはNext.jsの設定オプションが含まれます。
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export', // シングルページアプリケーション (SPA) を出力します。
distDir: './build', // ビルド出力ディレクトリを `./dist` に変更します。
}
export default nextConfig
ステップ3: Root レイアウトを作成する
Next.jsのApp Routerアプリケーションには、アプリケーション内のすべてのページをラップするReact Server ComponentであるRoot レイアウトファイルが含まれている必要があります。このファイルはapp
ディレクトリの最上位に定義されます。
CRAアプリケーションにおけるRoot レイアウトファイルの最も近い同等物は、<html>
、<head>
、<body>
タグを含むindex.html
ファイルです。
このステップでは、index.html
ファイルをRoot レイアウトファイルに変換します:
src
ディレクトリに新しいapp
ディレクトリを作成します- その
app
ディレクトリ内に新しいlayout.tsx
ファイルを作成します:
- TypeScript
- JavaScript
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return '...'
}
export default function RootLayout({ children }) {
return '...'
}
Good to know: Layoutファイルには、
.js
、.jsx
、または.tsx
の拡張子を使用できます。
以前 に作成した<RootLayout>
コンポーネントにindex.html
ファイルの内容をコピーし、body.div#root
とbody.noscript
のタグを<div id="root">{children}</div>
に置き換えます:
- TypeScript
- JavaScript
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>
)
}
export default function RootLayout({ children }) {
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: Next.jsはCRAの
public/manifest.json
ファイル、追加のアイコン(favicon
、icon
、apple-icon
を除く)、およびテスト設定を無視しますが、これらが必要な場合でもNext.jsはこれらのオプションをサポートしています。詳しくはMetadata APIおよびTestingのドキュメントをご覧ください。
ステップ4: メタデータ
Next.jsは既にデフォルトでmeta charsetおよびmeta viewportタグを含んでいるため、これらを<head>
から安全に削除できます:
- TypeScript
- JavaScript
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>
)
}
export default function RootLayout({ children }) {
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>
タグを安全に削除できます:
- TypeScript
- JavaScript
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>
)
}
export default function RootLayout({ children }) {
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>
)
}
最後に、Next.jsはMetadata APIで最後の<head>
タグを管理することができます。最終的なメタデータ情報をエクスポートされたmetadata
オブジェクトに移動します:
- TypeScript
- JavaScript
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>
)
}
export const metadata = {
title: 'React App',
description: 'Web site created with Next.js.',
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
上記の変更により、すべてをindex.html
で宣言するところから、フレームワークに組み込まれたNext.jsの規約ベースのアプローチへの移行を行いました(Metadata API). このアプローチにより、ページのSEOおよびウェブ共有性をより簡単に向上させることができます。