并行路由
Parallel Routing(并行路由) 允许你同时或有条件的在同一个 layout(布局) 中展示一个或多个 page(页面). 对于 app 内高度动态的部分, 像 dashboards(数据表), 社交网站上的信息, Parallel Routing(并行路由) 可以实现复杂的路由模式.
比如, 你可以同时展示渲染小组和分析页面.
Parallel Routing(并行路由) 允许你为不同的路由定义各自的 error(错误 UI) 和 loading states(加载状态), 因为它们是被分开传输的.
Parallel Routing(并行路由) 允许你基于一个明确条件的情况下, 渲染一个 slot(插槽), 像权限状态. 这样就可以在同一个 URL 上使用完全不同的的代码.
Convention (文件名协定)
使用 slot(插槽) 来创建 Parallel Routing(并行路由). Slot(插槽) 使用 @folder
作为文件名协定, 它会被作为属性传递给同一层的 layout(布局).
Slots(插槽) 不是路由段的一部分, 它不会影响 URL 结构.文件路径
/@team/members
将会被作为/members
进行访问.
比如, 在下述的文件结构中定义了两个插槽: @analytics
和 @team
.
上层的文件夹结构表示 app/layout.js
内的组件现在将会接收 @analytics
和 @team
插槽属性, 并且可以和 children
属性被一起并行渲染.
export default function Layout(props: {
children: React.ReactNode;
analytics: React.ReactNode;
team: React.ReactNode;
}) {
return (
<>
{props.children}
{props.team}
{props.analytics}
</>
);
}
请注意:
children
属性是隐式的插槽, 不需要在文件夹中被指定. 这表示,app/page.js
与app/@children/page.js
是相同的.
Unmatched Routes (不匹配的路由)
默认情况下, 槽中呈现的内容是与 当前 URL 相匹配的.
如果插槽不匹配, Next.js 会根据路由技术和文件夹结构渲染不同的内容.
default.js
你可以定义一个 default.js
文件作为备用组件来防止当 Next.js 不能通过当前 URL 恢复一个插槽的激活状态.
看下述的文件结构. @team
插槽有一个 settings
目录, 但是 @analytics
没有.
Navigation (导航)
在导航时, Next.js 会渲染插槽之前的活动状态, 即使它与当前 URL 不匹配.
Reload (重新加载)
在重新加载时, 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
.
Examples (示例)
Modals (拟态窗口)
并行路由可以被用来渲染 Modals(拟态窗口).
@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>
);
}
为确保在 Modal(拟态窗口) 未激活时不会渲染其中内容, 你可以创建一个 default.js
文件并返回 null
.
export default function Default() {
return null;
}
Dismissing a Modal (解除拟态窗口)
如果拟态窗口是通过客户端导航(如使用 <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>
);
}
在 Intercepting Routes(拦截路线) 中查看拟态窗口的更多信息.
如果你想要导航到其他地方并且取消拟态窗口, 你可以使用一个 catch-all(捕捉所有的) 路由.
export default function CatchAll() {
return null;
}
捕捉所有的路由优先于
default.js
.
Conditional Routes (视情况而定的路由)
Parallel Routes(并行路由) 可以被用来实现 Conditional Routing(视情况而定的路由). 比如, 你可以依赖权限状态来选择渲染 @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;
}