如何從 Pages Router 遷移至 App Router
本指南將協助您:
升級步驟
Node.js 版本
最低 Node.js 版本要求現在是 v18.17。詳情請參閱 Node.js 文件。
Next.js 版本
要更新至 Next.js 版本 13,請使用您偏好的套件管理工具執行以下指令:
ESLint 版本
如果您使用 ESLint,需要升級 ESLint 版本:
小提示:您可能需要重啟 VS Code 中的 ESLint 伺服器才能使變更生效。開啟命令面板(Mac 上按
cmd+shift+p
;Windows 上按ctrl+shift+p
)並搜尋ESLint: Restart ESLint Server
。
後續步驟
更新完成後,請參閱以下章節進行後續操作:
- 升級新功能:幫助您升級至新功能的指南,例如改進的 Image 和 Link 元件。
- 從
pages
目錄遷移至app
目錄:逐步指南,協助您從pages
目錄逐步遷移至app
目錄。
升級新功能
Next.js 13 引入了新的 App Router,帶來新功能和慣例。新的 Router 位於 app
目錄中,並與 pages
目錄共存。
升級至 Next.js 13 不需要使用 App Router。您可以繼續使用 pages
目錄,同時享受適用於兩個目錄的新功能,例如更新的 Image 元件、Link 元件、Script 元件 和 字體優化。
<Image/>
元件
Next.js 12 透過臨時導入 next/future/image
對 Image 元件進行了改進。這些改進包括減少客戶端 JavaScript、更易於擴展和樣式化圖片、更好的無障礙性以及原生瀏覽器延遲載入。
在版本 13 中,這些新行為現在是 next/image
的預設值。
有兩個程式碼轉換工具可協助您遷移至新的 Image 元件:
next-image-to-legacy-image
程式碼轉換:安全且自動地將next/image
導入重新命名為next/legacy/image
。現有元件將保持相同行為。next-image-experimental
程式碼轉換:危險地添加內聯樣式並移除未使用的屬性。這將改變現有元件的行為以符合新的預設值。使用此程式碼轉換前,需先執行next-image-to-legacy-image
程式碼轉換。
<Link>
元件
<Link>
元件 不再需要手動添加 <a>
標籤作為子元素。此行為在 版本 12.2 中作為實驗性選項添加,現在成為預設值。在 Next.js 13 中,<Link>
總是渲染 <a>
並允許您將屬性傳遞給底層標籤。
例如:
要將您的連結升級至 Next.js 13,可以使用 new-link
程式碼轉換。
<Script>
元件
next/script
的行為已更新以支援 pages
和 app
,但需要進行一些更改以確保順利遷移:
- 將之前包含在
_document.js
中的任何beforeInteractive
腳本移至根佈局檔案(app/layout.tsx
)。 - 實驗性的
worker
策略尚不適用於app
,標記為此策略的腳本需要移除或修改為使用其他策略(例如lazyOnload
)。 onLoad
、onReady
和onError
處理程序在伺服器元件中不起作用,因此請確保將它們移至 客戶端元件 或完全移除。
字體優化
先前,Next.js 透過 內聯字體 CSS 協助您優化字體。版本 13 引入了新的 next/font
模組,讓您能夠自定義字體載入體驗,同時確保出色的效能和隱私。next/font
同時支援 pages
和 app
目錄。
雖然 內聯 CSS 在 pages
中仍然有效,但在 app
中不起作用。您應該改用 next/font
。
請參閱 字體優化 頁面以了解如何使用 next/font
。
從 pages
遷移至 app
🎥 觀看影片:學習如何逐步採用 App Router → YouTube (16 分鐘)。
遷移至 App Router 可能是首次使用 Next.js 基於 React 功能(如伺服器元件、Suspense 等)的體驗。結合 Next.js 的新功能(如 特殊檔案 和 佈局),遷移意味著需要學習新概念、思維模型和行為變更。
我們建議透過將遷移分解為較小的步驟來降低這些更新的複雜性。app
目錄的設計初衷是與 pages
目錄同時工作,以實現逐頁遷移。
app
目錄支援嵌套路由 和 佈局。了解更多。- 使用嵌套資料夾定義路由,並使用特殊的
page.js
檔案使路由段公開可訪問。了解更多。 - 特殊檔案慣例 用於為每個路由段建立 UI。最常見的特殊檔案是
page.js
和layout.js
。- 使用
page.js
定義路由特有的 UI。 - 使用
layout.js
定義跨多個路由共享的 UI。 - 特殊檔案可以使用
.js
、.jsx
或.tsx
副檔名。
- 使用
- 您可以在
app
目錄中並置其他檔案,例如元件、樣式、測試等。了解更多。 - 資料獲取函數如
getServerSideProps
和getStaticProps
已被app
中的 新 API 取代。getStaticPaths
已被generateStaticParams
取代。 pages/_app.js
和pages/_document.js
已被單一的app/layout.js
根佈局取代。了解更多。pages/_error.js
已被更細粒度的error.js
特殊檔案取代。了解更多。pages/404.js
已被not-found.js
檔案取代。pages/api/*
API 路由已被route.js
(路由處理器)特殊檔案取代。
步驟 1:建立 app
目錄
更新至最新 Next.js 版本(需 13.4 或更高):
然後,在專案根目錄(或 src/
目錄)建立新的 app
目錄。
步驟 2:建立根佈局
在 app
目錄中建立新的 app/layout.tsx
檔案。這是 根佈局,將應用於 app
內的所有路由。
app
目錄 必須 包含根佈局。- 根佈局必須定義
<html>
和<body>
標籤,因為 Next.js 不會自動建立它們。 - 根佈局取代了
pages/_app.tsx
和pages/_document.tsx
檔案。 - 佈局檔案可以使用
.js
、.jsx
或.tsx
副檔名。
要管理 <head>
HTML 元素,可以使用 內建的 SEO 支援:
遷移 _document.js
和 _app.js
如果您有現有的 _app
或 _document
檔案,可以將其內容(例如全域樣式)複製到根佈局(app/layout.tsx
)。app/layout.tsx
中的樣式 不會 應用於 pages/*
。您應該保留 _app
/_document
以在遷移期間防止 pages/*
路由損壞。完全遷移後,即可安全刪除它們。
如果您使用任何 React Context 提供者,需要將它們移至 客戶端元件。
將 getLayout()
模式遷移至佈局(可選)
Next.js 建議在 pages
目錄中為頁面元件添加 屬性 以實現每頁佈局。此模式可被 app
目錄中對 嵌套佈局 的原生支援取代。
查看前後範例
之前
之後
-
從
pages/dashboard/index.js
中移除Page.getLayout
屬性,並按照 遷移頁面的步驟 將頁面移至app
目錄。app/dashboard/page.js -
將
DashboardLayout
的內容移至新的 客戶端元件 以保留pages
目錄的行為。app/dashboard/DashboardLayout.js -
將
DashboardLayout
導入到app
目錄中的新layout.js
檔案。app/dashboard/layout.js -
您可以逐步將
DashboardLayout.js
(客戶端元件)中的非互動部分移至layout.js
(伺服器元件),以減少傳送至客戶端的元件 JavaScript 量。
步驟 3:遷移 next/head
在 pages
目錄中,next/head
React 元件用於管理 <head>
HTML 元素,如 title
和 meta
。在 app
目錄中,next/head
被新的 內建 SEO 支援 取代。
之前:
之後:
步驟 4:遷移頁面
- 在
app
目錄 中的頁面預設為 伺服器元件 (Server Components)。這與pages
目錄不同,後者中的頁面是 客戶端元件 (Client Components)。 - 資料獲取 (Data fetching) 在
app
目錄中有所改變。getServerSideProps
、getStaticProps
和getInitialProps
已被更簡單的 API 取代。 app
目錄使用巢狀資料夾來定義路由,並使用特殊的page.js
檔案來公開存取路由片段。-
pages
目錄app
目錄路由 index.js
page.js
/
about.js
about/page.js
/about
blog/[slug].js
blog/[slug]/page.js
/blog/post-1
我們建議將頁面遷移分為兩個主要步驟:
- 步驟 1:將預設匯出的頁面元件移動到新的客戶端元件中。
- 步驟 2:將新的客戶端元件匯入到
app
目錄中的新page.js
檔案中。
須知:這是最簡單的遷移路徑,因為它的行為與
pages
目錄最為相似。
步驟 1:建立新的客戶端元件
- 在
app
目錄中建立一個新的獨立檔案(例如app/home-page.tsx
或類似檔案),該檔案匯出一個客戶端元件。要定義客戶端元件,請在檔案頂部(在任何匯入之前)添加'use client'
指令。- 與 Pages Router 類似,有一個 優化步驟 可以在初始頁面載入時將客戶端元件預渲染為靜態 HTML。
- 將預設匯出的頁面元件從
pages/index.js
移動到app/home-page.tsx
。
步驟 2:建立新頁面
-
在
app
目錄中建立一個新的app/page.tsx
檔案。這預設是一個伺服器元件。 -
將
home-page.tsx
客戶端元件匯入到頁面中。 -
如果您在
pages/index.js
中獲取資料,請使用新的 資料獲取 API 將資料獲取邏輯直接移動到伺服器元件中。詳情請參閱 資料獲取升級指南。 -
如果您的舊頁面使用了
useRouter
,您需要更新為新的路由鉤子。了解更多。 -
啟動您的開發伺服器並訪問
http://localhost:3000
。您應該會看到現有的索引路由,現在是通過 app 目錄提供的。
步驟 5:遷移路由鉤子
新增了一個新的路由器來支援 app
目錄中的新行為。
在 app
目錄中,您應該使用從 next/navigation
匯入的三個新鉤子:useRouter()
、usePathname()
和 useSearchParams()
。
- 新的
useRouter
鉤子是從next/navigation
匯入的,其行為與從next/router
匯入的pages
中的useRouter
鉤子不同。- 從
next/router
匯入的useRouter
鉤子 在app
目錄中不受支援,但可以繼續在pages
目錄中使用。
- 從
- 新的
useRouter
不返回pathname
字串。請改用獨立的usePathname
鉤子。 - 新的
useRouter
不返回query
物件。搜尋參數和動態路由參數現在是分開的。請改用useSearchParams
和useParams
鉤子。 - 您可以一起使用
useSearchParams
和usePathname
來監聽頁面變化。詳情請參閱 路由器事件 (Router Events) 部分。 - 這些新鉤子僅在客戶端元件中受支援。它們不能在伺服器元件中使用。
此外,新的 useRouter
鉤子有以下變更:
isFallback
已被移除,因為fallback
已被 取代。locale
、locales
、defaultLocales
、domainLocales
值已被移除,因為內建的 i18n Next.js 功能在app
目錄中不再需要。了解更多關於 i18n。basePath
已被移除。替代方案不會是useRouter
的一部分。它尚未實現。asPath
已被移除,因為as
的概念已從新路由器中移除。isReady
已被移除,因為它不再必要。在 靜態渲染 (static rendering) 期間,任何使用useSearchParams()
鉤子的元件將跳過預渲染步驟,而是在運行時在客戶端渲染。route
已被移除。usePathname
或useSelectedLayoutSegments()
提供了替代方案。
在 pages
和 app
之間共享元件
要保持元件在 pages
和 app
路由器之間相容,請參考 從 next/compat/router
匯入的 useRouter
鉤子。
這是 pages
目錄中的 useRouter
鉤子,但旨在用於在路由器之間共享元件。當您準備好僅在 app
路由器中使用它時,請更新為新的 從 next/navigation
匯入的 useRouter
。
步驟 6:遷移資料獲取方法
pages
目錄使用 getServerSideProps
和 getStaticProps
來獲取頁面的資料。在 app
目錄中,這些先前的資料獲取函數被替換為基於 fetch()
和 async
React 伺服器元件的 更簡單的 API。
伺服器端渲染 (getServerSideProps
)
在 pages
目錄中,getServerSideProps
用於在伺服器上獲取資料並將 props 傳遞給檔案中的預設匯出 React 元件。頁面的初始 HTML 從伺服器預渲染,然後在瀏覽器中「水合」(hydrating) 頁面(使其具有互動性)。
在 App Router 中,我們可以使用 伺服器元件 (Server Components) 將資料獲取邏輯放在 React 元件中。這允許我們向客戶端發送更少的 JavaScript,同時保留伺服器渲染的 HTML。
通過將 cache
選項設置為 no-store
,我們可以指示獲取的資料應該 永遠不被快取。這類似於 pages
目錄中的 getServerSideProps
。
存取請求物件
在 pages
目錄中,您可以基於 Node.js HTTP API 獲取基於請求的資料。
例如,您可以從 getServerSideProps
獲取 req
物件,並使用它來獲取請求的 cookies 和 headers。
app
目錄公開了新的唯讀函數來獲取請求資料:
headers
:基於 Web Headers API,可以在 伺服器元件 (Server Components) 中用於獲取請求標頭。cookies
:基於 Web Cookies API,可以在 伺服器元件 (Server Components) 中用於獲取 cookies。
靜態網站生成 (getStaticProps
)
在 pages
目錄中,getStaticProps
函數用於在構建時預渲染頁面。此函數可用於從外部 API 或直接從資料庫獲取資料,並在構建期間將此資料傳遞給整個頁面。
在 app
目錄中,使用 fetch()
獲取資料將預設為 cache: 'force-cache'
,這將快取請求資料直到手動失效。這類似於 pages
目錄中的 getStaticProps
。
動態路徑 (getStaticPaths
)
在 pages
目錄中,getStaticPaths
函數用於定義在建置時應預渲染的動態路徑。
在 app
目錄中,getStaticPaths
已被 generateStaticParams
取代。
generateStaticParams
的行為與 getStaticPaths
類似,但提供了一個簡化的 API 來返回路由參數,並且可以在 layouts 內部使用。generateStaticParams
的返回形式是一個片段陣列,而不是嵌套的 param
物件陣列或解析後的路徑字串。
在 app
目錄的新模型中,使用 generateStaticParams
這個名稱比 getStaticPaths
更為合適。get
前綴被更具描述性的 generate
取代,這在 getStaticProps
和 getServerSideProps
不再必要的情況下更為貼切。Paths
後綴被 Params
取代,這對於具有多個動態片段的路由更為合適。
取代 fallback
在 pages
目錄中,getStaticPaths
返回的 fallback
屬性用於定義未在建置時預渲染的頁面行為。此屬性可以設置為 true
以在頁面生成時顯示回退頁面,false
以顯示 404 頁面,或 blocking
以在請求時生成頁面。
在 app
目錄中,config.dynamicParams
屬性 控制如何處理 generateStaticParams
之外的參數:
true
:(預設值)未包含在generateStaticParams
中的動態片段將按需生成。false
:未包含在generateStaticParams
中的動態片段將返回 404。
這取代了 pages
目錄中 getStaticPaths
的 fallback: true | false | 'blocking'
選項。fallback: 'blocking'
選項未包含在 dynamicParams
中,因為在串流的情況下,'blocking'
和 true
之間的差異可以忽略不計。
當 dynamicParams
設置為 true
(預設值)時,當請求尚未生成的路由片段時,它將被伺服器渲染並緩存。
增量靜態再生 (getStaticProps
與 revalidate
)
在 pages
目錄中,getStaticProps
函數允許你添加 revalidate
字段以在特定時間後自動重新生成頁面。
在 app
目錄中,使用 fetch()
進行數據獲取時可以使用 revalidate
,這將緩存請求指定的秒數。
API 路由
API 路由在 pages/api
目錄中繼續工作,無需任何更改。然而,它們已被 app
目錄中的 路由處理器 (Route Handlers) 取代。
路由處理器允許你使用 Web Request 和 Response API 為給定路由創建自定義請求處理器。
須知:如果你之前使用 API 路由從客戶端調用外部 API,現在可以使用 伺服器組件 (Server Components) 來安全地獲取數據。了解更多關於 數據獲取 (data fetching) 的資訊。
單頁應用程式 (SPA)
如果你同時從單頁應用程式 (SPA) 遷移到 Next.js,請參閱我們的 文檔 以了解更多。
步驟 7:樣式設定
在 pages
目錄中,全局樣式表僅限於 pages/_app.js
。在 app
目錄中,此限制已被取消。全局樣式可以添加到任何布局、頁面或組件中。
Tailwind CSS
如果你使用 Tailwind CSS,你需要在 tailwind.config.js
文件中添加 app
目錄:
你還需要在 app/layout.js
文件中導入全局樣式:
了解更多關於 使用 Tailwind CSS 設定樣式
同時使用 App Router 和 Pages Router
當在不同 Next.js 路由器之間導航時,將發生硬導航。使用 next/link
的自動鏈接預取不會跨路由器預取。
相反,你可以 優化導航 以保留預取和快速的頁面轉換。 了解更多。
Codemods
Next.js 提供了 Codemod 轉換來幫助你在功能被棄用時升級代碼庫。詳見 Codemods 以獲取更多資訊。