并行路由
并行路由允许您在相同布局中同时或有条件地渲染一个或多个页面。对于应用程序的高度动态部分,例如社交网站上的仪表板和动态信息流,可以使用并行路由来实现复杂的路由模式。
例如,您可以同时渲染团队和分析页面。
并行路由允许您为每个路由在其独立流入时定义独立的错误和加载状态。
并行路由还允许您基于特定条件(如身份验证状态)有条件地渲染一个插槽。这使得可以在相同的 URL 上完全分离的代码。
约定
使用命名插槽创建并行路由。插槽使用 @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)
useSelectedLayoutSegment
和 useSelectedLayoutSegments
都接受一个 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"
。
示例
模态框
并行路由可用于呈现模态框。
@auth
插槽呈现一个 <Modal>
组件,可以通过导航到匹配的路由(例如 /login
)来显示。
export default async function Layout(props: {
// ...
auth: React.ReactNode
}) {
return (
<>
{/* ... */}
{props.auth}
</>
)
}
import { Modal } from 'components/modal'
export default function Login() {
return (
<Modal>
<h1>Login</h1>
{/* ... */}
</Modal>
)
}
为了确保在模态框不活动时不渲染其内容,您可以创建一个返回 null
的 default.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()}>Close modal</span>
<h1>Login</h1>
...
</Modal>
)
}
有关模态框的更多信息请参见截获路由 一节。
如果要导航到其他地方并关闭模态框,还可以使用 catch-all 路由。
export default function CatchAll() {
return null
}
catch-all 路由优先于 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
}