跳至內容
建置您的應用程式升級從 Create React App 遷移

從 Create React App 遷移

本指南將協助您將現有的 Create React App 網站遷移至 Next.js。

為何要轉換?

您可能有幾個理由想要從 Create React App 轉換到 Next.js。

初始頁面載入時間緩慢

Create React App 使用純粹的客戶端 React。僅限客戶端的應用程式,也稱為單頁應用程式 (SPA),通常初始頁面載入時間緩慢。發生這種情況的原因有幾個:

  1. 瀏覽器需要等待 React 程式碼和您的整個應用程式套件下載並執行,然後您的程式碼才能發送請求以載入資料。
  2. 您的應用程式程式碼會隨著您新增的每個新功能和依賴項而增長。

沒有自動程式碼分割 網路瀑布 快速且刻意的加載狀態

內建支援透過 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.js 設定選項

next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'export', // Outputs a Single-Page Application (SPA).
  distDir: './build', // Changes the build output directory to `./dist`.
}
 
export default nextConfig

步驟 3:建立根佈局 應用程式路由器 應用程式必須包含一個 根佈局 檔案,它是一個 React 伺服器元件,它將包裝應用程式中的所有頁面。這個檔案定義在 app 目錄的最上層。

在 CRA 應用程式中最接近根佈局檔案的等效檔案是 index.html 檔案,其中包含您的 <html><head><body> 標籤。

在此步驟中,您會將 index.html 檔案轉換為根佈局檔案

  1. src 目錄中建立一個新的 app 目錄。
  2. 在該 app 目錄內建立一個新的 layout.tsx 檔案
app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return '...'
}

**注意事項**: 佈局檔案可以使用 .js.jsx.tsx 副檔名。

將您的 index.html 檔案的內容複製到先前建立的 <RootLayout> 元件中,同時將 body.div#rootbody.noscript 標籤替換為 <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" 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>
  )
}

注意事項:Next.js 會忽略 CRA 的 public/manifest.json 檔案、額外的圖示(除了faviconiconapple-icon)以及測試設定,但如果這些是必要條件,Next.js 也支援這些選項。詳情請參閱Metadata API測試文件。

步驟 4:Metadata

Next.js 預設已包含meta charsetmeta viewport 標籤,因此您可以安全地從 <head> 中移除它們。

app/layout.tsx
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>
  )
}

任何metadata 檔案,例如 favicon.icoicon.pngrobots.txt,只要您將它們放置在 app 目錄的頂層,就會自動添加到應用程式的 <head> 標籤中。將所有支援的檔案移動到 app 目錄後,您可以安全地刪除它們的 <link> 標籤。

app/layout.tsx
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>
  )
}

最後,Next.js 可以使用Metadata API 管理您最後的 <head> 標籤。將您最終的 metadata 資訊移動到匯出的 metadata 物件 中。

app/layout.tsx
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 內建於框架中的慣例式方法(Metadata API)。這種方法使您可以更輕鬆地改善 SEO 和網頁的分享性。

步驟 5:樣式

與 Create React App 類似,Next.js 內建支援CSS 模組

如果您使用全域 CSS 檔案,請將其匯入到您的 app/layout.tsx 檔案中。

app/layout.tsx
import '../index.css'
 
// ...

如果您使用 Tailwind,您需要安裝 postcssautoprefixer

終端機
npm install postcss autoprefixer

然後,在您的專案根目錄建立一個 postcss.config.js 檔案。

postcss.config.js
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

步驟 6:建立進入點頁面

在 Next.js 中,您可以透過建立 page.tsx 檔案來宣告應用程式的進入點。這個檔案在 CRA 中最接近的對應檔案是您的 src/index.tsx 檔案。在此步驟中,您將設定應用程式的進入點。

在您的 app 目錄中建立一個 [[...slug]] 目錄。

由於本指南旨在先將 Next.js 設定為 SPA(單頁應用程式),因此您需要頁面進入點來捕捉應用程式所有可能的路由。為此,請在您的 app 目錄中建立一個新的 [[...slug]] 目錄。

這個目錄就是所謂的可選的全域捕捉路由區段。Next.js 使用基於檔案系統的路由器,其中目錄用於定義路由。這個特殊的目錄將確保應用程式的所有路由都會導向到其包含的 page.tsx 檔案。

app/[[...slug]] 目錄內建立一個新的 page.tsx 檔案,並包含以下內容

app/[[...slug]]/page.tsx
export function generateStaticParams() {
  return [{ slug: [''] }]
}
 
export default function Page() {
  return '...' // We'll update this
}

