平行路由 (Parallel Routes)

平行路由 (Parallel Routing) 讓您能在同一佈局中同時或有條件地渲染一或多個頁面。對於應用中高度動態的區塊,例如社交網站的儀表板和動態消息,平行路由可用來實現複雜的路由模式。

舉例來說,您可以同時渲染團隊和分析頁面。

平行路由圖示

平行路由允許您為每條路由定義獨立的錯誤和載入狀態,因為它們是獨立串流進來的。

平行路由可實現自訂錯誤和載入狀態

平行路由還允許您根據特定條件(例如驗證狀態)有條件地渲染插槽。這使得同一 URL 上能實現完全分離的程式碼。

條件式路由圖示

慣例

平行路由是透過命名插槽 (slots) 建立的。插槽使用 @folder 慣例定義,並作為 props 傳遞給同層級的佈局元件。

插槽 不是 路由區段,且 不會影響 URL 結構。檔案路徑 /@team/members 實際可透過 /members 存取。

例如,以下檔案結構定義了兩個明確的插槽:@analytics@team

平行路由檔案系統結構

上述資料夾結構意味著 app/layout.js 中的元件現在接受 @analytics@team 插槽 props,並可與 children prop 並行渲染:

export default function Layout(props: {
  children: React.ReactNode
  analytics: React.ReactNode
  team: React.ReactNode
}) {
  return (
    <>
      {props.children}
      {props.team}
      {props.analytics}
    </>
  )
}

須知children prop 是一個隱含插槽,無需對應到資料夾。這表示 app/page.js 等同於 app/@children/page.js

未匹配路由

預設情況下,插槽內渲染的內容會匹配當前 URL。

當插槽未匹配時,Next.js 渲染的內容會根據路由技術和資料夾結構而有所不同。

default.js

您可以定義一個 default.js 檔案,當 Next.js 無法根據當前 URL 恢復插槽的活動狀態時,作為後備渲染。

考慮以下資料夾結構。@team 插槽有一個 settings 目錄,但 @analytics 沒有。

平行路由未匹配路由

導航

在導航時,Next.js 會渲染插槽先前活動的狀態,即使它不匹配當前 URL。

重新載入

在重新載入時,Next.js 會先嘗試渲染未匹配插槽的 default.js 檔案。如果該檔案不存在,則會渲染 404 頁面。

未匹配路由的 404 有助於確保您不會意外渲染不應平行渲染的路由。

useSelectedLayoutSegment(s)

useSelectedLayoutSegmentuseSelectedLayoutSegments 都接受一個 parallelRoutesKey 參數,可讓您讀取該插槽內的路由區段。

'use client'

import { useSelectedLayoutSegment } from 'next/navigation'

export default async function Layout(props: {
  //...
  auth: React.ReactNode
}) {
  const loginSegments = useSelectedLayoutSegment('auth')
  // ...
}

當用戶導航到 @auth/login 或直接在 URL 欄輸入 /login 時,loginSegments 會等於字串 "login"

範例

模態框 (Modals)

平行路由可用來渲染模態框。

平行路由圖示

@auth 插槽渲染一個 <Modal> 元件,可透過導航到匹配的路由(例如 /login)來顯示。

export default async function Layout(props: {
  // ...
  auth: React.ReactNode
}) {
  return (
    <>
      {/* ... */}
      {props.auth}
    </>
  )
}

為確保模態框內容在不活動時不會被渲染,您可以建立一個返回 nulldefault.js 檔案。

export default function Default() {
  return null
}

關閉模態框

如果模態框是透過客戶端導航啟動的(例如使用 <Link href="/login">),您可以透過呼叫 router.back() 或使用 Link 元件來關閉模態框。

'use client'
import { useRouter } from 'next/navigation'
import { Modal } from 'components/modal'

export default async function Login() {
  const router = useRouter()
  return (
    <Modal>
      <span onClick={() => router.back()}>關閉模態框</span>
      <h1>Login</h1>
      ...
    </Modal>
  )
}

更多關於模態框的資訊,請參閱 攔截路由 (Intercepting Routes) 章節。

如果您想導航到其他地方並關閉模態框,也可以使用萬用路由 (catch-all route)。

平行路由圖示
export default function CatchAll() {
  return null
}

萬用路由的優先級高於 default.js

條件式路由

平行路由可用來實現條件式路由。例如,您可以根據驗證狀態渲染 @dashboard@login 路由。

import { getUser } from '@/lib/auth'

export default function Layout({
  dashboard,
  login,
}: {
  dashboard: React.ReactNode
  login: React.ReactNode
}) {
  const isLoggedIn = getUser()
  return isLoggedIn ? dashboard : login
}
平行路由驗證範例

On this page