從 Create React App 遷移
本指南將協助您將現有的 Create React App (CRA) 網站遷移到 Next.js。
為什麼要切換?
有幾個原因可能讓您想要從 Create React App 切換到 Next.js
初始頁面載入時間緩慢
Create React App 完全使用客戶端 React。純客戶端的應用程式,也稱為單頁應用程式 (SPA),通常會遇到初始頁面載入時間緩慢的問題。這發生有幾個原因:
- 瀏覽器需要等待 React 程式碼和您的整個應用程式 bundle 下載並執行,之後您的程式碼才能發送請求以載入資料。
- 您的應用程式程式碼會隨著您新增的每個新功能和依賴項而增長。
沒有自動程式碼分割
先前載入時間緩慢的問題可以透過程式碼分割在某種程度上緩解。但是,如果您嘗試手動進行程式碼分割,可能會不小心引入網路瀑布流。Next.js 在其路由和建置管線中內建了自動程式碼分割和 tree-shaking。
網路瀑布流
效能不佳的常見原因發生在應用程式進行循序客戶端-伺服器請求以擷取資料時。在SPA 中擷取資料的一種模式是渲染一個佔位符,然後在元件掛載後擷取資料。不幸的是,子元件只能在其父元件完成載入自身資料後才開始擷取資料,從而導致請求的「瀑布流」。
雖然 Next.js 支援客戶端資料擷取,但 Next.js 也允許您將資料擷取移至伺服器。這通常完全消除了客戶端-伺服器瀑布流。
快速且有意的載入狀態
透過內建支援透過 React Suspense 進行串流,您可以定義 UI 的哪些部分先載入以及以什麼順序載入,而不會建立網路瀑布流。
這使您能夠建置載入速度更快的頁面,並消除版面配置偏移。
選擇資料擷取策略
根據您的需求,Next.js 允許您在頁面或元件層級選擇您的資料擷取策略。例如,您可以從 CMS 擷取資料,並在建置時 (SSG) 渲染部落格文章以獲得快速的載入速度,或在必要時在請求時 (SSR) 擷取資料。
中介層
Next.js 中介層允許您在請求完成之前在伺服器上執行程式碼。例如,您可以透過將使用者重新導向到中介層中僅限身份驗證頁面的登入頁面,來避免未經身份驗證內容的閃爍。您也可以將其用於 A/B 測試、實驗和國際化等功能。
內建優化
圖片、字型和第三方腳本通常對應用程式的效能有很大的影響。Next.js 包含專門的元件和 API,可自動為您優化它們。
遷移步驟
我們的目標是盡快獲得可運作的 Next.js 應用程式,以便您可以逐步採用 Next.js 功能。首先,我們將您的應用程式視為純客戶端應用程式 (SPA),而不會立即替換您現有的路由器。這降低了複雜性和合併衝突。
注意:如果您正在使用進階 CRA 設定,例如
package.json
中的自訂homepage
欄位、自訂 service worker 或特定的 Babel/webpack 調整,請參閱本指南末尾的「額外考量」章節,以取得有關在 Next.js 中複製或調整這些功能的提示。
步驟 1:安裝 Next.js 依賴
在您現有的專案中安裝 Next.js
npm install next@latest
步驟 2:建立 Next.js 設定檔
在您的專案根目錄(與您的 package.json
相同的層級)建立一個 next.config.ts
。此檔案包含您的 Next.js 設定選項。
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
output: 'export', // Outputs a Single-Page Application (SPA)
distDir: 'build', // Changes the build output directory to `build`
}
export default nextConfig
注意:使用
output: 'export'
表示您正在進行靜態匯出。您將無法存取伺服器端功能,例如 SSR 或 API。您可以移除此行以利用 Next.js 伺服器功能。
步驟 3:建立根版面配置
Next.js App Router 應用程式必須包含一個根版面配置檔案,這是一個React 伺服器元件,它將包裝您的所有頁面。
在 CRA 應用程式中,根版面配置檔案最接近的對應項是 public/index.html
,其中包含您的 <html>
、<head>
和 <body>
標籤。
- 在您的
src
目錄內(或如果您喜歡將app
放在根目錄,則在您的專案根目錄)建立一個新的app
目錄。 - 在
app
目錄內,建立一個layout.tsx
(或layout.js
)檔案
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return '...'
}
現在將您舊的 index.html
的內容複製到此 <RootLayout>
元件中。將 body div#root
(和 body noscript
)替換為 <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>
)
}
需知:Next.js 預設會忽略 CRA 的
public/manifest.json
、額外的圖示和測試設定。如果您需要這些,Next.js 透過其 Metadata API 和 Testing 設定提供支援。
步驟 4:元數據
Next.js 自動包含 <meta charset="UTF-8" />
和 <meta name="viewport" content="width=device-width, initial-scale=1" />
標籤,因此您可以從 <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>
)
}
最後,Next.js 可以使用 Metadata API 管理您最後的 <head>
標籤。將您最終的元數據資訊移動到匯出的 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 框架內建的基於慣例的方法 (Metadata API)。此方法使您能夠更輕鬆地改善您頁面的 SEO 和網路分享性。
步驟 5:樣式
與 CRA 類似,Next.js 開箱即用支援 CSS Modules。它也支援全域 CSS 匯入。
如果您有全域 CSS 檔案,請將其匯入到您的 app/layout.tsx
中
import '../index.css'
export const 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>
)
}
如果您正在使用 Tailwind CSS,請參閱我們的安裝文件。
步驟 6:建立入口點頁面
Create React App 使用 src/index.tsx
(或 index.js
)作為入口點。在 Next.js (App Router) 中,app
目錄內的每個資料夾都對應到一個路由,而每個資料夾都應該有一個 page.tsx
。
由於我們現在想要將應用程式保留為 SPA 並攔截所有路由,因此我們將使用可選的 catch-all 路由。
- 在
app
內建立一個[[...slug]]
目錄。
app
┣ [[...slug]]
┃ ┗ page.tsx
┣ layout.tsx
- 將以下內容新增到
page.tsx
:
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return '...' // We'll update this
}
這告訴 Next.js 為空的 slug (/
) 生成單一路由,有效地將所有路由映射到同一個頁面。此頁面是一個伺服器元件,預先渲染為靜態 HTML。
步驟 7:新增僅限客戶端的入口點
接下來,我們將把您的 CRA 的根 App 元件嵌入到客戶端元件中,以便所有邏輯都保留在客戶端。如果您是第一次使用 Next.js,值得注意的是,客戶端元件(預設情況下)仍然在伺服器上預先渲染。您可以將它們視為具有執行客戶端 JavaScript 的附加功能。
在 app/[[...slug]]/
中建立一個 client.tsx
(或 client.js
)
'use client'
import dynamic from 'next/dynamic'
const App = dynamic(() => import('../../App'), { ssr: false })
export function ClientOnly() {
return <App />
}
'use client'
指令使此檔案成為客戶端元件。- 具有
ssr: false
的dynamic
匯入停用了<App />
元件的伺服器端渲染,使其成為真正的僅限客戶端 (SPA)。
現在更新您的 page.tsx
(或 page.js
)以使用您的新元件
import { ClientOnly } from './client'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return <ClientOnly />
}
步驟 8:更新靜態圖片匯入
在 CRA 中,匯入圖片檔案會將其 public 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>
標籤將減少應用程式中的變更量,並防止上述問題。然後,您可以選擇稍後遷移到 <Image>
元件,以透過設定 loader 或遷移到具有自動圖片優化的預設 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 URL。例如,public/logo.png
將為您的應用程式在 /logo.png
提供圖片,這將是 src
值。
警告:如果您正在使用 TypeScript,則在存取
src
屬性時可能會遇到類型錯誤。若要修正這些錯誤,您需要將next-env.d.ts
新增到您的tsconfig.json
檔案的include
陣列中。當您在步驟 9 中執行您的應用程式時,Next.js 將自動產生此檔案。
步驟 9:遷移環境變數
Next.js 支援環境變數的方式與 CRA 類似,但對於您想要在瀏覽器中公開的任何變數,都要求使用 NEXT_PUBLIC_
前綴。
主要差異是用於在客戶端公開環境變數的前綴。將所有具有 REACT_APP_
前綴的環境變數變更為 NEXT_PUBLIC_
。
步驟 10:更新 package.json
中的腳本
更新您的 package.json
腳本以使用 Next.js 命令。此外,將 .next
和 next-env.d.ts
新增到您的 .gitignore
{
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "npx serve@latest ./build"
}
}
# ...
.next
next-env.d.ts
現在您可以執行
npm run dev
開啟 https://127.0.0.1:3000。您應該可以看到您的應用程式現在正在 Next.js 上執行(在 SPA 模式下)。
步驟 11:清除
您現在可以移除特定於 Create React App 的產物
public/index.html
src/index.tsx
src/react-app-env.d.ts
reportWebVitals
設定react-scripts
依賴項(從package.json
中解除安裝)
其他考量
在 CRA 中使用自訂 homepage
如果您在 CRA package.json
中使用 homepage
欄位,以在特定子路徑下提供應用程式,您可以使用 next.config.ts
中的 basePath
設定,在 Next.js 中複製該設定。
import { NextConfig } from 'next'
const nextConfig: NextConfig = {
basePath: '/my-subpath',
// ...
}
export default nextConfig
處理自訂 Service Worker
如果您使用 CRA 的 service worker(例如,來自 create-react-app
的 serviceWorker.js
),您可以學習如何使用 Next.js 建立 漸進式 Web 應用程式 (PWA)。
代理 API 請求
如果您的 CRA 應用程式在 package.json
中使用 proxy
欄位,將請求轉發到後端伺服器,您可以使用 next.config.ts
中的 Next.js 重新導向來複製此行為。
import { NextConfig } from 'next'
const nextConfig: NextConfig = {
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'https://your-backend.com/:path*',
},
]
},
}
自訂 Webpack / Babel 設定
如果您在 CRA 中有自訂 webpack 或 Babel 設定,您可以在 next.config.ts
中擴展 Next.js 的設定。
import { NextConfig } from 'next'
const nextConfig: NextConfig = {
webpack: (config, { isServer }) => {
// Modify the webpack config here
return config
},
}
export default nextConfig
注意:這將需要透過從您的
dev
指令碼中移除--turbopack
來停用 Turbopack。
TypeScript 設定
如果您有 tsconfig.json
,Next.js 會自動設定 TypeScript。請確保 next-env.d.ts
列在您的 tsconfig.json
include
陣列中
{
"include": ["next-env.d.ts", "app/**/*", "src/**/*"]
}
Bundler 相容性
Create React App 和 Next.js 預設都使用 webpack 進行捆綁。Next.js 也提供 Turbopack,以便在本地開發時更快地進行
next dev --turbopack
如果您需要從 CRA 遷移進階 webpack 設定,您仍然可以提供 自訂 webpack 設定。
下一步
如果一切正常,您現在應該有一個可運作的 Next.js 應用程式,以單頁應用程式的形式執行。您尚未利用 Next.js 的功能(如伺服器端渲染或基於檔案的路徑),但您現在可以逐步執行。
- 將從 React Router 遷移到 Next.js App Router 以獲得
- 自動程式碼拆分
- 串流伺服器渲染
- React 伺服器元件
- 使用
<Image>
元件最佳化圖片 - 使用
next/font
最佳化字體 - 使用
<Script>
元件最佳化第三方指令碼 - 透過執行
npx next lint
並將其設定為符合您專案的需求,啟用 ESLint 以及 Next.js 建議的規則
注意:使用靜態匯出 (
output: 'export'
) 目前不支援useParams
Hook 或其他伺服器功能。若要使用所有 Next.js 功能,請從您的next.config.ts
中移除output: 'export'
。
這有幫助嗎?