這個檔案是一個伺服器元件。當您執行 next build 時,該檔案會被預渲染成靜態資源。它*不需要*任何動態程式碼。

這個檔案匯入了我們的全域 CSS,並告知generateStaticParams 我們只會產生一個路由,即 / 的索引路由。

現在,讓我們移動 CRA 應用程式的其餘部分,這些部分將僅在客戶端運行。

app/[[...slug]]/client.tsx
'use client'
 
import dynamic from 'next/dynamic'
 
const App = dynamic(() => import('../../App'), { ssr: false })
 
export function ClientOnly() {
  return <App />
}

這個檔案是一個客戶端元件,由 'use client' 指令定義。客戶端元件在發送到客戶端之前,仍然會在伺服器上預渲染為 HTML

由於我們想要啟動一個僅限客戶端的應用程式,我們可以將 Next.js 設定為從 App 元件開始停用預渲染。

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

現在,更新您的進入點頁面以使用新的元件

app/[[...slug]]/page.tsx
import { ClientOnly } from './client'
 
export function generateStaticParams() {
  return [{ slug: [''] }]
}
 
export default function Page() {
  return <ClientOnly />
}

步驟 7:更新靜態圖片匯入

Next.js 處理靜態圖片匯入的方式與 CRA 略有不同。使用 CRA 時,匯入圖片檔案會將其公開 URL 作為字串返回

App.tsx
import image from './img.png'
 
export default function App() {
  return <img src={image} />
}

使用 Next.js 時,靜態圖片匯入會返回一個物件。該物件可以直接與 Next.js 的<Image> 元件一起使用,或者您可以將物件的 src 屬性與現有的 <img> 標籤一起使用。

<Image> 元件額外具備自動圖片優化的優點。<Image> 元件會根據圖片尺寸自動設定產生的 <img>widthheight 屬性。這可以防止圖片載入時版面偏移。然而,如果您的應用程式包含的圖片只設定了一個維度樣式,而另一個維度未設定為 auto,則可能會導致問題。如果未設定為 auto,該維度將預設為 <img> 維度屬性的值,這可能會導致圖片顯示失真。

保留 <img> 標籤將減少應用程式中的變更量,並防止上述問題。之後您可以選擇遷移到 <Image> 元件,透過設定載入器或移至具有自動圖片優化的預設 Next.js 伺服器來利用圖片優化功能。

將從 /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 屬性時可能會遇到類型錯誤。要修復這些錯誤,您需要將 next-env.d.ts 新增到 tsconfig.json 檔案的include 陣列中。Next.js 會在您在步驟 9 執行應用程式時自動產生此檔案。

步驟 8:遷移環境變數

Next.js 支援 .env 環境變數,類似於 CRA。

主要區別在於用於在客戶端公開環境變數的前綴。將所有以 REACT_APP_ 為前綴的環境變數更改為 NEXT_PUBLIC_

步驟 9:更新 package.json 中的腳本

您現在應該可以執行您的應用程式,以測試是否已成功遷移到 Next.js。但在這之前,您需要使用 Next.js 相關指令更新 package.json 中的 scripts,並將 .nextnext-env.d.ts 新增到 .gitignore 檔案中。

package.json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "npx serve@latest ./build"
  }
}
.gitignore
# ...
.next
next-env.d.ts

現在執行 npm run dev,並開啟 https://127.0.0.1:3000。您應該會看到您的應用程式現在正在 Next.js 上運行。

步驟 10:清理

您現在可以從程式碼庫中清理與 Create React App 相關的產物

  • 刪除 public/index.html
  • 刪除 src/index.tsx
  • 刪除 src/react-app-env.d.ts
  • 刪除 reportWebVitals 設定
  • 解除安裝 CRA 相依套件 (react-scripts)

與打包工具的相容性

Create React App 和 Next.js 預設都使用 webpack 進行打包。

將 CRA 應用程式遷移到 Next.js 時,您可能想要遷移自訂的 webpack 設定。Next.js 支援提供自訂 webpack 設定

此外,Next.js 還透過 next dev --turbo 支援 Turbopack,以提升您的本地開發效能。Turbopack 也支援一些 webpack loaders,以確保相容性和漸進式採用。

後續步驟 Next.js App Router 以獲得
  • 使用 <Image> 元件最佳化圖片
  • 使用 next/font 最佳化字型
  • 使用 <Script> 元件最佳化第三方腳本
  • 更新您的 ESLint 設定以支援 Next.js 規則
  • 注意事項:使用靜態匯出目前不支援使用 useParams hook。