Lang x Lang

Migrating from Vite

このガイドは、既存の Vite アプリケーションを Next.js に移行するのに役立ちます。

Why Switch?

あなたが Vite から Next.js に切り替えたいと思ういくつかの理由があります:

初期ページの loading 時間が遅い

あなたがアプリケーションをdefault Vite plugin for React を使って作成した場合、あなたのアプリケーションは純粋に Client 側のアプリケーションです。Client 側のみのアプリケーションは、シングルページアプリケーション(SPA)とも呼ばれ、しばしば初期ページの loading 時間が遅くなります。これはいくつかの理由によって起こります:

  1. ブラウザは、React code とあなたのアプリケーションバンドル全体がダウンロードして実行されるまで待つ必要があります。それが完了すると、code がデータをロードするための Request を送信できるようになります。
  2. あなたのアプリケーション 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 を依存関係としてインストールすることです:

Terminal
npm install next@latest

ステップ 2:Next.js 設定ファイルを作成する

プロジェクトの root にnext.config.mjsを作成します。このファイルはあなたのNext.js 設定 optionsを保持します。

next.config.mjs
/** @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 を使用していない場合は、この手順をスキップできます。

  1. project reference tsconfig.node.jsonから削除してください
  2. ./dist/types/**/*.ts./next-env.d.tsinclude array に追加してください。
  3. ./node_modulesexclude array に追加してください
  4. { "name": "next" }compilerOptionsplugins配列 に追加してください:"plugins": [{ "name": "next" }]
  5. esModuleInterop trueに設定します:"esModuleInterop": true"
  6. jsx preserveに設定します:"jsx": "preserve"
  7. allowJs trueに設定します:"allowJs": true
  8. forceConsistentCasingInFileNames trueに設定します:"forceConsistentCasingInFileNames": true
  9. incremental trueに設定します:"incremental": true

これは、それらの変更を含む動作するtsconfig.jsonの例です:

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 ファイルに変換します。

  1. あなたの src ディレクトリに新しい app ディレクトリを作成してください。
  2. その app ディレクトリの中に新しい layout.tsx ファイルを作成します:
app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return null
}
app/layout.js
export default function RootLayout({ children }) {
  return null
}

Good to know: .js.jsx、または.tsxの拡張子は、 Layout ファイルで使用できます。

  1. あなたの index.html ファイルの内容を前に作成した <RootLayout> component にコピーし、body.div#rootbody.scriptのタグを<div id="root">{children}</div>で置き換えてください。
app/layout.tsx
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>
  )
}
app/layout.js
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>
  )
}
  1. Next.js は default でメタ文字セット メタ viewport のタグをすでに含んでいるため、それらをあなたの<head>から安全に削除することができます。
app/layout.tsx
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>
  )
}
app/layout.js
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>
  )
}
  1. favicon.icoicon.pngrobots.txtなどのmetadata ファイルは、appディレクトリのトップレベルに配置してある限り、自動的にアプリケーションの<head>タグに追加されます。すべてのサポートされるファイルappディレクトリに移動した後は、安全に<link>タグを delete することができます。
app/layout.tsx
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>
  )
}
app/layout.js
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>
  )
}
  1. 最後に、 Next.js はMetadata APIを使って最後の<head>タグを管理することができます。最終の metadata 情報をエクスポートしたmetadata objectに移動します:
app/layout.tsx
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>
  )
}
app/layout.js
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ファイルです。このステップでは、アプリケーションのエントリーポイントを設定します。

  1. appディレクトリ内に[[...slug]]ディレクトリを作成します。

このガイドではまず、 Next.js を SPA(シングルページアプリケーション)として設定することを目指しているため、アプリケーションのすべての routes を catch all するためのページエントリーポイントが必要です。そのためには、appディレクトリ内に新しい[[...slug]]ディレクトリを作成してください。

