Next.js App Router 遷移教學:從 Pages Router 無痛升級完整指南
自從 Next.js 13 推出 App Router 以來,很多團隊都面臨一個問題:「我們現有的 Pages Router 專案要不要遷移?怎麼遷?」
答案是:要遷,但不急著一次全部遷完。Next.js 官方支援兩種路由器共存,你可以漸進式地遷移,不需要大爆炸式的重構。
我最近幫公司把一個 50+ 頁面的 Pages Router 專案遷到 App Router,踩了不少坑。這篇會把我的經驗整理出來,讓你少走彎路。相關的前端知識,你也可以看看React 19 新功能完整介紹。
為什麼要遷移到 App Router
如果你的 Pages Router 專案跑得好好的,為什麼要花時間遷移?幾個實際的理由:
- Server Components:預設在伺服器端渲染,大幅減少客戶端 JavaScript bundle size
- 巢狀佈局(Nested Layouts):不再需要在每個頁面都重複 Layout 邏輯,路由切換時共用佈局不會重新渲染
- 串流渲染(Streaming SSR):配合 Suspense,可以逐步載入頁面,提升使用者體驗
- Server Actions:在 Server Component 中直接處理表單提交,不需要額外建 API Route
- 未來的功能更新都以 App Router 為主:Pages Router 進入維護模式,新功能(如 Partial Prerendering)只有 App Router 才有
遷移前的準備工作
動手之前,先做這幾件事:
- 升級到 Next.js 最新版:確保你的 Next.js 版本是 14.x 以上。App Router 在 14 之後穩定多了
- 盤點現有頁面:列出所有 pages/ 目錄下的檔案,標記哪些是純靜態、哪些用了 getServerSideProps、哪些用了 getStaticProps
- 確認第三方套件的相容性:有些套件(特別是依賴 Client-side 狀態的)可能需要升級或替換
- 建立 app/ 目錄:Next.js 允許 pages/ 和 app/ 共存,所以你可以直接建立 app/ 目錄開始遷移
路由結構對照與轉換
最基本的差異是檔案命名規則:
Pages Router:
pages/
├── index.tsx → /
├── about.tsx → /about
├── blog/
│ ├── index.tsx → /blog
│ └── [slug].tsx → /blog/:slug
└── _app.tsx → 全域 Layout
App Router:
app/
├── page.tsx → /
├── layout.tsx → 全域 Layout
├── about/
│ └── page.tsx → /about
└── blog/
├── page.tsx → /blog
└── [slug]/
└── page.tsx → /blog/:slug
最大的不同:App Router 裡每個路由都是一個資料夾,裡面放 page.tsx。這個設計讓你可以在同一個路由資料夾裡放 layout.tsx、loading.tsx、error.tsx 等特殊檔案。
Layout 與 Template 的設計
App Router 最大的優勢之一就是巢狀佈局。在 Pages Router 裡,你可能這樣做:
// Pages Router: 每個頁面都要手動套 Layout
export default function BlogPage() {
return (
<MainLayout>
<BlogSidebar>
<article>...</article>
</BlogSidebar>
</MainLayout>
);
}
App Router 中,你只需要在對應的路由資料夾放一個 layout.tsx:
// app/blog/layout.tsx
export default function BlogLayout({ children }) {
return (
<div className="flex">
<BlogSidebar />
<main>{children}</main>
</div>
);
}
所有 /blog/* 路由都會自動套用這個佈局,而且切換頁面時 Sidebar 不會重新渲染。
注意 layout.tsx 和 template.tsx 的差別:Layout 在路由切換時保持狀態(不重新掛載),Template 每次切換都會重新掛載。大部分情況用 Layout 就對了。
資料獲取方式的改變
這是遷移中改動最大的部分。Pages Router 的三大資料獲取函式在 App Router 中都消失了:
getStaticProps → 直接在 Server Component 中 fetch
// App Router: Server Component (預設)
async function BlogPage({ params }) {
const post = await fetch(`https://api.example.com/posts/${params.slug}`);
const data = await post.json();
return <Article data={data} />;
}
getServerSideProps → 加上 cache: 'no-store'
const res = await fetch('https://api.example.com/data', {
cache: 'no-store' // 等同於 SSR
});
getStaticPaths → generateStaticParams
export async function generateStaticParams() {
const posts = await getPosts();
return posts.map((post) => ({
slug: post.slug,
}));
}
Client Component vs Server Component
App Router 的所有元件預設都是 Server Component。如果你需要用到 useState、useEffect、事件處理等客戶端功能,需要在檔案頂部加上 'use client'。
遷移策略:
- 先把整個頁面搬過去,如果用了 hooks,先加上
'use client' - 然後逐步把不需要互動的部分抽成 Server Component
- 只把真正需要客戶端功能的部分保持為 Client Component
常見的 Client Component:表單、搜尋框、下拉選單、動態互動元素。
適合 Server Component:文章內容、列表頁、靜態區塊、資料庫查詢。
Metadata 與 SEO 設定遷移
Pages Router 用 next/head 設定 SEO 標籤,App Router 改用 Metadata API:
// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }) {
const post = await getPost(params.slug);
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
images: [post.coverImage],
},
};
}
這個 API 比 next/head 更好用,而且支援動態生成、自動去重複、和 Server Component 完美整合。
中介軟體與認證處理
好消息:middleware.ts 的用法在兩個 Router 中是一樣的,不需要改。
但如果你用了自定義的認證邏輯(像是在 getServerSideProps 裡檢查 Session),需要改成:
- 在 Server Component 中使用
cookies()或headers()讀取認證資訊 - 或者在
middleware.ts中統一處理認證邏輯 - 搭配
next-authv5 的話,它已經原生支援 App Router
漸進式遷移策略
推薦的遷移順序:
- 第一步:建立
app/layout.tsx,把_app.tsx的全域設定搬過去 - 第二步:從最簡單的靜態頁面開始遷移(About、Contact 等)
- 第三步:遷移列表頁和詳細頁(Blog Index → Blog Detail)
- 第四步:遷移有複雜互動的頁面(Dashboard、表單等)
- 第五步:遷移 API Routes 到 Route Handlers
- 第六步:清理 pages/ 目錄中已遷移的檔案
重要提醒:同一個路由不能同時存在於 pages/ 和 app/ 中。如果 pages/about.tsx 和 app/about/page.tsx 同時存在,Next.js 會報錯。
結語
Pages Router 到 App Router 的遷移不是一蹴可幾的事,但也沒有想像中可怕。關鍵是漸進式遷移——不需要一次全部改完,一次改一兩個頁面,保持專案可以正常運行。
如果你的專案還在 Pages Router,我建議至少把新功能開始用 App Router 開發。等團隊熟悉了 Server Component 的開發模式,再回頭慢慢遷移舊頁面。
你可能也喜歡
探索其他領域的精選好文
LangChain vs LlamaIndex 完整比較:2026 年 RAG 框架到底該怎麼選?
在 RAG 應用開發中,LangChain 和 LlamaIndex 是最常被拿來比較的兩大框架。這篇文章從架構設計、效能數據到實戰經驗,幫你釐清到底該選哪一個。
Google SGE 對 SEO 的影響:2026 年你必須知道的因應策略
Google AI Overview 已經出現在將近一半的搜尋結果中。SEO 不會死,但規則正在改變。這篇整理最新數據和五個你現在就該開始做的因應策略。
DaVinci Resolve 免費影片剪輯入門教學:從安裝到完成第一支影片
DaVinci Resolve 是好萊塢等級的剪輯軟體,但免費版就能滿足 90% 的需求。這篇帶你從安裝開始,一步步完成第一支影片。
Redis 快取策略教學:Cache-Aside、Write-Through 到實戰踩坑全紀錄
快取不是 set/get 那麼簡單。這篇從 Cache-Aside、Write-Through 到 Write-Behind,帶你理解每種策略的取捨,加上我踩過的坑,幫你少走彎路。