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:以資料夾與特定檔名進行結構化

透過在 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 Hook 可在頁面元件及其子元件中自由使用。

另一方面,App Router 中 app/ 內的元件,除非明確指定,否則預設視為伺服器元件。因此 useState 與 useEffect 等 React Hook 無法使用。客戶端的動作與 onClick 等 事件處理函式也無法使用。

因此,若要在 App Router 中改為 CSR,請撰寫 use client。

use client

這是 Server Component 與 Client Component 的邊界宣告。只要宣告了 use client,不僅該元件本身,所有被它匯入的模組也都會被視為 Client Component。

動態路由 [○○]

對於部落格或新聞文章的詳細頁面這類,URL 的一部分會因使用者或內容而改變的「動態 URL」,Next.js 提供的處理機制就是動態路由。

例如,假設有連結分別導向文章 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,並依照專案或作品的需求,靈活運用各自的 Router!

關於撰寫本文的人

主要以標記(markup)為核心,並使用 JavaScript、React 與 Next.js 進行前端開發。若我參與的網站能順利上線,我會非常開心!興趣是彈吉他。喜歡貓咪和烤地瓜🐱🍠

Hirattch

前端工程師 / 2022 年入職

查看此員工的文章

我們的優勢在於穩健的團隊體制與快速的應對能力

Liberogic 由經驗豐富的資深人員積極推動專案,因此獲得客戶的高度評價。
我們會妥善指派專案經理與總監,致力於順利推進整體專案。 我們採取依據專案需求適才適所分配資源的方式,避免因全程投入而增加不必要的成本,並且在掌握業務內容、製作與提交估價方面的速度也廣受好評。

注: 本公司不積極承接類似 SES 的駐點業務等,敬請事先諒解。

可搭配 Slack、Teams、Redmine、Backlog、Asana、Jira、Notion、Google Workspace、Zoom、Webex 等幾乎所有主要的專案管理工具與聊天工具使用。

在利用 SES 或離岸外包的大型專案中,是否對技術挑戰與推進方式感到困擾?

案例分析