跳到內容
指南遷移App Router

如何從 Pages 遷移到 App Router

本指南將幫助您

升級

Node.js 版本

最低 Node.js 版本現在是 **v18.17**。有關更多資訊,請參閱 Node.js 文件

Next.js 版本

要更新到 Next.js 13 版本,請使用您首選的包管理器執行以下命令

終端
npm install next@latest react@latest react-dom@latest

ESLint 版本

如果您正在使用 ESLint,則需要升級您的 ESLint 版本

終端
npm install -D eslint-config-next@latest

**須知**:您可能需要在 VS Code 中重啟 ESLint 伺服器,以使 ESLint 更改生效。開啟命令面板(Mac 上為 `cmd+shift+p`;Windows 上為 `ctrl+shift+p`),然後搜尋 `ESLint: Restart ESLint Server`。

後續步驟

更新後,請參閱以下部分以瞭解下一步

升級新功能

Next.js 13 引入了新的 App Router,具有新功能和約定。新路由器在 `app` 目錄中可用,並與 `pages` 目錄並存。

升級到 Next.js 13 **不**需要使用 App Router。您可以繼續使用 `pages`,並使用在兩個目錄中都有效的新功能,例如更新的 Image 元件Link 元件Script 元件字型最佳化

<Image/> 元件

Next.js 12 引入了 Image 元件的新改進,並使用了臨時匯入:`next/future/image`。這些改進包括更少的客戶端 JavaScript、更簡單的影像擴充套件和樣式化方式、更好的可訪問性以及原生瀏覽器惰性載入。

在版本 13 中,這種新行為現在是 `next/image` 的預設行為。

有兩個 codemod 可以幫助您遷移到新的 Image 元件

  • **`next-image-to-legacy-image` 碼轉換**:安全自動地將 `next/image` 匯入重新命名為 `next/legacy/image`。現有元件將保持相同的行為。
  • **`next-image-experimental` 碼轉換**:危險地新增內聯樣式並刪除未使用的 prop。這將改變現有元件的行為以匹配新的預設值。要使用此碼轉換,您需要首先執行 `next-image-to-legacy-image` 碼轉換。

`<Link>` 元件不再需要手動新增 `<a>` 標籤作為子項。此行為在 版本 12.2 中作為實驗性選項新增,現在是預設行為。在 Next.js 13 中,`<Link>` 始終渲染 `<a>` 並允許您將 prop 轉發到底層標籤。

例如

import Link from 'next/link'
 
// Next.js 12: `<a>` has to be nested otherwise it's excluded
<Link href="/about">
  <a>About</a>
</Link>
 
// Next.js 13: `<Link>` always renders `<a>` under the hood
<Link href="/about">
  About
</Link>

要將您的連結升級到 Next.js 13,您可以使用 `new-link` 碼轉換

<Script> 元件

`next/script` 的行為已更新以支援 `pages` 和 `app`,但需要進行一些更改以確保平穩遷移

  • 將您之前在 `_document.js` 中包含的任何 `beforeInteractive` 指令碼移動到根佈局檔案 (`app/layout.tsx`)。
  • 實驗性 `worker` 策略在 `app` 中尚不起作用,使用此策略表示的指令碼將不得不被刪除或修改以使用不同的策略(例如 `lazyOnload`)。
  • `onLoad`、`onReady` 和 `onError` 處理程式在 Server Components 中不起作用,因此請確保將它們移動到 Client Component 或完全刪除它們。

字型最佳化

以前,Next.js 透過內聯字型 CSS 幫助您最佳化字型。版本 13 引入了新的`next/font` 模組,使您能夠自定義字型載入體驗,同時仍能確保出色的效能和隱私。`next/font` 在 `pages` 和 `app` 目錄中都受支援。

雖然內聯 CSS 在 `pages` 中仍然有效,但在 `app` 中無效。您應該改用`next/font`

請參閱 字型最佳化 頁面,瞭解如何使用 `next/font`。

從 `pages` 遷移到 `app`

**🎥 觀看:** 瞭解如何逐步採用 App Router → YouTube (16 分鐘)

遷移到 App Router 可能是您首次使用 Next.js 基於的 React 功能,例如 Server Components、Suspense 等。當與 Next.js 的新功能(例如特殊檔案佈局)結合使用時,遷移意味著要學習新的概念、心智模型和行為更改。

