Parallel Routes 并行路由
Parallel Routes allows you to simultaneously or conditionally render one or more pages within the same layout. They are useful for highly dynamic sections of an app, such as dashboards and feeds on social sites.
并行路由允许您在同一布局中同时或有条件地呈现一个或多个页面。它们对于应用程序的高度动态部分很有用,例如仪表板和社交网站上的信息流。
For example, considering a dashboard, you can use parallel routes to simultaneously render the team and analytics pages:
例如,考虑一个仪表盘,您可以使用并行路由同时呈现 team 和 analytics 页面:
1.Slots 插槽
Parallel routes are created using named slots. Slots are defined with the @folder convention. For example, the following file structure defines two slots: @analytics and @team:
使用命名插槽创建并行路由。插槽使用 @folder 约定定义。例如,以下文件结构定义了两个插槽: @analytics 和 @team :
Slots are passed as props to the shared parent layout. For the example above, the component in app/layout.js now accepts the @analytics and @team slots props, and can render them in parallel alongside the children prop:
插槽作为道具传递给共享的父布局。对于上面的示例, app/layout.js 中的组件现在接受 @analytics 和 @team 插槽道具,并且可以与 children 道具并行渲染它们:
// app/layout.tsx
export default function Layout({
children,
team,
analytics,
}: {
children: React.ReactNode
analytics: React.ReactNode
team: React.ReactNode
}) {
return (
<>
{children}
{team}
{analytics}
</>
)
}
However, slots are not route segments and do not affect the URL structure. For example, for /dashboard/@analytics/views, the URL will be /dashboard/views since @analytics is a slot.
然而,插槽不是路由段,并且不影响 URL 结构。例如,对于 /dashboard/@analytics/views ,URL 将是 /dashboard/views ,因为 @analytics 是一个插槽。
Good to know: 值得了解:
The
children
prop is an implicit slot that does not need to be mapped to a folder. This meansapp/page.js
is equivalent toapp/@children/page.js
.children
属性是一个隐式插槽,不需要映射到文件夹。这意味着app/page.js
等同于app/@children/page.js
。
2.Active state and navigation 活动状态和导航
By default, Next.js keeps track of the active state (or subpage) for each slot. However, the content rendered within a slot will depend on the type of navigation:
默认情况下,Next.js 会跟踪每个插槽的活动状态(或子页面)。但是,插槽内呈现的内容将取决于导航类型:
-Soft Navigation: During client-side navigation, Next.js will perform a partial render, changing the subpage within the slot, while maintaining the other slot's active subpages, even if they don't match the current URL.
软导航:在客户端导航期间,Next.js 将执行部分渲染,更改插槽内的子页面,同时保持其他插槽的活动子页面,即使它们与当前 URL 不匹配。
-Hard Navigation: After a full-page load (browser refresh), Next.js cannot determine the active state for the slots that don't match the current URL. Instead, it will render a default.js file for the unmatched slots, or 404 if default.js doesn't exist.
硬导航:在整页加载(浏览器刷新)后,Next.js 无法确定与当前 URL 不匹配的插槽的活动状态。相反,它将为不匹配的插槽呈现 default.js 文件,如果 default.js 不存在,则呈现 404 。
Good to know: 值得了解:
The
404
for unmatched routes helps ensure that you don't accidentally render a parallel route on a page that it was not intended for.
针对不匹配路线的404
有助于确保您不会意外地在页面上呈现一条不适合该页面的平行路线。
a.default.js
You can define a default.js file to render as a fallback for unmatched slots during the initial load or full-page reload.
您可以定义一个 default.js 文件,以便在初始加载或整页重新加载期间为不匹配的插槽呈现一个后备方案。
Consider the following folder structure. The @team slot has a /settings page, but @analytics does not.
考虑以下文件夹结构。 @team 插槽有一个 /settings 页面,但 @analytics 没有。
When navigating to /dashboard/settings, the @team slot will render the /settings page while maintaining the currently active page for the @analytics slot.
导航到 /dashboard/settings 时, @team 插槽将呈现 /settings 页面,同时保持 @analytics 插槽的当前活动页面。
On refresh, Next.js will render a default.js for @analytics. If default.js doesn't exist, a 404 is rendered instead.
刷新时,Next.js 将为 default.js 呈现 @analytics 。如果 default.js 不存在,则会呈现 404 。
Additionally, since children is an implicit slot, you also need to create a default.js file to render a fallback for children when Next.js cannot recover the active state of the parent page.
此外,由于 children 是一个隐式插槽,因此您还需要创建一个 default.js 文件,以便在 Next.js 无法恢复父页面的活动状态时为 children 呈现一个后备。
ps: 真是算无遗策啊,居然可以默认加载前页面
b.useSelectedLayoutSegment(s)
Both useSelectedLayoutSegment and useSelectedLayoutSegments accept a parallelRoutesKey parameter, which allows you to read the active route segment within a slot.
useSelectedLayoutSegment 和 useSelectedLayoutSegments 都接受一个 parallelRoutesKey 参数,该参数允许您在插槽内读取活动路由段。
// app/layout.tsx
'use client'
import { useSelectedLayoutSegment } from 'next/navigation'
export default function Layout({ auth }: { auth: React.ReactNode }) {
const loginSegments = useSelectedLayoutSegment('auth')
// ...
}
When a user navigates to app/@auth/login (or /login in the URL bar), loginSegments will be equal to the string "login".
当用户导航到 app/@auth/login (或 URL 栏中的 /login )时, loginSegments 将等于字符串 "login" 。
3.Examples 示例
a.Conditional Routes 条件路由
You can use Parallel Routes to conditionally render routes based on certain conditions, such as user role. For example, to render a different dashboard page for the /admin or /user roles:
您可以使用并行路由根据某些条件(例如用户角色)有条件地呈现路由。例如,为 /admin 或 /user 角色呈现不同的仪表板页面:
// app/dashboard/layout.tsx
import { checkUserRole } from '@/lib/auth'
export default function Layout({
user,
admin,
}: {
user: React.ReactNode
admin: React.ReactNode
}) {
const role = checkUserRole()
return <>{role === 'admin' ? admin : user}</>
}
ps: 丰富了layout的写法
b.Tab Groups 选项卡组
You can add a layout inside a slot to allow users to navigate the slot independently. This is useful for creating tabs.
您可以在插槽内添加 layout ,以允许用户独立浏览插槽。这对于创建选项卡非常有用。
For example, the @analytics slot has two subpages: /page-views and /visitors.
例如, @analytics 插槽有两个子页面: /page-views 和 /visitors 。
Within @analytics, create a layout file to share the tabs between the two pages:
在 @analytics 内,创建一个 layout 文件,以便在两个页面之间共享选项卡:
import Link from 'next/link'
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<>
<nav>
<Link href="/dashboard/page-views">Page Views</Link>
<Link href="/dashboard/visitors">Visitors</Link>
</nav>
<div>{children}</div>
</>
)
}
c.Modals 模态框
Parallel Routes can be used together with Intercepting Routes to create modals. This allows you to solve common challenges when building modals, such as:
平行路由可以与拦截路由一起使用来创建模态框。这使您能够解决构建模态框时遇到的常见挑战,例如:
-Making the modal content shareable through a URL.
通过 URL 共享模态框内容。
-Preserving context when the page is refreshed, instead of closing the modal.
在刷新页面时保留上下文,而不是关闭模态框。
-Closing the modal on backwards navigation rather than going to the previous route.
在向后导航时关闭模态框,而不是转到上一个路由。
-Reopening the modal on forwards navigation.
重新打开模态窗口以进行向前导航。
Consider the following UI pattern, where a user can open a login modal from a layout using client-side navigation, or access a separate /login page:
考虑以下 UI 模式,用户可以使用客户端导航从布局中打开登录模态窗口,或访问单独的 /login 页面:
To implement this pattern, start by creating a /login route that renders your main login page.
要实现此模式,首先创建一个 /login 路由来呈现您的主登录页面。
// app/login/page.tsx
import { Login } from '@/app/ui/login'
export default function Page() {
return <Login />
}
Then, inside the @auth slot, add default.js file that returns null. This ensures that the modal is not rendered when it's not active.
然后,在 @auth 插槽内,添加返回 null 的 default.js 文件。这可确保在模态不处于活动状态时不渲染它。
// app/@auth/default.tsx
export default function Default() {
return null
}
Inside your @auth slot, intercept the /login route by updating the /(.)login folder. Import the <Modal> component and its children into the /(.)login/page.tsx file:
在您的 @auth 插槽中,通过更新 /(.)login 文件夹来拦截 /login 路由。将 <Modal> 组件及其子组件导入到 /(.)login/page.tsx 文件中:
// app/@auth/(.)login/page.tsx
import { Modal } from '@/app/ui/modal'
import { Login } from '@/app/ui/login'
export default function Page() {
return (
<Modal>
<Login />
</Modal>
)
}
Good to know: 值得了解:
The convention used to intercept the route, e.g.
(.)
, depends on your file-system structure. See Intercepting Routes convention.
用于拦截路由的约定(例如(.)
)取决于您的文件系统结构。请参阅拦截路由约定。By separating the
<Modal>
functionality from the modal content (<Login>
), you can ensure any content inside the modal, e.g. forms, are Server Components. See Interleaving Client and Server Components for more information.
通过将<Modal>
功能与模态内容 (<Login>
) 分离,您可以确保模态内的任何内容(例如表单)都是服务器组件。有关更多信息,请参阅交错客户端和服务器组件。
Ⅰ.Opening the modal 打开模态
Now, you can leverage the Next.js router to open and close the modal. This ensures the URL is correctly updated when the modal is open, and when navigating backwards and forwards.
现在,您可以利用 Next.js 路由器来打开和关闭模态。这可确保在模态打开时以及在前后导航时正确更新 URL。
To open the modal, pass the @auth slot as a prop to the parent layout and render it alongside the children prop.
要打开模态,请将 @auth 插槽作为道具传递给父布局,并与 children 道具一起呈现。
import Link from 'next/link'
export default function Layout({
auth,
children,
}: {
auth: React.ReactNode
children: React.ReactNode
}) {
return (
<>
<nav>
<Link href="/login">Open modal</Link>
</nav>
<div>{auth}</div>
<div>{children}</div>
</>
)
}
When the user clicks the <Link>, the modal will open instead of navigating to the /login page. However, on refresh or initial load, navigating to /login will take the user to the main login page.
当用户点击 <Link> 时,将打开模态窗口,而不是导航到 /login 页面。但是,在刷新或初始加载时,导航到 /login 将把用户带到主登录页面。
Ⅱ.Closing the modal 关闭模态框
You can close the modal by calling router.back() or by using the Link component.
您可以通过调用 router.back() 或使用 Link 组件来关闭模态框。
'use client'
import { useRouter } from 'next/navigation'
export function Modal({ children }: { children: React.ReactNode }) {
const router = useRouter()
return (
<>
<button
onClick={() => {
router.back()
}}
>
Close modal
</button>
<div>{children}</div>
</>
)
}
When using the Link component to navigate away from a page that shouldn't render the @auth slot anymore, we use a catch-all route that returns null.
当使用 Link 组件从不应该再渲染 @auth 插槽的页面导航离开时,我们会使用返回 null 的万能路由
// app/ui/modal.tsx
import Link from 'next/link'
export function Modal({ children }: { children: React.ReactNode }) {
return (
<>
<Link href="/">Close modal</Link>
<div>{children}</div>
</>
)
}
// app/@auth/[...catchAll]/page.tsx
export default function CatchAll() {
return null
}
Good to know: 值得了解:
We use a catch-all route in our
@auth
slot to close the modal because of the behavior described in Active state and navigation. Since client-side navigations to a route that no longer match the slot will remain visible, we need to match the slot to a route that returnsnull
to close the modal.
由于活动状态和导航中描述的行为,我们在@auth
插槽中使用一个 catch-all 路由来关闭模态窗口。由于客户端导航到不再匹配插槽的路由仍会保持可见,我们需要将插槽匹配到返回null
以关闭模态窗口的路由。Other examples could include opening a photo modal in a gallery while also having a dedicated
/photo/[id]
page, or opening a shopping cart in a side modal.
其他示例可能包括在画廊中打开照片模态窗口,同时还有一个专门的/photo/[id]
页面,或在侧边模态窗口中打开购物车。View an example of modals with Intercepted and Parallel Routes.
查看带有拦截和并行路由的模态示例。
d.Loading and Error UI 加载和错误 UI
Parallel Routes can be streamed independently, allowing you to define independent error and loading states for each route:
并行路由可以独立流式传输,允许您为每个路由定义独立的错误和加载状态:
See the Loading UI and Error Handling documentation for more information.
有关更多信息,请参阅加载 UI 和错误处理文档。