如何從 Pages 遷移到 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 伺服器,以使 ESLint 更改生效。開啟命令面板(Mac 上為 `cmd+shift+p`;Windows 上為 `ctrl+shift+p`),然後搜尋 `ESLint: Restart ESLint Server`。
後續步驟
更新後,請參閱以下部分以瞭解下一步
- 升級新功能:幫助您升級到新功能(如改進的 Image 和 Link 元件)的指南。
- 從 `pages` 目錄遷移到 `app` 目錄:幫助您逐步從 `pages` 目錄遷移到 `app` 目錄的指南。
升級新功能
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> 元件
`<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` 中所有路由的根佈局。
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 支援
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` 目錄中對巢狀佈局的原生支援來替換。
檢視前後示例
之前
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.jsexport 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.jsimport 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 支援替換。
之前
import Head from 'next/head'
export default function Page() {
return (
<>
<Head>
<title>My page title</title>
</Head>
</>
)
}之後
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/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'
// 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 部分。
- 這些新的鉤子僅在客戶端元件中受支援。它們不能在伺服器元件中使用。
'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()` 提供了替代方案。
在 `pages` 和 `app` 之間共享元件
為了使元件在 `pages` 和 `app` 路由器之間相容,請參考 `next/compat/router` 中的 `useRouter` 鉤子。這是 `pages` 目錄中的 `useRouter` 鉤子,但旨在在路由器之間共享元件時使用。一旦您準備好僅在 `app` 路由器上使用它,請更新到 `next/navigation` 中的新 `useRouter`。
步驟 6:遷移資料獲取方法
`pages` 目錄使用 `getServerSideProps` 和 `getStaticProps` 為頁面獲取資料。在 `app` 目錄中,這些以前的資料獲取函式被基於 `fetch()` 和 `async` React 伺服器元件構建的更簡單的 API 取代。
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` 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` 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` 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` 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` 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` 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` 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` 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` 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` 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` 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` 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 Request 和 Response API 為給定路由建立自定義請求處理程式。
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` 檔案中
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` 檔案中匯入您的全域性樣式
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 提供了碼轉換,以幫助您在功能棄用時升級程式碼庫。有關更多資訊,請參閱 碼轉換。
這有幫助嗎?