跳到內容

從 Pages 到 App

本指南將協助您

升級

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,其中包含新功能和慣例。新的 Router 在 app 目錄中可用,並與 pages 目錄共存。

升級到 Next.js 13 需要使用 App Router。您可以繼續使用 pages 以及在兩個目錄中皆可運作的新功能,例如更新的 Image 組件Link 組件Script 組件,以及 字體最佳化

<Image/> 組件

Next.js 12 透過臨時匯入:next/future/image,為 Image 組件引入了新的改進。這些改進包括更少的客戶端 JavaScript、更輕鬆地擴展和設定圖片樣式、更好的可訪問性,以及原生瀏覽器延遲載入。

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

有兩個 codemod 可以協助您遷移到新的 Image 組件

  • next-image-to-legacy-image codemod:安全且自動地將 next/image 匯入重新命名為 next/legacy/image。現有組件將維持相同的行為。
  • next-image-experimental codemod:危險地新增內嵌樣式並移除未使用的屬性。這會變更現有組件的行為以符合新的預設值。若要使用此 codemod,您需要先執行 next-image-to-legacy-image codemod。

<Link> 組件 不再需要手動新增 <a> 標籤作為子項。此行為已在 版本 12.2 中作為實驗性選項新增,現在已成為預設值。在 Next.js 13 中,<Link> 始終渲染 <a>,並允許您將屬性轉發到基礎標籤。

例如

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 codemod

<Script> 組件

next/script 的行為已更新為同時支援 pagesapp,但需要進行一些變更以確保順利遷移

  • 將您先前包含在 _document.js 中的任何 beforeInteractive 腳本移至根版面配置檔案 (app/layout.tsx)。
  • 實驗性的 worker 策略尚無法在 app 中運作,以這種策略表示的腳本必須移除或修改為使用不同的策略(例如 lazyOnload)。
  • onLoadonReadyonError 處理器無法在伺服器組件中運作,因此請務必將它們移至 客戶端組件 或完全移除它們。

字體最佳化

先前,Next.js 透過內嵌字體 CSS 協助您最佳化字體。版本 13 引入了新的 next/font 模組,讓您能夠自訂字體載入體驗,同時仍然確保出色的效能和隱私。next/fontpagesapp 目錄中皆受支援。

雖然內嵌 CSS 仍然可以在 pages 中運作,但無法在 app 中運作。您應該改用 next/font

請參閱 字體最佳化 頁面,以了解如何使用 next/font

pages 遷移到 app

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

遷移到 App Router 可能是第一次使用 Next.js 建構在其之上的 React 功能,例如伺服器組件、Suspense 等。當與新的 Next.js 功能(例如 特殊檔案版面配置)結合使用時,遷移意味著需要學習新的概念、心智模型和行為變更。

我們建議將您的遷移分解為較小的步驟,以降低這些更新的組合複雜性。app 目錄經過刻意設計,可與 pages 目錄同時運作,以允許逐頁逐步遷移。

  • app 目錄支援巢狀路由版面配置。深入了解
  • 使用巢狀資料夾定義路由,並使用特殊的 page.js 檔案使路由區段公開訪問。深入了解
  • 特殊檔案慣例 用於為每個路由區段建立 UI。最常見的特殊檔案是 page.jslayout.js
    • 使用 page.js 定義路由獨有的 UI。
    • 使用 layout.js 定義跨多個路由共享的 UI。
    • .js.jsx.tsx 檔案擴展名可用於特殊檔案。
  • 您可以將其他檔案並置在 app 目錄內,例如組件、樣式、測試等。深入了解
  • 資料獲取函式(例如 getServerSidePropsgetStaticProps)已在 app 內部被 新的 API 取代。getStaticPaths 已被 generateStaticParams 取代。
  • pages/_app.jspages/_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.tsxpages/_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 建議將屬性新增至 Page 組件,以在 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 屬性,並遵循 遷移 Pages 的步驟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 元素,例如 titlemeta。在 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 '...'
}

查看所有 metadata 選項.

步驟 4:遷移 Pages

  • app 目錄 中的 Pages 預設為 伺服器組件。這與 pages 目錄不同,在 pages 目錄中,頁面是 客戶端組件
  • 資料fetching(資料擷取)app 中已變更。getServerSidePropsgetStaticPropsgetInitialProps 已被更簡化的 API 取代。
  • app 目錄使用巢狀資料夾來定義路由,並使用特殊的 page.js 檔案使路由區段可公開存取。
  • pages 目錄app 目錄路由
    index.jspage.js/
    about.jsabout/page.js/about
    blog/[slug].jsblog/[slug]/page.js/blog/post-1

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

  • 步驟 1:將預設匯出的 Page Component(頁面元件)移至新的 Client Component(客戶端元件)中。
  • 步驟 2:將新的 Client Component(客戶端元件)匯入到 app 目錄內新的 page.js 檔案中。

小知識:這是最簡單的遷移路徑,因為它與 pages 目錄的行為最為相似。

步驟 1:建立新的 Client Component(客戶端元件)

  • app 目錄內建立新的獨立檔案(例如 app/home-page.tsx 或類似檔案),匯出 Client Component(客戶端元件)。若要定義 Client Component(客戶端元件),請在檔案頂端(任何 import 之前)新增 'use client' 指令。
    • 與 Pages Router(頁面路由器)類似,有一個 最佳化步驟,可以在初始頁面載入時將 Client Component(客戶端元件)預先渲染為靜態 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 檔案。這預設為 Server Component(伺服器元件)。

  • home-page.tsx Client Component(客戶端元件)匯入到此頁面。

  • 如果您先前在 pages/index.js 中執行資料fetching(資料擷取),請使用新的 資料fetching API,將資料fetching(資料擷取)邏輯直接移至 Server Component(伺服器元件)中。請參閱 資料fetching(資料擷取)升級指南 以取得更多詳細資訊。

    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,您需要更新為新的路由 hooks(鉤子)。深入瞭解

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

