前言
在 Next.js 应用中,路由扮演着至关重要的角色。它决定了页面的渲染方式以及请求的处理逻辑。
Next.js 提供了两套路由解决方案:Pages Router 与 App Router。自 v13.4 版本起,App Router 成为默认的路由系统,为新的 Next.js 项目带来更多优势。
文件系统路由
Next.js 的路由系统基于文件系统,使得每个文件能够直接对应一个路由地址:
- pages/index.js 对应根路由 /
- pages/about.js 对应路由 /about
从 Pages Router 迁移到 App Router
自 Next.js v13 起,App Router 成为新的默认路由模式。与 Pages Router 相比,App Router 提供了更强大的功能、更好的性能和更灵活的代码组织方式。
- Pages Router 结构:
pages/
├── index.js
├── about.js
└── more.js
- App Router 结构:
src/
└── app/
├── page.js
├── layout.js
├── template.js
├── loading.js
├── error.js
└── not-found.js
├── about/
│ └── page.js
└── more/
└── page.js
App Router 通过引入诸如 layout.js、template.js 等特殊文件,实现了更细致的页面结构划分,从而改善了代码的组织和管理。
尽管推荐使用 App Router,但 Next.js 仍然兼容 Pages Router。如果你想继续使用 Pages Router,可以在项目的 src 目录或根目录创建 pages 文件夹。
需要注意的是:
- App Router 与 Pages Router 可以共存,但 App Router 优先级更高。
- 如果两者解析到同一个 URL,可能会导致构建错误。
使用 App Router
App Router 是 Next.js 基于文件系统的路由解决方案,它通过目录和文件的方式定义路由、页面、布局、模板等,使得项目结构清晰,便于管理。
定义路由(Routes)
App Router 通过文件夹来定义路由。每个文件夹代表一个 URL 片段,创建嵌套文件夹即可创建嵌套路由。
例如,app/dashboard/settings 目录对应的路由地址是 /dashboard/settings。
定义页面(Pages)
为了让路由可访问,你需要创建名为 page.js 的文件。这是一种约定,例如:
- app/page.js => 路由 /
- app/dashboard/page.js => 路由 /dashboard
- app/dashboard/settings/page.js => 路由 /dashboard/settings
页面通常用于展示 UI,如下面的代码示例:
// app/page.js
export default function Page() {
return <h1>Hello, Next.js!</h1>;
}
访问 http://localhost:3000/ 会显示 “Hello, Next.js!”(把项目原来的 page.jsx 和它的样式删除)。
定义布局(Layouts)
布局是多个页面共享的 UI 结构,如侧边导航栏。
布局文件通常命名为 layout.js,默认导出一个接收 children prop 的 React 组件。例如:
// app/dashboard/layout.js 定义了导航栏
export default function DashboardLayout({ children }) {
return (
<section>
<nav>Navigation</nav>
{children}
</section>
);
}
// app/dashboard/page.js 定义了页面内容
export default function Page() {
return <h1>Hello, Dashboard!</h1>;
}
定义模板(Templates)
模板类似于布局,也会传入每个子布局或页面。
但模板在路由切换时会为每个 children 创建一个新实例,不保持状态,
定义模板,创建 template.js 文件,如下例:
// app/template.js
export default function Template({ children }) {
return <div>{children}</div>;
}
定义加载界面(Loading UI)
Next.js 提供了 loading.js 文件,用于展示加载界面。
这个功能是通过 React 的 Suspense API 实现的。当路由变化时,会立即展示 fallback UI,等数据加载完成后,再展示实际内容。
// app/dashboard/loading.js
export default function DashboardLoading() {
return <>Loading dashboard...</>;
}
同级的 page.js 代码如下:
// app/dashboard/page.js
async function getData() {
await new Promise(resolve => setTimeout(resolve, 2000));
return {
message: 'Hello, Dashboard!',
};
}
export default async function DashboardPage(props) {
const { message } = await getData();
return <h1>{message}</h1>;
}
请求数据先 loading:
请求完成后:
定义错误处理(Error Handling)
Next.js 提供了 error.js 文件,用于创建发生错误时的展示 UI。
这个功能是通过 React 的 Error Boundary 功能实现的。
// app/dashboard/error.js
export default function Error({ error, reset }) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</div>
);
}
定义 404 页面
not-found.js 用于定义资源不存在时显示的页面。
在 app 目录下新建一个 not-found.js,即可自定义 404 页面效果。
这个文件只能由两种情况触发:当组件抛出了 notFound 函数时,或者当路由地址不匹配时。
// app/not-found.js
import Link from 'next/link';
export default function NotFound() {
return (
<div>
<h2>Not Found</h2>
<p>Could not find requested resource</p>
<Link href="/">Return Home</Link>
</div>
);
}