我們建議將您的遷移分解為更小的步驟,以降低這些更新的綜合複雜性。`app` 目錄旨在與 `pages` 目錄同時工作,以實現逐頁增量遷移。

  • `app` 目錄支援巢狀路由**和**佈局。瞭解更多
  • 使用巢狀資料夾定義路由,並使用特殊的 `page.js` 檔案使路由段可公開訪問。瞭解更多
  • 特殊檔案約定用於為每個路由段建立 UI。最常見的特殊檔案是 `page.js` 和 `layout.js`。
    • 使用 `page.js` 定義路由獨有的 UI。
    • 使用 `layout.js` 定義跨多個路由共享的 UI。
    • `.js`、`.jsx` 或 `.tsx` 副檔名可用於特殊檔案。
  • 您可以將其他檔案(例如元件、樣式、測試等)並置在 `app` 目錄中。瞭解更多
  • 資料獲取函式(如 `getServerSideProps` 和 `getStaticProps`)已替換為 `app` 中的新 API。`getStaticPaths` 已替換為`generateStaticParams`
  • `pages/_app.js` 和 `pages/_document.js` 已替換為單個 `app/layout.js` 根佈局。瞭解更多
  • `pages/_error.js` 已替換為更細粒度的 `error.js` 特殊檔案。瞭解更多
  • `pages/404.js` 已替換為 `not-found.js` 檔案。
  • `pages/api/*` API 路由已替換為 `route.js`(路由處理程式)特殊檔案。

步驟 1:建立 `app` 目錄

更新到最新的 Next.js 版本(需要 13.4 或更高版本)

npm install next@latest

然後,在您的專案根目錄(或 `src/` 目錄)下建立一個新的 `app` 目錄。

步驟 2:建立根佈局

在 `app` 目錄中建立一個新的 `app/layout.tsx` 檔案。這是一個將應用於 `app` 中所有路由的根佈局

app/layout.tsx
export default function RootLayout({
  // Layouts must accept a children prop.
  // This will be populated with nested layouts or pages
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
  • `app` 目錄**必須**包含一個根佈局。
  • 根佈局必須定義 `<html>` 和 `<body>` 標籤,因為 Next.js 不會自動建立它們
  • 根佈局替換了 `pages/_app.tsx` 和 `pages/_document.tsx` 檔案。
  • 佈局檔案可以使用 `.js`、`.jsx` 或 `.tsx` 副檔名。

要管理 `<head>` HTML 元素,您可以使用 內建 SEO 支援

app/layout.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'Home',
  description: 'Welcome to Next.js',
}

遷移 `_document.js` 和 `_app.js`

如果您有現有的 `_app` 或 `_document` 檔案,您可以將其內容(例如全域性樣式)複製到根佈局 (`app/layout.tsx`)。`app/layout.tsx` 中的樣式**不會**應用於 `pages/*`。您應該在遷移期間保留 `_app`/`_document` 以防止您的 `pages/*` 路由中斷。一旦完全遷移,您就可以安全地刪除它們。

如果您正在使用任何 React Context 提供程式,它們將需要移動到 客戶端元件

將 `getLayout()` 模式遷移到佈局(可選)

Next.js 建議向頁面元件新增一個屬性,以在 `pages` 目錄中實現每頁佈局。此模式可以使用 `app` 目錄中對巢狀佈局的原生支援來替換。

檢視前後示例

之前

components/DashboardLayout.js
export default function DashboardLayout({ children }) {
  return (
    <div>
      <h2>My Dashboard</h2>
      {children}
    </div>
  )
}
pages/dashboard/index.js
import DashboardLayout from '../components/DashboardLayout'
 
export default function Page() {
  return <p>My Page</p>
}
 
Page.getLayout = function getLayout(page) {
  return <DashboardLayout>{page}</DashboardLayout>
}

之後

  • 從 `pages/dashboard/index.js` 中刪除 `Page.getLayout` 屬性,並按照遷移頁面的步驟將其遷移到 `app` 目錄。

    app/dashboard/page.js
    export default function Page() {
      return <p>My Page</p>
    }
  • 將 `DashboardLayout` 的內容移動到一個新的客戶端元件中,以保留 `pages` 目錄的行為。

    app/dashboard/DashboardLayout.js
    'use client' // this directive should be at top of the file, before any imports.
     
    // This is a Client Component
    export default function DashboardLayout({ children }) {
      return (
        <div>
          <h2>My Dashboard</h2>
          {children}
        </div>
      )
    }
  • 將 `DashboardLayout` 匯入到 `app` 目錄中的新 `layout.js` 檔案中。

    app/dashboard/layout.js
    import DashboardLayout from './DashboardLayout'
     
    // This is a Server Component
    export default function Layout({ children }) {
      return <DashboardLayout>{children}</DashboardLayout>
    }
  • 您可以將 `DashboardLayout.js`(客戶端元件)的非互動部分逐步移動到 `layout.js`(伺服器元件)中,以減少傳送給客戶端的元件 JavaScript 數量。

步驟 3:遷移 `next/head`

在 `pages` 目錄中,`next/head` React 元件用於管理 `<head>` HTML 元素,例如 `title` 和 `meta`。在 `app` 目錄中,`next/head` 被新的內建 SEO 支援替換。

之前

pages/index.tsx
import Head from 'next/head'
 
export default function Page() {
  return (
    <>
      <Head>
        <title>My page title</title>
      </Head>
    </>
  )
}

之後

app/page.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'My Page Title',
}
 
