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

從 Create React App 遷移

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

為何要轉換?

您可能想要從 Create React App 轉換到 Next.js 的原因有很多。

初始頁面載入時間過慢

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

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

沒有自動程式碼分割

先前提到的載入時間過慢的問題可以透過程式碼分割來改善。然而,如果您嘗試手動進行程式碼分割,通常會使效能變得更糟。手動進行程式碼分割時,很容易無意中造成網路瀑布流。Next.js 在其路由器中內建了自動程式碼分割功能。

網路瀑布流 快速且可控的載入狀態 React Suspense 串流 支援,您可以更有目的地控制 UI 的哪些部分要先載入以及載入順序,而不會造成網路瀑布流。

這能讓您構建載入速度更快的頁面,並消除版面配置偏移

選擇資料提取策略

根據您的需求,Next.js 允許您在頁面和組件層級選擇資料提取策略。您可以決定在構建時、伺服器上的請求時或在客戶端提取資料。例如,您可以從您的 CMS 提取資料並在構建時渲染您的部落格文章,然後可以有效地在 CDN 上快取。

中介軟體

Next.js 中介軟體允許您在請求完成之前在伺服器上運行程式碼。這對於避免在使用者訪問僅限驗證的頁面時出現未經驗證內容的閃爍特別有用,方法是將使用者重定向到登入頁面。中介軟體也適用於實驗和國際化

內建優化

圖片字體第三方腳本通常會對應用程式的效能產生顯著影響。Next.js 附帶了內建組件,可自動為您優化這些內容。

遷移步驟 步驟 1:安裝 Next.js 相依套件

您需要做的第一件事是將 next 安裝為相依套件

終端機
npm install next@latest

步驟 2:建立 Next.js 設定檔

在專案的根目錄建立一個 next.config.mjs 檔案。這個檔案將包含您的 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:建立根佈局

Next.js 應用程式路由器 應用程式必須包含一個 根佈局 檔案,它是一個 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:詮釋資料

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>
  )
}

任何 中繼資料檔案,例如 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 物件 中。

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} />

或者,您可以根據檔名參考圖片資源的公開網址。例如,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)

Bundler 相容性

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

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

此外,Next.js 也支援透過 next dev --turbo 使用 Turbopack 來提升本地開發效能。為了相容性和漸進式採用,Turbopack 也支援一些 webpack loaders

後續步驟

如果一切按計劃進行,您現在擁有一個可以作為單頁應用程式執行的 Next.js 應用程式。然而,您尚未利用 Next.js 的大部分優勢,但您現在可以開始進行漸進式更改以獲得所有優勢。以下是您接下來可能想要做的事情

注意事項:使用靜態匯出目前不支援使用 useParams hook。