Topics

梳理容易混淆的 Next.js Pages Router 与 App Router 之间的差异

  • column

Next.js 作为广受欢迎的 Web 开发框架,在第 13 版中引入了全新的 App Router,并将其作为默认路由系统。

在同时参与 Pages Router 与 App Router 项目的过程中,相信不少人也会时常混淆:“这种写法到底是哪一种来着?”我自己就是如此!!

尤其是在基于文件结构的路由定义方式、URL 动态部分的处理方法、渲染机制,以及从 API 获取数据等构成应用开发根基的环节中,Pages Router 与 App Router 存在着不容忽视的差异。

本文聚焦于“容易混淆”的要点,简要整理 Pages Router 与 App Router 的主要差异,作为备忘记录!

文件路由的差异

Pages Router:文件名直接成为路径的简洁设计

放置在 pages 目录下的 .js、.jsx、.ts、.tsx 文件,其文件名会直接作为 URL 路径,这一点十分简洁。

例如:src/page/about.tsx ⇒ 路由为 /about。

App Router:通过文件夹与特定文件名进行结构化

在 App Router 中,只需在 src/app 下放置文件夹或文件,即可实现路由。

但是,用于路由的文件如果命名为特定的文件名 page,则会被视为该路径对应页面的内容。

例如:src/app/about/page.tsx ⇒ 路由为 /about。

App Router 的特色“特殊文件”

此外,在 App Router 中,除了 page.tsx 之外,还有其他具有特定含义的文件。

app/
├── page.tsx           --> ページの中身。これが一番よく使うファイル
├── route.tsx          --> APIの定義(page.jsと共存不可)
├── layout.tsx         --> 共通の見た目
├── loading.tsx        --> 読み込み中の画面
├── error.tsx          --> エラー時の画面
├── global-error.tsx   --> グローバルエラー画面
├── templete.tsx       --> 共通の見た目(リセットされるレイアウト)
├── default.tsx        --> デフォルトの画面
└── not-found.tsx      --> notFound関数がスローされたときの画面

让我特别惊讶的是 not-found.tsx。只要准备好这个页面,访问不存在的 URL 时就会自动显示它。没想到实现起来竟然如此简单,不禁感叹。

渲染方式的差异

在 Pages Router 中,组件基本上以客户端交互为前提,服务端渲染主要用于提升首屏加载速度和 SEO。useState 和 useEffect 等 React Hooks 可以在页面组件及其子组件中自由使用。

另一方面,App Router 中 app/ 目录下的组件默认被视为服务器组件,除非明确指定。因此,useState 和 useEffect 等 React 钩子无法使用。同时,客户端操作(如 onClick)等事件处理程序也无法使用。

因此,在 App Router 中要实现 CSR,需要编写 use client。

use client

这是 Server Component 与 Client Component 的边界声明。声明 use client 后,不仅该组件本身,所有被其导入的模块也都会被视为 Client Component。

动态路由 [○○]

Next.js 的动态路由是一种机制,用于处理URL的一部分会因用户或内容而变化的“动态URL”,例如博客或新闻的文章详情页。

例如,假设有链接跳转到文章 A 和文章 B。

例如,包含跳转至文章A、文章B链接的页面截图
import Link from "next/link";
const linkStyle=`ext-lg font-bold border-b border-primary px-1`

export default function page() {
  return (
    <div>
      <div className="flex gap-6">
        <Link href="/post/A" className={linkStyle}>記事Aへのリンク</Link>
        <Link href="/post/B" className={linkStyle}>記事Bへのリンク</Link>
      </div>
    </div>
  );
}

各链接的跳转目标

post/A 页面截图
post/B 页面截图

使用动态路由时,只需创建[slug]文件,即可让post/A和post/B使用同一模板。
※[ ] 中的命名可自由设定,因此不一定非要使用slug。

App Router 的情况

/app/post/[slug]/page.tsx

export default aysinc function PostPage({ params }: { params: Promise<{ slug: string }>) {
  const {slug}= await params;
  return (
    <div>
      <h1>記事: {slug}</h1>
    </div>
  );
}

使用params。params.slug 中会包含 URL 中的[slug]部分。

※ 过去通过 params 获取参数时无需异步处理,但从 Next.js 15 开始,需改为异步方式。因此,似乎需要将 props 定义为 Promise 类型。

Pages Router 的情况

/pages/post/[slug].tsx


import { useRouter } from 'next/router';

export default function PostPage() {
  const router = useRouter();
  const { slug } = router.query;

  return (
    <div>
      <h1>記事: {slug}</h1>
    </div>
  );
}

使用 useRouter。router.query.slug 将获取 URL 中 [slug] 部分的值。

API 数据获取方法

在 Liberogic 的这篇专栏中大显身手的 microCMS,与 Next.js 等框架的兼容性非常好。

App Router 的情况

简单使用 fetch 和 async await 即可。

但是,声明了 use client 的文件会变为 CSR,因此无法使用 async await!


async function getPost() {
  const res = await fetch(`https://.../api/data/`);

  // エラーハンドリング例
  if (!res.ok) {
     throw new Error(`Failed to fetch post: ${res.status}`);
  }

  return res.json();
}

调用方

export default function Example() {
   const data = await getPost()
   // ... data を使って表示 ...
}

Pages Router 的情况

使用 getStaticProps,并将获取到的数据作为 props 传递给页面组件。


export async function getServerSideProps(context) {
  // context.req, context.res などにアクセス可能
  const res = await fetch(`https://.../api/data/`, {
     headers: { Cookie: context.req.headers.cookie } // 例: Cookieを渡す
  });
  const data = await res.json();

  return {
    props: {
      data,
    },
  };
}

调用方

export default function Example({ data }) {
  // ... data を使って表示 ...
}

理解两种路由器,熟练掌握 Next.js!

就我而言,最初接触 Next.js 的项目用的是 App Router。

之后在另一个项目中接触了Pages Router,当时就陷入了困惑:“新建页面时,文件名到底是 index.tsx 还是 page.tsx 来着??”

这次的内容只是Next.js的一小部分,但通过重新查阅App Router与Pages Router之间的区别等资料,我自己也梳理清楚了不少内容。

就个人而言,我很喜欢App Router的写法,简单易懂,但我也希望能够在Pages Router的项目中从容应对。

理想的状态是能够更深入地理解 Next.js,并根据项目或作品的需要,灵活选择合适的路由方式!

撰写本文的人

主要从事标记(Markup)工作,并使用 JavaScript、React 和 Next.js 进行前端开发。看到自己参与的网站顺利上线,我会非常开心!爱好是弹吉他。喜欢猫和烤红薯🐱🍠

Hirappi

前端工程师 / 2022年入职

查看此员工的文章

以稳健的团队体制和迅速的应对能力为傲

Liberogic 的资深团队积极推动项目,因此深受客户好评。
我们致力于合理配置项目经理与总监,确保整个项目顺利推进。 我们采用按需投入的方式,避免不必要的全员投入导致成本增加,合理分配资源、人尽其才。同时,我们在掌握业务内容、制作与提交报价方面的速度也广受好评。

※ 当社不积极开展 SES 类驻场业务,敬请谅解。

您可以使用 Slack、Teams、Redmine、Backlog、Asana、Jira、Notion、Google Workspace、Zoom、Webex 等几乎所有主流的项目管理工具和聊天工具。

在利用 SES 或离岸外包的大型项目中,您是否正在为技术难题或推进方式而烦恼?

案例分析