export default function Page() {
  return '...'
}

檢視所有元資料選項.

步驟 4:遷移頁面

  • `app` 目錄中的頁面預設是伺服器元件。這與 `pages` 目錄不同,`pages` 目錄中的頁面是客戶端元件
  • `app` 中的資料獲取已更改。`getServerSideProps`、`getStaticProps` 和 `getInitialProps` 已被更簡單的 API 取代。
  • `app` 目錄使用巢狀資料夾定義路由,並使用特殊的 `page.js` 檔案使路由段可公開訪問。
  • `pages` 目錄`app` 目錄路由
    index.jspage.js/
    about.jsabout/page.js/about
    blog/[slug].jsblog/[slug]/page.js/blog/post-1

我們建議將頁面遷移分解為兩個主要步驟

  • 步驟 1:將預設匯出的頁面元件移動到新的客戶端元件中。
  • 步驟 2:將新的客戶端元件匯入到 `app` 目錄中的新 `page.js` 檔案中。

**須知**:這是最簡單的遷移路徑,因為它與 `pages` 目錄具有最相似的行為。

步驟 1:建立一個新的客戶端元件

  • 在 `app` 目錄中建立一個新的單獨檔案(即 `app/home-page.tsx` 或類似檔案),該檔案匯出一個客戶端元件。要定義客戶端元件,請在檔案頂部(在任何匯入之前)新增 `'use client'` 指令。
    • 與 Pages Router 類似,存在一個最佳化步驟,可在初始頁面載入時將客戶端元件預渲染為靜態 HTML。
  • 將預設匯出的頁面元件從 `pages/index.js` 移動到 `app/home-page.tsx`。
app/home-page.tsx
'use client'
 
// This is a Client Component (same as components in the `pages` directory)
// It receives data as props, has access to state and effects, and is
// prerendered on the server during the initial page load.
export default function HomePage({ recentPosts }) {
  return (
    <div>
      {recentPosts.map((post) => (
        <div key={post.id}>{post.title}</div>
      ))}
    </div>
  )
}

步驟 2:建立新頁面

  • 在 `app` 目錄中建立一個新的 `app/page.tsx` 檔案。預設情況下,這是一個伺服器元件。

  • 將 `home-page.tsx` 客戶端元件匯入到頁面中。

  • 如果您之前在 `pages/index.js` 中獲取資料,請使用新的 資料獲取 API 將資料獲取邏輯直接移動到伺服器元件中。有關更多詳細資訊,請參閱資料獲取升級指南

    app/page.tsx
    // Import your Client Component
    import HomePage from './home-page'
     
    async function getPosts() {
      const res = await fetch('https://...')
      const posts = await res.json()
      return posts
    }
     
    export default async function Page() {
      // Fetch data directly in a Server Component
      const recentPosts = await getPosts()
      // Forward fetched data to your Client Component
      return <HomePage recentPosts={recentPosts} />
    }
  • 如果您的上一頁使用了 `useRouter`,則需要更新到新的路由鉤子。瞭解更多

  • 啟動您的開發伺服器並訪問 `https://:3000` 。您應該會看到您的現有索引路由,現在透過 `app` 目錄提供服務。

步驟 5:遷移路由鉤子

已新增新路由器以支援 `app` 目錄中的新行為。

