Skip to main content

并行路由

并行路由允许您在相同布局中同时或有条件地渲染一个或多个页面。对于应用程序的高度动态部分,例如社交网站上的仪表板和动态信息流,可以使用并行路由来实现复杂的路由模式。

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

parallel routes

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

parallel routes cinematic universe

并行路由还允许您基于特定条件(如身份验证状态)有条件地渲染一个插槽。这使得可以在相同的 URL 上完全分离的代码。

conditional routes UI

约定

使用命名插槽创建并行路由。插槽使用 @folder 约定定义,并作为 props 传递给相同级别的布局。

插槽不是路由段,不会影响 URL 结构。文件路径 /@team/members 将在 /members 处可访问。

例如,以下文件结构定义了两个明确的插槽:@analytics@team

parallel routes file system

上面的文件结构意味着 app/layout.js 中的组件现在接受 @analytics@team 插槽的 props, 并可以与 children prop 一起并行渲染:

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

children prop 是一个隐式插槽,无需映射到文件夹。 这意味着 app/page.js 等同于 app/@children/page.js

不匹配的路由

默认情况下,插槽内部呈现的内容将匹配当前 URL。

对于不匹配的插槽,Next.js 渲染的内容会根据路由技术和文件夹结构而异。

default.js

可以定义一个 default.js 文件,作为 Next.js 无法基于当前 URL 恢复插槽的活动状态时的回退呈现。

考虑以下文件结构。@team 插槽有一个 settings 目录,但 @analytics 没有。

parallel routes unmatched routes

导航

在导航时,Next.js 将呈现插槽先前的活动状态,即使它与当前 URL 不匹配。

重新加载

在重新加载时,Next.js 将首先尝试渲染不匹配插槽的 default.js 文件。如果不可用,则呈现一个 404。

note

不匹配路由的 404 有助于确保您不会意外地呈现不应该并行呈现的路由。

useSelectedLayoutSegment(s)

useSelectedLayoutSegmentuseSelectedLayoutSegments 都接受一个 parallelRoutesKey,它允许您读取该插槽中的活动路由段。

app/layout.tsx
'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"

示例

模态框

并行路由可用于呈现模态框。

parallel routes auth modal

@auth 插槽呈现一个 <Modal> 组件,可以通过导航到匹配的路由(例如 /login)来显示。

app/layout.tsx
export default async function Layout(props: {
// ...
auth: React.ReactNode
}) {
return (
<>
{/* ... */}
{props.auth}
</>
)
}
app/@auth/login/page.tsx
import { Modal } from 'components/modal'

export default function Login() {
return (
<Modal>
<h1>Login</h1>
{/* ... */}
</Modal>
)
}

为了确保在模态框不活动时不渲染其内容,您可以创建一个返回 nulldefault.js 文件。

app/@auth/default.tsx
export default function Default() {
return null
}

关闭模态框

如果模态框是通过客户端导航启动的,例如使用 <Link href="/login">, 您可以通过调用 router.back() 或使用 Link 组件来关闭模态框。

app/@auth/login/page.tsx
'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()}>Close modal</span>
<h1>Login</h1>
...
</Modal>
)
}
note

有关模态框的更多信息请参见截获路由 一节。

如果要导航到其他地方并关闭模态框,还可以使用 catch-all 路由。

parallel routes catchall

app/@auth/[...catchAll]/page.tsx
export default function CatchAll() {
return null
}
note

catch-all 路由优先于 default.js

条件路由

并行路由可用于实现条件路由。例如,您可以根据身份验证状态呈现 @dashboard@login 路由。

app/layout.tsx
import { getUser } from '@/lib/auth'

export default function Layout({
dashboard,
login,
}: {
dashboard: React.ReactNode
login: React.ReactNode
}) {
const isLoggedIn = getUser()
return isLoggedIn ? dashboard : login
}

conditional routes UI