このディレクトリは、オプショナルな catch-all route セグメントと呼ばれるものです。 Next.js は、ディレクトリが routes を定義するために使用されるファイルシステムベースの router を使用します。この特別なディレクトリにより、アプリケーションのすべての routes がその包含する page.tsx ファイルに向けられるようになります。

  1. 次の内容で app/[[...slug]] ディレクトリ内に新しい page.tsx ファイルを作成します:
app/[[...slug]]/page.tsx
import '../../index.css'

export function generateStaticParams() {
  return [{ slug: [''] }]
}

export default function Page() {
  return '...' // We'll update this
}
app/[[...slug]]/page.js
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 アプリケーションの残りを移動しましょう。

app/[[...slug]]/client.tsx
'use client'

import React from 'react'
import dynamic from 'next/dynamic'

const App = dynamic(() => import('../../App'), { ssr: false })

export function ClientOnly() {
  return <App />
}
app/[[...slug]]/client.js
'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 を使用するようにします:

app/[[...slug]]/page.tsx
import '../../index.css'
import { ClientOnly } from './client'

export function generateStaticParams() {
  return [{ slug: [''] }]
}

export default function Page() {
  return <ClientOnly />
}
app/[[...slug]]/page.js
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 として返されます:

App.tsx
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>widthheightの属性を自動的に設定します。これにより、image が読み込まれたときの layout のシフトを防ぐことができます。ただし、autoに設定されていない次元がある image を含むアプリには問題を引き起こすことがあります。autoに設定されていない場合、次元は<img>の次元属性の value に default 設定され、これが image が歪んで表示される原因となる可能性があります。

<img>タグを保持することで、アプリケーションの変更量が減少し、上記の問題を防ぐことができます。その後、オプションで<Image>component に移行して、ローダーの設定による images の最適化を活用するか、自動的な images 最適化がある default の Next.jsserver に移行することができます。

  1. /publicからインポートされた画像の絶対的な import paths を相対インポートに変換します:
// Before
import logo from "/logo.png";

// After
import logo from "../public/logo.png";
  1. ** <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.MODEprocess.env.NODE_ENV
  • import.meta.env.PRODprocess.env.NODE_ENV === 'production'
  • import.meta.env.DEVprocess.env.NODE_ENV !== 'production'
  • import.meta.env.SSRtypeof window !== 'undefined'

Next.js も同様に組み込みのBASE_URL環境 variable を提供していません。しかし、必要であれば一つ設定することができます:

  1. 以下をあなたの.envファイルに追加してください:
.env
# ...
NEXT_PUBLIC_BASE_PATH="/some-base-path"
  1. next.config.mjs ファイルで basePathprocess.env.NEXT_PUBLIC_BASE_PATH に設定します:
next.config.mjs
/** @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
  1. import.meta.env.BASE_URLの使用をprocess.env.NEXT_PUBLIC_BASE_PATHに更新します

ステップ 8:package.jsonの中の Scripts を更新する

次に、 test としてアプリケーションを実行して、 Next.js に正常に移行できたかどうかをテストできるはずです。しかし、その前に、package.jsonの中のscriptsを Next.js 関連のコマンドで更新し、.nextnext-env.d.ts.gitignoreに追加する必要があります。

package.json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  }
}
.gitignore
# ...
.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
  • Deletevite-env.d.ts
  • Delete tsconfig.node.jsonを削除します。
  • Delete vite.config.ts を削除します
  • Vite の依存関係をアンインストールする

Next Steps

すべてが計画通りに進んだ場合、あなたは現在、シングルページアプリケーションとして動作する Next.js アプリケーションを手に入れているはずです。しかし、まだ Next.js の大部分の利点を活用していませんが、すぐに徐々に変更を加えてすべての利点を得ることができます。次にしたいことは次の通りです:

当社サイトでは、Cookie を使用しています。各規約をご確認の上ご利用ください:
Cookie Policy, Privacy Policy および Terms of Use