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

并行路由允许你为每个路由定义独立的 error 和 loading 状态。因为它们是独立被 stream 进来的。

并行路由也允许你根据某些条件(例如身份验证)渲染一个 slot(插槽),从而在相同的 url 上实现完全分离的代码。

约定
并行路由是使用命名 slots(插槽)实现的。slots 使用@folder 约定定义,并作为属性传递给同级别的布局。
slots 不会影响路由的 segments 以及不会影响 URL 的结构。路径/@team/members可以在/members访问。
举个例子,下面的文件结构定义了两个插槽: @analytics 和 @team.

上面的文件结构意味着在app/lauout.js的组件会接受到 @analytics 和 @team插槽的 props,并且能够将它们与children并行。
js1export default function Layout(props: { 2 children: React.ReactNode 3 analytics: React.ReactNode 4 team: React.ReactNode 5}) { 6 return ( 7 <> 8 {props.children} 9 {props.team} 10 {props.analytics} 11 </> 12 ) 13}
childrenprops 是一个隐式 slot,不需要映射到文件夹。这意味着app/page.js相当于app/@children/page.js。
不匹配的路由
默认情况下,slot 中呈现的内容会匹配当前 url。
在 slot 不匹配的情况下,Next.js 呈现的内容因路由技术和文件夹结构而异。
default.js
当 Next.js 无法根据当前 URL 恢复slot的活动状态时,您可以定义一个 default.js 文件作为后备渲染。
参考以下文件夹结构。@team 插槽有一个 settings 目录,但 @analytics 没有。

navigation
在导航时,Next.js 将呈现插槽以前的活动状态,即使它与当前 URL 不匹配。
reload
重新加载时,Next.js 将首先尝试渲染不匹配插槽的 default.js 文件。如果不可用,则会渲染 404。
未匹配路由的 404 页面有助于确保您不会意外地渲染不应该并行呈现的路由。
useSelectedLayoutSegment(s)
useSelectedLayoutSegment 和 useSelectedLayoutSegments 两个都接受一个参数 parallelRoutesKey, 允许你在该插槽中读取活跃的路由。
typescript1'use client' 2 3import { useSelectedLayoutSegment } from 'next/navigation' 4 5export default async function Layout(props: { 6 //... 7 auth: React.ReactNode 8}) { 9 const loginSegments = useSelectedLayoutSegment('auth') 10 // ... 11}
当用户导航到 @auth/login,或在 URL 地址栏中输入 /login 时,loginSegments 将等于字符串 login。
例子
Modals(模态框)
并行路由可以用来渲染模态框

@auth 插槽可以渲染<Modal>组件,可以通过导航到匹配的路由(例如 /login)来显示。
typescript1// app/layout.tsx 2export default async function Layout(props: { 3 // ... 4 auth: React.ReactNode 5}) { 6 return ( 7 <> 8 {/* ... */} 9 {props.auth} 10 </> 11 ) 12}
typescript1// app/@auth/login/page.tsx 2import { Modal } from 'components/modal' 3 4export default function Login() { 5 return ( 6 <Modal> 7 <h1>Login</h1> 8 {/* ... */} 9 </Modal> 10 ) 11}
为了确保模态的内容在它不活动时不会被渲染,可以创建一个返回 null 的 default.js 文件。
typescript1export default function Default() { 2 return null 3}
关闭模态框
如果模态框是通过客户端导航初始化的,例如通过使用 <Link href="/login">,您可以通过调用 router.back() 或使用 Link 组件来关闭模态框。
typescript1// app/@auth/login/page.tsx 2'use client' 3import { useRouter } from 'next/navigation' 4import { Modal } from 'components/modal' 5 6export default async function Login() { 7 const router = useRouter() 8 return ( 9 <Modal> 10 <span onClick={() => router.back()}>Close modal</span> 11 <h1>Login</h1> 12 ... 13 </Modal> 14 ) 15}
如果您想要导航到其他位置并关闭模态框,还可以使用一个捕获所有路由。

typescript1// app/@auth/[...catchAll]/page.tsx 2export default function CatchAll() { 3 return null 4}
catchAll路由优先于 default.js。
条件路由
并行路由可以用于实现条件性路由。例如,您可以根据身份验证状态呈现 @dashboard 或 @login 路由。
typescript1import { getUser } from '@/lib/auth' 2 3export default function Layout({ dashboard, login }: { dashboard: React.ReactNode; login: React.ReactNode }) { 4 const isLoggedIn = getUser() 5 return isLoggedIn ? dashboard : login 6}
