App Router 漸進式遷移指南
本指南將協助您:
升級步驟
Node.js 版本
最低 Node.js 版本要求現為 v18.17。詳情請參閱 Node.js 文件。
Next.js 版本
若要更新至 Next.js 版本 13,請使用您偏好的套件管理工具執行以下指令:
npm install next@latest react@latest react-dom@latestESLint 版本
若您使用 ESLint,需升級 ESLint 版本:
npm install -D eslint-config-next@latest小提示:您可能需要重啟 VS Code 中的 ESLint 伺服器才能使變更生效。開啟命令面板(Mac 為
cmd+shift+p;Windows 為ctrl+shift+p)並搜尋ESLint: Restart ESLint Server。
後續步驟
更新完成後,請參閱以下章節進行後續操作:
- 升級新功能:協助您升級至新功能的指南,例如改進的 Image 和 Link 元件。
- 從
pages目錄遷移至app目錄:逐步指南,協助您逐步從pages遷移至app目錄。
升級新功能
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-imagecodemod:安全且自動地將next/image導入重命名為next/legacy/image。現有元件將維持相同行為。next-image-experimentalcodemod:危險地添加行內樣式並移除未使用的屬性。這將變更現有元件的行為以符合新預設值。使用此 codemod 前,需先執行next-image-to-legacy-imagecodemod。
<Link> 元件
<Link> 元件 不再需要手動添加 <a> 標籤作為子元素。此行為在 版本 12.2 中作為實驗性選項加入,現已成為預設值。在 Next.js 13 中,<Link> 總是渲染 <a> 並允許您將屬性傳遞至底層標籤。
例如:
import Link from 'next/link'
// Next.js 12: 必須嵌套 `<a>` 否則會被排除
<Link href="/about">
<a>About</a>
</Link>
// Next.js 13: `<Link>` 總是在底層渲染 `<a>`
<Link href="/about">
About
</Link>若要將您的連結升級至 Next.js 13,可以使用 new-link codemod。
<Script> 元件
next/script 的行為已更新以支援 pages 和 app,但需進行一些變更以確保順利遷移:
- 將先前在
_document.js中包含的任何beforeInteractive腳本移至根佈局檔案 (app/layout.tsx)。 - 實驗性的
worker策略尚不適用於app,標記為此策略的腳本需移除或修改為使用其他策略(例如lazyOnload)。 onLoad、onReady和onError處理器在伺服器元件中無效,請確保將它們移至 客戶端元件 或完全移除。
字體優化
先前,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 功能(如伺服器元件、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 內的所有路由。
export default function RootLayout({
// 佈局必須接受 children 屬性。
// 這將填入嵌套佈局或頁面
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}export default function RootLayout({
// 佈局必須接受 children 屬性。
// 這將填入嵌套佈局或頁面
children,
}) {
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 支援:
import { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Home',
description: 'Welcome to Next.js',
}export const 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 目錄中對 嵌套佈局 的原生支援取代。
查看前後範例
之前
export default function DashboardLayout({ children }) {
return (
<div>
<h2>My Dashboard</h2>
{children}
</div>
)
}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' // 此指令應位於檔案頂部,任何導入之前。 // 這是客戶端元件 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' // 這是伺服器元件 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 支援 取代。
之前:
import Head from 'next/head'
export default function Page() {
return (
<>
<Head>
<title>My page title</title>
</Head>
</>
)
}import Head from 'next/head'
export default function Page() {
return (
<>
<Head>
<title>My page title</title>
</Head>
</>
)
}之後:
import { Metadata } from 'next'
export const metadata: Metadata = {
title: 'My Page Title',
}
export default function Page() {
return '...'
}export const metadata = {
title: 'My Page Title',
}
export default function Page() {
return '...'
}步驟 4:遷移頁面
- 在
app目錄 中的頁面預設為 伺服器元件 (Server Components)。這與pages目錄不同,後者的頁面是 客戶端元件 (Client Components)。 app目錄中的 資料獲取 (Data fetching) 已變更。getServerSideProps、getStaticProps和getInitialProps已被更簡單的 API 取代。app目錄使用巢狀資料夾來 定義路由 (define routes),並使用特殊的page.js檔案使路由區段可公開存取。-
pages目錄app目錄路由 index.jspage.js/about.jsabout/page.js/aboutblog/[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。
'use client'
// 這是一個客戶端元件(與 `pages` 目錄中的元件相同)
// 它接收資料作為 props,可以訪問狀態和效果,並且
// 在初始頁面載入時會在伺服器上預渲染。
export default function HomePage({ recentPosts }) {
return (
<div>
{recentPosts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
)
}'use client'
// 這是一個客戶端元件。它接收資料作為 props,
// 並且可以訪問狀態和效果,就像 `pages` 目錄中的頁面元件一樣。
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 將資料獲取邏輯直接移至伺服器元件中。詳情請參閱 資料獲取升級指南。// 導入您的客戶端元件 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() { // 直接在伺服器元件中獲取資料 const recentPosts = await getPosts() // 將獲取的資料傳遞給您的客戶端元件 return <HomePage recentPosts={recentPosts} /> }// 導入您的客戶端元件 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() { // 直接在伺服器元件中獲取資料 const recentPosts = await getPosts() // 將獲取的資料傳遞給您的客戶端元件 return <HomePage recentPosts={recentPosts} /> } -
如果您的舊頁面使用了
useRouter,則需要更新為新的路由鉤子。了解更多。 -
啟動開發伺服器並訪問
http://localhost:3000。您應該會看到現有的索引路由,現在通過 app 目錄提供。
步驟 5:遷移路由鉤子
新增了一個新的路由器來支援 app 目錄中的新行為。
在 app 中,您應該使用從 next/navigation 導入的三個新鉤子:useRouter()、usePathname() 和 useSearchParams()。
- 新的
useRouter鉤子是從next/navigation導入的,其行為與從next/router導入的pages中的useRouter鉤子不同。- 從
next/router導入的useRouter鉤子 在app目錄中不受支援,但可以繼續在pages目錄中使用。
- 從
- 新的
useRouter不返回pathname字串。請改用獨立的usePathname鉤子。 - 新的
useRouter不返回query物件。請改用獨立的useSearchParams鉤子。 - 您可以一起使用
useSearchParams和usePathname來監聽頁面變更。詳情請參閱 路由器事件 (Router Events) 部分。 - 這些新鉤子僅在客戶端元件中受支援。它們不能在伺服器元件中使用。
'use client'
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
export default function ExampleClientComponent() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
// ...
}'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值已被移除,因為內建的 i18n Next.js 功能在app目錄中不再需要。了解更多關於 i18n 的資訊。basePath已被移除。替代方案不會是useRouter的一部分。目前尚未實現。asPath已被移除,因為新路由器中已移除as的概念。isReady已被移除,因為它不再必要。在 靜態渲染 (static rendering) 期間,任何使用useSearchParams()鉤子的元件將跳過預渲染步驟,改為在運行時在客戶端渲染。
步驟 6:遷移資料獲取方法
pages 目錄使用 getServerSideProps 和 getStaticProps 來獲取頁面的資料。在 app 目錄中,這些舊的資料獲取函數已被基於 fetch() 和 async React 伺服器元件的 更簡單 API 取代。
export default async function Page() {
// 此請求應緩存直到手動失效。
// 類似於 `getStaticProps`。
// `force-cache` 是預設值,可以省略。
const staticData = await fetch(`https://...`, { cache: 'force-cache' })
// 此請求應在每次請求時重新獲取。
// 類似於 `getServerSideProps`。
const dynamicData = await fetch(`https://...`, { cache: 'no-store' })
// 此請求應緩存 10 秒。
// 類似於帶有 `revalidate` 選項的 `getStaticProps`。
const revalidatedData = await fetch(`https://...`, {
next: { revalidate: 10 },
})
return <div>...</div>
}export default async function Page() {
// 此請求應緩存直到手動失效。
// 類似於 `getStaticProps`。
// `force-cache` 是預設值,可以省略。
const staticData = await fetch(`https://...`, { cache: 'force-cache' })
// 此請求應在每次請求時重新獲取。
// 類似於 `getServerSideProps`。
const dynamicData = await fetch(`https://...`, { cache: 'no-store' })
// 此請求應緩存 10 秒。
// 類似於帶有 `revalidate` 選項的 `getStaticProps`。
const revalidatedData = await fetch(`https://...`, {
next: { revalidate: 10 },
})
return <div>...</div>
}伺服器端渲染 (getServerSideProps)
在 pages 目錄中,getServerSideProps 用於在伺服器上獲取資料並將 props 傳遞給檔案中預設導出的 React 元件。頁面的初始 HTML 從伺服器預渲染,然後在瀏覽器中「水合」(hydrating) 頁面(使其可互動)。
// `pages` 目錄
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 目錄中,我們可以使用 伺服器元件 (Server Components) 將資料獲取邏輯與 React 元件放在一起。這使我們可以向客戶端發送更少的 JavaScript,同時保留伺服器渲染的 HTML。
通過將 cache 選項設置為 no-store,我們可以指示獲取的資料應 永不緩存。這與 pages 目錄中的 getServerSideProps 類似。
// `app` 目錄
// 此函數可以命名為任何名稱
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>
)
}// `app` 目錄
// 此函數可以命名為任何名稱
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 物件,並使用它來獲取請求的 cookies 和 headers。
// `pages` 目錄
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 目錄公開了新的唯讀函數來獲取請求資料:
headers():基於 Web Headers API,可以在 伺服器元件 (Server Components) 中用於獲取請求 headers。cookies():基於 Web Cookies API,可以在 伺服器元件 (Server Components) 中用於獲取 cookies。
// `app` 目錄
import { cookies, headers } from 'next/headers'
async function getData() {
const authHeader = headers().get('authorization')
return '...'
}
export default async function Page() {
// 您可以直接在伺服器元件中使用 `cookies()` 或 `headers()`
// 或在您的資料獲取函數中使用
const theme = cookies().get('theme')
const data = await getData()
return '...'
}// `app` 目錄
import { cookies, headers } from 'next/headers'
async function getData() {
const authHeader = headers().get('authorization')
return '...'
}
export default async function Page() {
// 您可以直接在伺服器元件中使用 `cookies()` 或 `headers()`
// 或在您的資料獲取函數中使用
const theme = cookies().get('theme')
const data = await getData()
return '...'
}靜態網站生成 (getStaticProps)
在 pages 目錄中,getStaticProps 函數用於在構建時預渲染頁面。此函數可用於從外部 API 或直接從資料庫獲取資料,並在建構過程中將此資料傳遞給整個頁面。
// `pages` 目錄
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` 目錄
// 此函數可以命名為任何名稱
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` 目錄
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` 目錄
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/${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` 目錄
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` 目錄
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` 目錄
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` 目錄
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) 取代。
路由處理器允許你使用 Web 的 Request 和 Response API 為指定路由創建自定義請求處理器。
export async function GET(request: Request) {}export async function GET(request) {}須知:如果你之前使用 API 路由從客戶端調用外部 API,現在可以使用 伺服器元件 (Server Components) 來安全地獲取數據。了解更多關於 數據獲取 的資訊。
步驟 7:樣式設定
在 pages 目錄中,全局樣式表僅限於 pages/_app.js。在 app 目錄中,此限制已被取消。全局樣式可以添加到任何版面配置、頁面或元件中。
Tailwind CSS
如果你使用 Tailwind CSS,需要在 tailwind.config.js 文件中添加 app 目錄:
module.exports = {
content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}', // <-- 添加此行
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
],
}你還需要在 app/layout.js 文件中導入全局樣式:
import '../styles/globals.css'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}了解更多關於 使用 Tailwind CSS 設定樣式
Codemods
Next.js 提供了 Codemod 轉換,以幫助在功能被棄用時升級你的代碼庫。詳見 Codemods 以獲取更多資訊。