Skip to content
 

Next.js 平行路由:构建模块化动态布局

更新: 11/26/2025字数: 0 字 时长: 0 分钟

在现代 Web 应用开发中,我们经常需要构建复杂的界面布局,比如仪表盘、社交平台或管理后台。传统的路由方式往往无法满足这种动态、多部分同时更新的需求。这就是 Next.js 平行路由大显身手的地方。

什么是平行路由?

平行路由(Parallel Routes)是 Next.js 的一个高级特性,它允许你在同一个布局中同时渲染多个页面,每个页面都可以独立管理和更新。

核心概念速览

概念说明示例
插槽 (@folder)定义平行路由的特殊文件夹@analytics, @sidebar
布局集成插槽作为 props 传递给布局{ children, analytics, sidebar }
条件渲染基于状态动态显示不同插槽{isAdmin ? admin : user}
独立状态管理每个插槽有自己的加载和错误状态@team/loading.js

快速上手:构建仪表盘布局

让我们通过一个实际的仪表盘案例来探索平行路由的强大功能。

项目结构

app/
├── dashboard/
│   ├── @analytics/
│   │   ├── page.js
│   │   └── loading.js
│   ├── @sidebar/
│   │   ├── page.js
│   │   └── default.js
│   ├── layout.js
│   ├── page.js
│   └── default.js

1. 定义插槽布局

jsx
// app/dashboard/layout.js
export default function DashboardLayout({ children, analytics, sidebar }) {
  return (
    <div className="dashboard-container">
      {/* 主导航区域 */}
      <nav className="main-nav">
        <h1>企业仪表盘</h1>
      </nav>

      <div className="dashboard-content">
        {/* 侧边栏插槽 */}
        <aside className="sidebar">{sidebar}</aside>

        {/* 主内容区域 */}
        <main className="main-content">{children}</main>

        {/* 分析面板插槽 */}
        <section className="analytics-panel">{analytics}</section>
      </div>
    </div>
  );
}

2. 创建各个插槽内容

主内容区域:

jsx
// app/dashboard/page.js
export default function DashboardPage() {
  return (
    <div>
      <h2>欢迎回来!</h2>
      <p>今日概览和重要通知...</p>
    </div>
  );
}

分析面板插槽:

jsx
// app/dashboard/@analytics/page.js
export default function AnalyticsPanel() {
  return (
    <div className="analytics-card">
      <h3>实时数据</h3>
      <div className="metrics">
        <div className="metric">访问量: 1,234</div>
        <div className="metric">转化率: 68%</div>
      </div>
    </div>
  );
}

// 独立的加载状态
export function AnalyticsLoading() {
  return <div className="analytics-card loading">分析数据加载中...</div>;
}

侧边栏插槽:

jsx
// app/dashboard/@sidebar/page.js
export default function Sidebar() {
  return (
    <nav className="sidebar-nav">
      <ul>
        <li>
          <a href="/dashboard">首页</a>
        </li>
        <li>
          <a href="/dashboard/analytics">分析</a>
        </li>
        <li>
          <a href="/dashboard/settings">设置</a>
        </li>
      </ul>
    </nav>
  );
}

高级特性深度探索

1. 条件渲染:基于用户角色的动态布局

jsx
// app/dashboard/layout.js
export default function DashboardLayout({ children, admin, user, analytics }) {
  const userRole = getUserRole(); // 你的权限逻辑

  return (
    <div className="dashboard-container">
      <nav className="main-nav">...</nav>

      <div className="dashboard-content">
        <aside className="sidebar">
          {/* 根据角色显示不同侧边栏 */}
          {userRole === "admin" ? admin : user}
        </aside>

        <main className="main-content">{children}</main>

        <section className="analytics-panel">{analytics}</section>
      </div>
    </div>
  );
}

2. 结合拦截路由实现模态框

平行路由与拦截路由的结合可以创建出色的用户体验:

app/
├── @modal/
│   └── (.)photo/
│       └── [id]/
│           └── page.js
├── photo/
│   └── [id]/
│       └── page.js
└── layout.js
jsx
// app/layout.js
export default function Layout({ children, modal }) {
  return (
    <html>
      <body>
        {children}
        {modal}
      </body>
    </html>
  );
}

// app/@modal/(.)photo/[id]/page.js
export default function PhotoModal({ params }) {
  return (
    <div className="modal-overlay">
      <div className="modal-content">
        <img src={`/photos/${params.id}`} alt="Preview" />
        <a href="/">关闭</a>
      </div>
    </div>
  );
}

3. 错误边界和加载状态

每个插槽都可以有自己的错误处理和加载状态:

jsx
// app/dashboard/@analytics/error.js
'use client';

export default function AnalyticsError({ error, reset }) {
  return (
    <div className="analytics-error">
      <h3>数据分析加载失败</h3>
      <button onClick={reset}>重试</button>
    </div>
  );
}

// app/dashboard/@analytics/loading.js
export default function AnalyticsLoading() {
  return (
    <div className="analytics-loading">
      <div className="loading-spinner"></div>
      <p>数据加载中...</p>
    </div>
  );
}

解决常见问题

1. 硬导航后的默认内容

jsx
// app/dashboard/default.js
export default function DefaultDashboard() {
  return (
    <div>
      <h2>默认仪表盘视图</h2>
      <p>这是硬导航后的回退内容</p>
    </div>
  );
}

// app/dashboard/@sidebar/default.js
export default function DefaultSidebar() {
  return (
    <nav className="sidebar-nav">
      <ul>
        <li><a href="/dashboard">首页</a></li>
        <li><a href="/dashboard/analytics">分析</a></li>
      </ul>
    </nav>
  );
}

2. 路由冲突避免

确保不要同时存在:

  • 具体路由 /chat
  • 可选 catch-all 路由 /chat/[[...id]]

解决方案:

bash
# 选择一种路由模式
app/
├── chat/
   ├── page.js          # /chat
   └── [id]/
       └── page.js      # /chat/123

我见青山多妩媚,料青山见我应如是。