在 `app` 中,您應該使用從 `next/navigation` 匯入的三個新鉤子:`useRouter()``usePathname()``useSearchParams()`

  • 新的 `useRouter` 鉤子從 `next/navigation` 匯入,其行為與 `pages` 中從 `next/router` 匯入的 `useRouter` 鉤子不同。
    • 從 `next/router` 匯入的 `useRouter` 鉤子在 `app` 目錄中不受支援,但可以繼續在 `pages` 目錄中使用。
  • 新的 `useRouter` 不返回 `pathname` 字串。請改用單獨的 `usePathname` 鉤子。
  • 新的 `useRouter` 不返回 `query` 物件。搜尋引數和動態路由引數現在是分開的。請改用 `useSearchParams` 和 `useParams` 鉤子。
  • 您可以結合使用 `useSearchParams` 和 `usePathname` 來監聽頁面更改。有關更多詳細資訊,請參閱 Router Events 部分。
  • 這些新的鉤子僅在客戶端元件中受支援。它們不能在伺服器元件中使用。
app/example-client-component.tsx
'use client'
 
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
 
export default function ExampleClientComponent() {
  const router = useRouter()
  const pathname = usePathname()
  const searchParams = useSearchParams()
 
  // ...
}

此外,新的 `useRouter` 鉤子還有以下更改

  • `isFallback` 已刪除,因為 `fallback` 已被替換
  • `locale`、`locales`、`defaultLocales`、`domainLocales` 值已刪除,因為在 `app` 目錄中不再需要內建的 i18n Next.js 功能。瞭解有關 i18n 的更多資訊
  • `basePath` 已刪除。替代方案不會是 `useRouter` 的一部分。它尚未實現。
  • `asPath` 已刪除,因為 `as` 的概念已從新路由器中刪除。
  • `isReady` 已刪除,因為它不再需要。在靜態渲染期間,任何使用 `useSearchParams()` 鉤子的元件都將跳過預渲染步驟,而是在執行時在客戶端渲染。
  • `route` 已刪除。`usePathname` 或 `useSelectedLayoutSegments()` 提供了替代方案。

檢視 `useRouter()` API 參考.

在 `pages` 和 `app` 之間共享元件

為了使元件在 `pages` 和 `app` 路由器之間相容,請參考 `next/compat/router` 中的 `useRouter` 鉤子。這是 `pages` 目錄中的 `useRouter` 鉤子,但旨在在路由器之間共享元件時使用。一旦您準備好僅在 `app` 路由器上使用它,請更新到 `next/navigation` 中的新 `useRouter`

步驟 6:遷移資料獲取方法

`pages` 目錄使用 `getServerSideProps` 和 `getStaticProps` 為頁面獲取資料。在 `app` 目錄中,這些以前的資料獲取函式被基於 `fetch()` 和 `async` React 伺服器元件構建的更簡單的 API 取代。

app/page.tsx
export default async function Page() {
  // This request should be cached until manually invalidated.
  // Similar to `getStaticProps`.
  // `force-cache` is the default and can be omitted.
  const staticData = await fetch(`https://...`, { cache: 'force-cache' })
 
  // This request should be refetched on every request.
  // Similar to `getServerSideProps`.
  const dynamicData = await fetch(`https://...`, { cache: 'no-store' })
 
  // This request should be cached with a lifetime of 10 seconds.
  // Similar to `getStaticProps` with the `revalidate` option.
  const revalidatedData = await fetch(`https://...`, {
    next: { revalidate: 10 },
  })
 
  return <div>...</div>
}

伺服器端渲染 (`getServerSideProps`)

在 `pages` 目錄中,`getServerSideProps` 用於在伺服器上獲取資料並將 prop 轉發到檔案中預設匯出的 React 元件。頁面的初始 HTML 從伺服器預渲染,然後瀏覽器中“水合”頁面(使其具有互動性)。

pages/dashboard.js
// `pages` directory
 
export async function getServerSideProps() {
  const res = await fetch(`https://...`)
  const projects = await res.json()
 
  return { props: { projects } }
}
 
export default function Dashboard({ projects }) {
  return (
    <ul>
      {projects.map((project) => (
        <li key={project.id}>{project.name}</li>
      ))}
    </ul>
  )
}

在 App Router 中,我們可以使用伺服器元件將資料獲取並置在 React 元件中。這允許我們向客戶端傳送更少的 JavaScript,同時保留伺服器渲染的 HTML。

