并行路由 (Parallel Routes)

并行路由 (Parallel Routing) 允许您在同一布局中同时或条件性地渲染多个页面。对于应用中高度动态的部分(如社交网站的控制面板和信息流),可以使用并行路由来实现复杂的路由模式。

例如,您可以同时渲染团队页面和分析页面。

并行路由示意图

并行路由允许您为每个独立加载的路由定义独立的错误和加载状态。

并行路由支持自定义错误和加载状态

并行路由还允许您基于特定条件(如认证状态)条件性地渲染插槽。这使得同一 URL 下可以呈现完全分离的代码。

条件路由示意图

约定

并行路由通过命名的插槽 (slots) 创建。插槽使用 @folder 约定定义,并作为 props 传递给同层级的布局组件。

插槽 不是 路由段 (route segments),也 不会影响 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>登录</h1>
      ...
    </Modal>
  )
}

更多关于模态框的信息,请参阅 拦截路由 (Intercepting Routes) 部分。

如果您想导航到其他地方并关闭模态框,也可以使用全捕获 (catch-all) 路由。

并行路由示意图
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