Next.js Parallel Routes 與 Intercepting Routes
在處理複雜的網頁應用程式(如管理後台或社交媒體)時,基礎的路由機制有時不足以應付多樣化的 UI 需求。Next.js 提供了兩個強大的工具:平行路由 與 攔截路由。
平行路由 (Parallel Routes)
平行路由允許你在同一個佈局 (Layout) 中,同時顯不同的頁面。這在構建儀表板 (Dashboard) 時特別有用。
核心運作方式:Slots
你透過在資料夾前加上 @ 符號來定義一個 Slot。
目錄結構:
app/
dashboard/
layout.tsx
@analytics/ # Slot 1
page.tsx
@team/ # Slot 2
page.tsx
在 layout.tsx 中,這些 Slots 會作為 Props 傳入:
// app/dashboard/layout.tsx
export default function Layout({
children, // 對應 app/dashboard/page.tsx
analytics, // 對應 app/dashboard/@analytics/page.tsx
team, // 對應 app/dashboard/@team/page.tsx
}: {
children: React.ReactNode;
analytics: React.ReactNode;
team: React.ReactNode;
}) {
return (
<div className="grid grid-cols-2 gap-4">
<div className="col-span-2">{children}</div>
<div className="bg-white p-4 shadow">{analytics}</div>
<div className="bg-white p-4 shadow">{team}</div>
</div>
);
}
優點
- 獨立暫停 (Independent Streaming):每個 Slot 可以有自己的
loading.tsx,不會互相阻塞。 - 條件渲染:你可以根據權限或狀態來決定是否使用
return null來隱藏某個 Slot。
預設視圖 (default.tsx)
這是使用平行路由時最容易遇到的坑。
當你導航到一個路由(例如 /dashboard/settings)時,Next.js 會同時嘗試載入所有定義的 Slots(@analytics 和 @team)對應於 /settings 的內容。
如果 @analytics 沒有定義 settings/page.tsx,Next.js 該顯示什麼?
這取決於導航的類型:
軟導航 (Soft Navigation) - 點擊
<Link>跳轉: Next.js 會保留該 Slot 當前顯示的內容 (State preserved)。硬導航 (Hard Navigation) - 瀏覽器重新整理 (F5): Next.js 無法得知當前的狀態,因此會嘗試尋找
default.tsx。- 如果有
default.tsx,渲染該內容。 - 如果沒有
default.tsx,Next.js 會渲染 404,導致整個頁面壞掉。
- 如果有
因此,強烈建議為每個 Slot 建立一個 default.tsx,以確保重新整理時不會出錯:
// app/dashboard/@analytics/default.tsx
export default function Default() {
// 當路徑不匹配時,顯示這個內容
// 你可以回傳 null 來什麼都不顯示,或回傳骨架屏
return null;
}
攔截路由 (Intercepting Routes)
攔截路由讓你可以在當前頁面中「攔截」某個導航跳轉,並顯示特定的 UI(通常是 Modal 或側拉窗),同時瀏覽器的 URL 會更新。
符號規範:
(.):匹配同一層級。(..):匹配上一層級。(...):匹配根目錄。
實際案例:照片牆
假設你在瀏覽 /feed 頁面,點擊一張照片。
- 使用者看到:在當前頁面彈出照片 Modal。
- URL 變更為:
/photo/123。 - 如果重新整理頁面:則會直接進入
/photo/123的完整頁面,而不是 Modal。
目錄結構:
app/
feed/
page.tsx # 照片列表
(..)photo/ # 攔截層
[id]/
page.tsx # Modal 內容
photo/
[id]/
page.tsx # 完整頁面內容
// app/feed/(..)photo/[id]/page.tsx
export default function PhotoModal({ params }: { params: { id: string } }) {
return (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center">
<div className="bg-white p-8">這是照片 {params.id} 的彈窗</div>
</div>
);
}
組合應用
平行路由與攔截路由經常搭配使用。例如,在 @modal Slot 下定義一個 (.)login 路由,這樣當使用者點擊登入連結時,就能在不離開當前頁面的情況下顯示登入彈窗。
default.tsx 的定義,以處理重新整理或不匹配路徑時的備位畫面。掌握了這些進階路由模式,你的 Next.js 應用將能擁有媲美原生 App 的流暢體驗。