Next.js 路由全面解析:从 Pages Router 迁移到 App Router

前言

在 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 结构:

arduino 复制代码
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,如下面的代码示例:

javascript 复制代码
// 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 组件。例如:

javascript 复制代码
// app/dashboard/layout.js 定义了导航栏
export default function DashboardLayout({ children }) {
  return (
    <section>
      <nav>Navigation</nav>
      {children}
    </section>
  );
}
javascript 复制代码
// app/dashboard/page.js 定义了页面内容
export default function Page() {
	return <h1>Hello, Dashboard!</h1>;
}

定义模板(Templates)

模板类似于布局,也会传入每个子布局或页面。

但模板在路由切换时会为每个 children 创建一个新实例,不保持状态,

定义模板,创建 template.js 文件,如下例:

javascript 复制代码
// app/template.js
export default function Template({ children }) {
  return <div>{children}</div>;
}

定义加载界面(Loading UI)

Next.js 提供了 loading.js 文件,用于展示加载界面。

这个功能是通过 React 的 Suspense API 实现的。当路由变化时,会立即展示 fallback UI,等数据加载完成后,再展示实际内容。

javascript 复制代码
// app/dashboard/loading.js
export default function DashboardLoading() {
	return <>Loading dashboard...</>;
}

同级的 page.js 代码如下:

javascript 复制代码
// 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 功能实现的。

javascript 复制代码
// 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 函数时,或者当路由地址不匹配时。

javascript 复制代码
// 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>
  );
}
相关推荐
腾讯TNTWeb前端团队3 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰6 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪6 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪7 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy7 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom8 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom8 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom8 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom8 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom8 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试