透過將 `cache` 選項設定為 `no-store`,我們可以指示獲取的資料永遠不被快取。這類似於 `pages` 目錄中的 `getServerSideProps`。

app/dashboard/page.tsx
// `app` directory
 
// This function can be named anything
async function getProjects() {
  const res = await fetch(`https://...`, { cache: 'no-store' })
  const projects = await res.json()
 
  return projects
}
 
export default async function Dashboard() {
  const projects = await getProjects()
 
  return (
    <ul>
      {projects.map((project) => (
        <li key={project.id}>{project.name}</li>
      ))}
    </ul>
  )
}

訪問請求物件

在 `pages` 目錄中,您可以根據 Node.js HTTP API 檢索基於請求的資料。

例如,您可以從 `getServerSideProps` 中檢索 `req` 物件,並使用它來檢索請求的 cookie 和標頭。

pages/index.js
// `pages` directory
 
export async function getServerSideProps({ req, query }) {
  const authHeader = req.getHeaders()['authorization'];
  const theme = req.cookies['theme'];
 
  return { props: { ... }}
}
 
export default function Page(props) {
  return ...
}

`app` 目錄公開了新的只讀函式來檢索請求資料

app/page.tsx
// `app` directory
import { cookies, headers } from 'next/headers'
 
async function getData() {
  const authHeader = (await headers()).get('authorization')
 
  return '...'
}
 
export default async function Page() {
  // You can use `cookies` or `headers` inside Server Components
  // directly or in your data fetching function
  const theme = (await cookies()).get('theme')
  const data = await getData()
  return '...'
}

靜態站點生成 (`getStaticProps`)

在 `pages` 目錄中,`getStaticProps` 函式用於在構建時預渲染頁面。此函式可用於從外部 API 或直接從資料庫獲取資料,並將這些資料傳遞給在構建期間生成的整個頁面。

pages/index.js
// `pages` directory
 
export async function getStaticProps() {
  const res = await fetch(`https://...`)
  const projects = await res.json()
 
  return { props: { projects } }
}
 
export default function Index({ projects }) {
  return projects.map((project) => <div>{project.name}</div>)
}

在 `app` 目錄中,使用 `fetch()` 獲取資料將預設為 `cache: 'force-cache'`,這將快取請求資料直到手動失效。這類似於 `pages` 目錄中的 `getStaticProps`。

app/page.js
// `app` directory
 
// This function can be named anything
async function getProjects() {
  const res = await fetch(`https://...`)
  const projects = await res.json()
 
  return projects
}
 
export default async function Index() {
  const projects = await getProjects()
 
  return projects.map((project) => <div>{project.name}</div>)
}

動態路徑 (`getStaticPaths`)

在 `pages` 目錄中,`getStaticPaths` 函式用於定義在構建時應預渲染的動態路徑。

pages/posts/[id].js
// `pages` directory
import PostLayout from '@/components/post-layout'
 
export async function getStaticPaths() {
  return {
    paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
  }
}
 
export async function getStaticProps({ params }) {
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()
 
  return { props: { post } }
}
 
export default function Post({ post }) {
  return <PostLayout post={post} />
}

在 `app` 目錄中,`getStaticPaths` 被 `generateStaticParams` 取代。

`generateStaticParams` 的行為類似於 `getStaticPaths`,但具有簡化的 API 用於返回路由引數,並且可以在佈局中使用。`generateStaticParams` 的返回形式是一個段陣列,而不是巢狀 `param` 物件的陣列或已解析路徑的字串。

app/posts/[id]/page.js
// `app` directory
import PostLayout from '@/components/post-layout'
 
export async function generateStaticParams() {
  return [{ id: '1' }, { id: '2' }]
}
 
async function getPost(params) {
  const res = await fetch(`https://.../posts/${(await params).id}`)
  const post = await res.json()
 
  return post
}
 
export default async function Post({ params }) {
  const post = await getPost(params)
 
  return <PostLayout post={post} />
}

對於 `app` 目錄中的新模型,使用名稱 `generateStaticParams` 比 `getStaticPaths` 更合適。`get` 字首被更具描述性的 `generate` 取代,現在 `getStaticProps` 和 `getServerSideProps` 不再需要,它獨自看起來更好。`Paths` 字尾被 `Params` 取代,這對於具有多個動態段的巢狀路由更合適。


替換 `fallback`

