平行路由(Parallel Routes)是 Next.js 13+ 中引入的高级路由模式,允许同时渲染多个页面或布局,非常适合构建复杂的界面布局,如仪表盘、多视图应用等。
基本概念
平行路由通过命名"插槽"(slots)实现,这些插槽在文件系统中使用 @
前缀定义。Next.js 会将这些插槽与同一路由段的其他内容并行渲染。
核心特点:
- 独立加载:每个平行路由可以有自己的加载状态
- 独立错误处理:一个路由的错误不会影响其他路由
- 条件渲染:可以基于条件动态渲染某些路由
基础示例
文件结构
app/
layout.js
page.js
@sidebar/
page.js
@main/
page.js
代码实现
// app/layout.js
export default function Layout({ children, sidebar, main }) {
return (
<div className="app-container">
<aside className="sidebar">{sidebar}</aside>
<section className="main-content">{main}</section>
<div className="additional-content">{children}</div>
</div>
)
}
// app/@sidebar/page.js
export default function Sidebar() {
return (
<nav>
<ul>
<li>Menu Item 1</li>
<li>Menu Item 2</li>
<li>Menu Item 3</li>
</ul>
</nav>
)
}
// app/@main/page.js
export default function MainContent() {
return (
<article>
<h1>Main Content Area</h1>
<p>This is the primary content section.</p>
</article>
)
}
应用场景:管理后台布局,侧边栏导航和主内容区独立加载和更新。
高级用法
1. 条件渲染平行路由
// app/layout.js
export default function Layout({ children, sidebar, analytics }) {
const showAnalytics = true // 可以从cookie或状态获取
return (
<div>
{sidebar}
<main>{children}</main>
{showAnalytics && analytics}
</div>
)
}
应用场景:根据用户权限或偏好决定是否显示分析面板。
2. 带加载状态的平行路由
// app/@dashboard/loading.js
export default function DashboardLoading() {
return <div>Loading dashboard...</div>
}
// app/layout.js
export default function Layout({ dashboard }) {
return (
<div>
<Suspense fallback={<DashboardLoading />}>
{dashboard}
</Suspense>
</div>
)
}
应用场景:仪表盘复杂组件需要单独加载时的优雅降级。
3. 独立错误处理
// app/@notifications/error.js
export default function NotificationsError({ error }) {
return (
<div className="notification-error">
Failed to load notifications: {error.message}
</div>
)
}
// app/layout.js
export default function Layout({ notifications }) {
return (
<div>
<ErrorBoundary fallback={<NotificationsError />}>
{notifications}
</ErrorBoundary>
</div>
)
}
应用场景:通知组件失败不应影响整个页面渲染。
实际应用场景
场景1:社交网络主页
app/
home/
layout.js
page.js
@sidebar/
page.js
@feed/
page.js
@trending/
page.js
// app/home/layout.js
export default function HomeLayout({ sidebar, feed, trending }) {
return (
<div className="home-grid">
<aside className="left-sidebar">{sidebar}</aside>
<main className="content-feed">{feed}</main>
<aside className="right-trending">{trending}</aside>
</div>
)
}
优势:三个区域独立加载,互不影响,提升用户体验。
场景2:电商产品详情页
app/
products/
[id]/
layout.js
page.js
@images/
page.js
@details/
page.js
@recommendations/
page.js
// app/products/[id]/layout.js
export default function ProductLayout({ images, details, recommendations }) {
return (
<div className="product-page">
<div className="gallery">{images}</div>
<div className="info">{details}</div>
<div className="suggestions">
<h3>You may also like</h3>
{recommendations}
</div>
</div>
)
}
优势:产品图片、详情和推荐可以并行加载,推荐模块出错不会影响主内容显示。
最佳实践
-
命名约定:
- 使用
@
前缀命名插槽文件夹 - 保持插槽名称语义化 (
@sidebar
,@header
等)
- 使用
-
性能优化:
// 为关键内容优先加载 <Suspense fallback={<Spinner />}> {mainContent} </Suspense> // 非关键内容延迟加载 <Suspense fallback={null}> {secondaryContent} </Suspense>
-
类型安全 (TypeScript):
interface LayoutProps { children: React.ReactNode modal: React.ReactNode analytics: React.ReactNode } export default function Layout({ children, modal, analytics }: LayoutProps) { // ... }
-
组合使用拦截路由:
// 结合平行路由和拦截路由实现复杂模态 export default function PhotoModal({ children, photo }) { return ( <> {children} <dialog open>{photo}</dialog> </> ) }
平行路由为构建复杂界面提供了强大的灵活性,特别适合需要多区域独立控制的大型应用。合理使用可以显著提升应用的模块化和用户体验。