Topics

整理 Next.js Pages Router 和 App Router 容易混淆的差異

  • column

作為獲得眾多支持的 Web 開發框架,Next.js 在版本 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。useStateuseEffect 等 React 的 Hook 可以在頁面元件內或其子元件內自由使用。

另一方面,在 App Router 的 app/ 目錄內的元件在 未明確指定的情況下,預設會被視為伺服器元件。因此 useStateuseEffectReact 的 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>
  );
}

使用 paramsparams.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>
  );
}

使用 useRouterrouter.query.slug 中會包含 URL 的 [slug] 部分的值。

API 資料取得方法

正是在 Liberogic 專欄中也大活躍的 microCMS,與 Next.js 等框架的相容性相當不錯。

App Router 的情況

可以直接使用 fetchasync 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,並根據不同的專案和製作需求靈活運用各自的路由器。

本文作者

我主要從事標記語言、JavaScript、React 和 Next.js 的前端開發。看到自己參與的網站順利上線時最開心!興趣是彈吉他。喜歡貓咪和烤地瓜🐱🍠

Hiracchi

前端工程師 / 2022年入職

查看此員工的文章

信心十足的團隊體制與迅速的應對能力是我們的優勢

Liberogic 擁有經驗豐富的人員積極推進專案,因而獲得客戶的高度評價。
我們恰當地安排專案經理和總監,致力於順利推進整個專案。 我們避免不必要的全面投入而導致成本增加,而是採用適材適所配置資源的方式,因此在業務把握到估價制作與提交的速度上也備受好評。

請注意,我們不積極進行 SES 形式的駐場業務。

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

在利用 SES 或離岸開發的大型專案中,您是否對技術挑戰或解決方法感到困惑呢?

案例分析