在 `pages` 目錄中,從 `getStaticPaths` 返回的 `fallback` 屬性用於定義未在構建時預渲染的頁面的行為。此屬性可以設定為 `true` 以在頁面生成時顯示回退頁面,設定為 `false` 以顯示 404 頁面,或設定為 `blocking` 以在請求時生成頁面。

pages/posts/[id].js
// `pages` directory
 
export async function getStaticPaths() {
  return {
    paths: [],
    fallback: 'blocking'
  };
}
 
export async function getStaticProps({ params }) {
  ...
}
 
export default function Post({ post }) {
  return ...
}

在 `app` 目錄中,`config.dynamicParams` 屬性控制如何處理`generateStaticParams` 之外的引數

  • **`true`**:(預設)`generateStaticParams` 中未包含的動態段按需生成。
  • **`false`**:`generateStaticParams` 中未包含的動態段將返回 404。

這替換了 `pages` 目錄中 `getStaticPaths` 的 `fallback: true | false | 'blocking'` 選項。`fallback: 'blocking'` 選項未包含在 `dynamicParams` 中,因為 `blocking` 和 `true` 之間的差異在流式傳輸時微不足道。

app/posts/[id]/page.js
// `app` directory
 
export const dynamicParams = true;
 
export async function generateStaticParams() {
  return [...]
}
 
async function getPost(params) {
  ...
}
 
export default async function Post({ params }) {
  const post = await getPost(params);
 
  return ...
}

`dynamicParams` 設定為 `true`(預設值)時,當請求尚未生成的路由段時,它將進行伺服器渲染並快取。

增量靜態再生成 (`getStaticProps` 與 `revalidate`)

在 `pages` 目錄中,`getStaticProps` 函式允許您新增 `revalidate` 欄位,以在一定時間後自動重新生成頁面。

pages/index.js
// `pages` directory
 
export async function getStaticProps() {
  const res = await fetch(`https://.../posts`)
  const posts = await res.json()
 
  return {
    props: { posts },
    revalidate: 60,
  }
}
 
export default function Index({ posts }) {
  return (
    <Layout>
      <PostList posts={posts} />
    </Layout>
  )
}

在 `app` 目錄中,使用 `fetch()` 獲取資料可以使用 `revalidate`,它將快取請求指定的秒數。

app/page.js
// `app` directory
 
async function getPosts() {
  const res = await fetch(`https://.../posts`, { next: { revalidate: 60 } })
  const data = await res.json()
 
  return data.posts
}
 
export default async function PostList() {
  const posts = await getPosts()
 
  return posts.map((post) => <div>{post.name}</div>)
}

API 路由

API 路由在 `pages/api` 目錄中繼續工作,沒有任何更改。但是,它們已被 `app` 目錄中的 路由處理程式 取代。

路由處理程式允許您使用 Web RequestResponse API 為給定路由建立自定義請求處理程式。

app/api/route.ts
export async function GET(request: Request) {}

**須知**:如果您之前使用 API 路由從客戶端呼叫外部 API,您現在可以使用伺服器元件來安全地獲取資料。瞭解有關資料獲取的更多資訊。

單頁應用

如果您同時也在從單頁應用 (SPA) 遷移到 Next.js,請參閱我們的文件以瞭解更多資訊。

步驟 7:樣式

在 `pages` 目錄中,全域性樣式表僅限於 `pages/_app.js`。在 `app` 目錄中,此限制已解除。全域性樣式可以新增到任何佈局、頁面或元件。

Tailwind CSS

如果您正在使用 Tailwind CSS,您需要將 `app` 目錄新增到您的 `tailwind.config.js` 檔案中

tailwind.config.js
module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx,mdx}', // <-- Add this line
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
  ],
}

您還需要在 `app/layout.js` 檔案中匯入您的全域性樣式

app/layout.js
import '../styles/globals.css'
 
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

瞭解有關使用 Tailwind CSS 進行樣式設計的更多資訊

將 App Router 與 Pages Router 一起使用

在不同 Next.js 路由器提供的路由之間導航時,將進行硬導航。使用 `next/link` 的自動連結預取不會跨路由器進行預取。

相反,您可以透過最佳化 App Router 和 Pages Router 之間的導航來保留預取和快速頁面轉換。瞭解更多

碼轉換

Next.js 提供了碼轉換,以幫助您在功能棄用時升級程式碼庫。有關更多資訊,請參閱 碼轉換