步驟 5:遷移路由 Hooks(鉤子)

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

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

  • 新的 useRouter hook(鉤子)是從 next/navigation 匯入的,並且與從 next/router 匯入的 pages 中的 useRouter hook(鉤子)行為不同。
  • 新的 useRouter 不會傳回 pathname 字串。請改用獨立的 usePathname hook(鉤子)。
  • 新的 useRouter 不會傳回 query 物件。搜尋參數和動態路由參數現在是分開的。請改用 useSearchParamsuseParams hooks(鉤子)。
  • 您可以一起使用 useSearchParamsusePathname 來監聽頁面變更。請參閱 Router Events(路由器事件) 章節以取得更多詳細資訊。
  • 這些新的 hooks(鉤子)僅在 Client Component(客戶端元件)中受支援。它們不能在 Server Component(伺服器元件)中使用。
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 hook(鉤子)具有以下變更

  • isFallback 已被移除,因為 fallback被取代
  • localelocalesdefaultLocalesdomainLocales 值已被移除,因為內建的 i18n Next.js 功能在 app 目錄中不再必要。深入瞭解 i18n
  • basePath 已被移除。替代方案將不會是 useRouter 的一部分。它尚未實作。
  • asPath 已被移除,因為 as 的概念已從新的路由器中移除。
  • isReady 已被移除,因為它不再必要。在 靜態渲染 期間,任何使用 useSearchParams() hook(鉤子)的元件都會跳過預先渲染步驟,而改為在執行階段於客戶端渲染。
  • route 已被移除。usePathnameuseSelectedLayoutSegments() 提供了替代方案。

檢視 useRouter() API 參考.

pagesapp 之間共用元件

為了保持元件在 pagesapp 路由器之間的相容性,請參考 來自 next/compat/routeruseRouter hook(鉤子)。這是來自 pages 目錄的 useRouter hook(鉤子),但旨在用於在路由器之間共用元件時使用。一旦您準備好僅在 app 路由器上使用它,請更新為來自 next/navigation 的新 useRouter

步驟 6:遷移資料 Fetching(資料擷取)方法

pages 目錄使用 getServerSidePropsgetStaticProps 來為頁面fetching(擷取)資料。在 app 目錄中,這些先前的資料fetching(資料擷取)函式已被建立在 fetch()async React Server Components(非同步 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 用於在伺服器上fetching(擷取)資料,並將 props 轉發到檔案中預設匯出的 React 元件。頁面的初始 HTML 從伺服器預先渲染,然後在瀏覽器中「hydration(注水)」頁面(使其具有互動性)。

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(應用程式路由器)中,我們可以使用 Server Components(伺服器元件),將我們的資料fetching(資料擷取)共置於 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>
  )
}

存取 Request Object(請求物件)

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

例如,您可以從 getServerSideProps 檢索 req 物件,並使用它來檢索請求的 cookies 和 headers。

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 或直接從資料庫fetching(擷取)資料,並在建置期間將此資料向下傳遞到整個頁面。

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() 進行資料fetching(資料擷取)預設為 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,用於傳回路由參數,並且可以在 layouts(版面配置) 內部使用。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 目錄中的新模型,使用名稱 generateStaticParamsgetStaticPaths 更合適。get 前綴被更具描述性的 generate 取代,現在 getStaticPropsgetServerSideProps 不再必要,因此 generate 單獨存在更合適。Paths 後綴被 Params 取代,對於具有多個動態區段的巢狀路由來說,Params 更合適。


取代 fallback

pages 目錄中,從 getStaticPaths 傳回的 fallback 屬性用於定義在建置時未預先渲染的頁面的行為。此屬性可以設定為 true 以在頁面產生時顯示 fallback(回退)頁面,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 目錄中 getStaticPathsfallback: 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 with 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() 進行資料fetching(資料擷取)可以使用 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 目錄中被 Route Handlers(路由處理器) 取代。

Route Handlers(路由處理器)允許您使用 Web RequestResponse API 為給定路由建立自訂請求處理器。

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

小知識:如果您先前使用 API 路由從客戶端呼叫外部 API,您現在可以使用 Server Components(伺服器元件) 來安全地fetching(擷取)資料。深入瞭解 資料fetching(資料擷取)

單頁應用程式

如果您也同時從單頁應用程式 (SPA) 遷移到 Next.js,請參閱我們的 文件 以深入瞭解。

步驟 7:樣式設定

pages 目錄中,全域樣式表僅限於 pages/_app.js。使用 app 目錄,此限制已解除。全域樣式可以新增至任何 layout(版面配置)、頁面或元件。

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 的自動連結預先fetching(預取)將不會跨路由器預先fetching(預取)。

相反地,您可以最佳化 App Router(應用程式路由器)和 Pages Router(頁面路由器)之間的導航,以保留預先fetching(預取)和快速頁面轉換。深入瞭解

Codemods

Next.js 提供 Codemod 轉換,以協助您在功能被棄用時升級程式碼庫。請參閱 Codemods 以取得更多資訊。