diff --git a/apps/docs/content/zh-hant/docs/01-app/01-getting-started/04-images.mdx b/apps/docs/content/zh-hant/docs/01-app/01-getting-started/04-images.mdx index e7927c81..5407a15d 100644 --- a/apps/docs/content/zh-hant/docs/01-app/01-getting-started/04-images.mdx +++ b/apps/docs/content/zh-hant/docs/01-app/01-getting-started/04-images.mdx @@ -1,9 +1,9 @@ --- -source-updated-at: 2025-05-22T15:18:56.000Z -translation-updated-at: 2025-05-25T20:43:27.368Z +source-updated-at: 2025-06-02T15:30:01.000Z +translation-updated-at: 2025-06-02T19:01:25.908Z title: 如何優化圖片 nav_title: 圖片 -description: 了解如何在 Next.js 中優化圖片 +description: 學習如何在 Next.js 中優化圖片 related: title: API 參考 description: 查看 Next.js Image 完整功能的 API 參考文件。 @@ -11,12 +11,12 @@ related: - app/api-reference/components/image --- -Next.js 的 [``](/docs/app/api-reference/components/image) 元件延伸了 HTML `` 元素,提供以下功能: +Next.js 的 [``](/docs/app/api-reference/components/image) 元件擴展了 HTML `` 元素,提供以下功能: -- **尺寸優化:** 自動為每種裝置提供正確尺寸的圖片,並使用 WebP 等現代圖片格式。 +- **尺寸優化:** 自動為每種裝置提供正確尺寸的圖片,使用 WebP 等現代圖片格式。 - **視覺穩定性:** 在圖片載入時自動防止[版面位移 (layout shift)](https://web.dev/articles/cls)。 -- **更快的頁面載入:** 僅在圖片進入視窗時才載入,使用瀏覽器原生的延遲載入,並可選擇模糊預覽圖。 -- **資源靈活性:** 按需調整圖片大小,即使是儲存在遠端伺服器上的圖片也能處理。 +- **更快的頁面載入:** 僅在圖片進入視窗時載入,使用瀏覽器原生的延遲載入,並可選擇模糊預覽圖。 +- **資源靈活性:** 按需調整圖片大小,即使是遠端伺服器上的圖片也能處理。 要開始使用 ``,請從 `next/image` 導入並在元件中渲染它。 @@ -42,7 +42,7 @@ export default function Page() { ## 本地圖片 -您可以將靜態檔案(如圖片和字體)儲存在根目錄下的 [`public`](/docs/app/api-reference/file-conventions/public-folder) 資料夾中。`public` 內的檔案可以從基本 URL (`/`) 開始在程式碼中引用。 +您可以將靜態檔案(如圖片和字體)存放在根目錄下的 [`public`](/docs/app/api-reference/file-conventions/public-folder) 資料夾中。`public` 內的檔案可以從基礎 URL (`/`) 開始在程式碼中引用。 顯示 app 和 public 資料夾的目錄結構 + ) +} +``` + +```jsx filename="app/page.js" switcher +import Image from 'next/image' + +export default function Page() { + return ( + 作者的照片 + ) +} +``` + +### 靜態導入圖片 + +您也可以導入並使用本地圖片檔案。Next.js 會根據導入的檔案自動確定圖片的固有 [`width`](/docs/app/api-reference/components/image#width-and-height) 和 [`height`](/docs/app/api-reference/components/image#width-and-height)。這些值用於確定圖片比例,並在圖片載入時防止[累積版面位移 (Cumulative Layout Shift)](https://web.dev/articles/cls)。 + +```tsx filename="app/page.tsx" switcher +import Image from 'next/image' +import ProfileImage from './profile.png' + +export default function Page() { + return ( + 作者的照片內容` 元件來新增專屬於頁面路由的內容。任何共享內容不應包裹在元件中。 */} +{/* 此文件的內容在應用程式路由和頁面路由之間共享。您可以使用 `內容` 元件來新增專屬於頁面路由的內容。任何共享內容不應包裹在元件中。 */} -[`next/font`](/docs/app/api-reference/components/font) 會自動優化您的字型(包括自訂字型),並移除外部網路請求以提升隱私性和效能。 +[`next/font`](/docs/app/api-reference/components/font) 會自動優化您的字型(包括自訂字型),並移除外部網路請求以提升隱私和效能。 -它包含**內建的自動自我託管**功能,適用於任何字型檔案。這意味著您可以無需擔心[版面偏移 (layout shift)](https://web.dev/articles/cls) 就能最佳化載入網頁字型。 +它包含**內建的自動自我託管**功能,適用於任何字型檔案。這意味著您可以無需擔心[版面偏移 (layout shift)](https://web.dev/articles/cls) 的情況下最佳化載入網頁字型。 -您還可以方便地使用所有 [Google 字型](https://fonts.google.com/)。CSS 和字型檔案會在建置時下載,並與其他靜態資源一起自我託管。**瀏覽器不會向 Google 發送任何請求。** +您也可以方便地使用所有 [Google 字型](https://fonts.google.com/)。CSS 和字型檔案會在建置時下載,並與其他靜態資源一起自我託管。**瀏覽器不會向 Google 發送任何請求。** @@ -60,7 +60,7 @@ export default function RootLayout({ children }) { -要在所有頁面中使用該字型,請將其新增至 `/pages` 下的 [`_app.js` 檔案](/docs/pages/building-your-application/routing/custom-app),如下所示: +要在所有頁面中使用字型,請將其新增至 `/pages` 下的 [`_app.js` 檔案](/docs/pages/building-your-application/routing/custom-app),如下所示: ```jsx filename="pages/_app.js" import { Inter } from 'next/font/google' @@ -79,11 +79,11 @@ export default function MyApp({ Component, pageProps }) { -> **🎥 觀看影片:** 進一步了解如何使用 `next/font` → [YouTube (6 分鐘)](https://www.youtube.com/watch?v=L8_98i_bMMA)。 +> **🎥 觀看影片:** 了解更多關於使用 `next/font` 的資訊 → [YouTube (6 分鐘)](https://www.youtube.com/watch?v=L8_98i_bMMA)。 ## 參考 -| 鍵值 | `font/google` | `font/local` | 類型 | 是否必填 | +| 鍵值 | `font/google` | `font/local` | 類型 | 必填 | | ------------------------------------------- | ------------------- | ------------------- | -------------------------- | ----------------- | | [`src`](#src) | | | 字串或物件陣列 | 是 | | [`weight`](#weight) | | | 字串或陣列 | 必填/選填 | @@ -109,31 +109,31 @@ export default function MyApp({ Component, pageProps }) { - `src:'./fonts/my-font.woff2'`,其中 `my-font.woff2` 位於 `app` 目錄內名為 `fonts` 的目錄中 - `src:[{path: './inter/Inter-Thin.ttf', weight: '100',},{path: './inter/Inter-Regular.ttf',weight: '400',},{path: './inter/Inter-Bold-Italic.ttf', weight: '700',style: 'italic',},]` -- 如果在 `app/page.tsx` 中呼叫字型載入器函式並使用 `src:'../styles/fonts/my-font.ttf'`,則 `my-font.ttf` 應位於專案根目錄的 `styles/fonts` 中 +- 如果在 `app/page.tsx` 中呼叫字型載入器函式並使用 `src:'../styles/fonts/my-font.ttf'`,則 `my-font.ttf` 位於專案根目錄的 `styles/fonts` 中 ### `weight` -字型的[`字重 (weight)`](https://fonts.google.com/knowledge/glossary/weight),有以下可能: +字型的[`字重 (weight)`](https://fonts.google.com/knowledge/glossary/weight),有以下可能性: -- 一個字串,其值為特定字型可用的字重,或如果是[可變字型](https://fonts.google.com/variablefonts)則為值的範圍 -- 如果字型不是[可變 Google 字型](https://fonts.google.com/variablefonts),則為字重值的陣列。僅適用於 `next/font/google` +- 一個字串,值為特定字型可用的字重,或如果是[可變字型 (variable font)](https://fonts.google.com/variablefonts) 則為值的範圍 +- 如果字型不是[可變 Google 字型 (variable google font)](https://fonts.google.com/variablefonts),則為字重值的陣列。僅適用於 `next/font/google` 用於 `next/font/google` 和 `next/font/local` -- 如果使用的字型**不是**[可變字型](https://fonts.google.com/variablefonts),則為必填 +- 如果使用的字型**不是**[可變字型 (variable)](https://fonts.google.com/variablefonts),則必填 範例: -- `weight: '400'`:單一字重值的字串 - 對於字型 [`Inter`](https://fonts.google.com/specimen/Inter?query=inter),可能的值為 `'100'`、`'200'`、`'300'`、`'400'`、`'500'`、`'600'`、`'700'`、`'800'`、`'900'` 或 `'variable'`(其中 `'variable'` 為預設值) -- `weight: '100 900'`:可變字型在 `100` 和 `900` 之間範圍的字串 +- `weight: '400'`:單一字重值的字串 - 對於字型 [`Inter`](https://fonts.google.com/specimen/Inter?query=inter),可能的值為 `'100'`、`'200'`、`'300'`、`'400'`、`'500'`、`'600'`、`'700'`、`'800'`、`'900'` 或 `'variable'`(其中 `'variable'` 是預設值) +- `weight: '100 900'`:可變字型的範圍字串,介於 `100` 和 `900` 之間 - `weight: ['100','400','900']`:非可變字型的 3 個可能值的陣列 ### `style` -字型的[`樣式 (style)`](https://developer.mozilla.org/docs/Web/CSS/font-style),有以下可能: +字型的[`樣式 (style)`](https://developer.mozilla.org/docs/Web/CSS/font-style),有以下可能性: -- 一個[值](https://developer.mozilla.org/docs/Web/CSS/font-style#values)的字串,預設值為 `'normal'` -- 如果字型不是[可變 Google 字型](https://fonts.google.com/variablefonts),則為樣式值的陣列。僅適用於 `next/font/google` +- 一個字串[值](https://developer.mozilla.org/docs/Web/CSS/font-style#values),預設值為 `'normal'` +- 如果字型不是[可變 Google 字型 (variable google font)](https://fonts.google.com/variablefonts),則為樣式值的陣列。僅適用於 `next/font/google` 用於 `next/font/google` 和 `next/font/local` @@ -142,12 +142,12 @@ export default function MyApp({ Component, pageProps }) { 範例: - `style: 'italic'`:字串 - 對於 `next/font/google`,可以是 `normal` 或 `italic` -- `style: 'oblique'`:字串 - 對於 `next/font/local`,可以接受任何值,但預期來自[標準字型樣式](https://developer.mozilla.org/docs/Web/CSS/font-style) +- `style: 'oblique'`:字串 - 對於 `next/font/local` 可以接受任何值,但預期來自[標準字型樣式](https://developer.mozilla.org/docs/Web/CSS/font-style) - `style: ['italic','normal']`:`next/font/google` 的 2 個值的陣列 - 值來自 `normal` 和 `italic` ### `subsets` -字型的[`子集 (subsets)`](https://fonts.google.com/knowledge/glossary/subsetting),由字串值的陣列定義,每個子集的名稱您希望[預載入](/docs/app/api-reference/components/font#specifying-a-subset)。透過 `subsets` 指定的字型在 [`preload`](#preload) 選項為 `true`(預設值)時,會在 head 中注入一個連結預載入標籤。 +字型的[`子集 (subsets)`](https://fonts.google.com/knowledge/glossary/subsetting),由字串值的陣列定義,每個子集的名稱您希望[預載 (preload)](/docs/app/api-reference/components/font#specifying-a-subset)。透過 `subsets` 指定的字型在 [`preload`](#preload) 選項為 `true`(預設值)時,會在 head 中注入一個 link preload 標籤。 用於 `next/font/google` @@ -169,7 +169,7 @@ export default function MyApp({ Component, pageProps }) { 範例: -- `axes: ['slnt']`:值為 `slnt` 的陣列,適用於 `Inter` 可變字型,該字型具有 `slnt` 作為額外的 `axes`,如[此處](https://fonts.google.com/variablefonts?vfquery=inter#font-families)所示。您可以透過在 [Google 可變字型頁面](https://fonts.google.com/variablefonts#font-families)上使用篩選器並尋找除 `wght` 之外的 axes 來找到您字型的可能 `axes` 值 +- `axes: ['slnt']`:值為 `slnt` 的陣列,用於 `Inter` 可變字型,該字型具有 `slnt` 作為額外的 `axes`,如[此處](https://fonts.google.com/variablefonts?vfquery=inter#font-families)所示。您可以在 [Google 可變字型頁面](https://fonts.google.com/variablefonts#font-families)上使用篩選器並尋找 `wght` 以外的 axes 來找到您字型的可能 `axes` 值 ### `display` @@ -185,7 +185,7 @@ export default function MyApp({ Component, pageProps }) { ### `preload` -一個布林值,指定是否應[預載入](/docs/app/api-reference/components/font#preloading)字型。預設為 `true`。 +一個布林值,指定是否應[預載 (preload)](/docs/app/api-reference/components/font#preloading) 字型。預設為 `true`。 用於 `next/font/google` 和 `next/font/local` @@ -197,7 +197,7 @@ export default function MyApp({ Component, pageProps }) { ### `fallback` -如果無法載入字型,則使用的回退字型。一個沒有預設值的回退字型字串陣列。 +如果無法載入字型,則使用的後備字型。一個字串陣列,沒有預設值。 - 選填 @@ -205,12 +205,12 @@ export default function MyApp({ Component, pageProps }) { 範例: -- `fallback: ['system-ui', 'arial']`:將回退字型設定為 `system-ui` 或 `arial` 的陣列 +- `fallback: ['system-ui', 'arial']`:將後備字型設定為 `system-ui` 或 `arial` 的陣列 ### `adjustFontFallback` -- 對於 `next/font/google`:一個布林值,設定是否應使用自動回退字型來減少[累積版面偏移 (Cumulative Layout Shift)](https://web.dev/cls/)。預設為 `true`。 -- 對於 `next/font/local`:一個字串或布林值 `false`,設定是否應使用自動回退字型來減少[累積版面偏移 (Cumulative Layout Shift)](https://web.dev/cls/)。可能的值為 `'Arial'`、`'Times New Roman'` 或 `false`。預設為 `'Arial'`。 +- 對於 `next/font/google`:一個布林值,設定是否應使用自動後備字型來減少[累積版面偏移 (Cumulative Layout Shift)](https://web.dev/cls/)。預設為 `true`。 +- 對於 `next/font/local`:一個字串或布林值 `false`,設定是否應使用自動後備字型來減少[累積版面偏移 (Cumulative Layout Shift)](https://web.dev/cls/)。可能的值為 `'Arial'`、`'Times New Roman'` 或 `false`。預設為 `'Arial'`。 用於 `next/font/google` 和 `next/font/local` @@ -218,12 +218,12 @@ export default function MyApp({ Component, pageProps }) { 範例: -- `adjustFontFallback: false`:適用於 `next/font/google` -- `adjustFontFallback: 'Times New Roman'`:適用於 `next/font/local` +- `adjustFontFallback: false`:用於 `next/font/google` +- `adjustFontFallback: 'Times New Roman'`:用於 `next/font/local` ### `variable` -一個字串值,用於定義如果樣式透過 [CSS 變數方法](#css-variables) 應用時要使用的 CSS 變數名稱。 +一個字串值,定義如果樣式使用 [CSS 變數方法](#css-variables) 應用時要使用的 CSS 變數名稱。 用於 `next/font/google` 和 `next/font/local` @@ -249,7 +249,7 @@ export default function MyApp({ Component, pageProps }) { ## Google 字型 -要使用 Google 字型,請從 `next/font/google` 導入它作為一個函式。我們建議使用[可變字型](https://fonts.google.com/variablefonts)以獲得最佳效能和靈活性。 +要使用 Google 字型,請從 `next/font/google` 匯入它作為一個函式。我們建議使用[可變字型 (variable fonts)](https://fonts.google.com/variablefonts) 以獲得最佳效能和靈活性。 @@ -293,7 +293,7 @@ export default function RootLayout({ children }) { } ``` -如果您無法使用可變字型,則**需要指定字重**: +如果您無法使用可變字型,則**需要指定一個字重**: ```tsx filename="app/layout.tsx" switcher import { Roboto } from 'next/font/google' @@ -339,7 +339,7 @@ export default function RootLayout({ children }) { -要在所有頁面中使用該字型,請將其新增至 `/pages` 下的 [`_app.js` 檔案](/docs/pages/building-your-application/routing/custom-app),如下所示: +要在所有頁面中使用字型,請將其新增至 `/pages` 下的 [`_app.js` 檔案](/docs/pages/building-your-application/routing/custom-app),如下所示: ```jsx filename="pages/_app.js" import { Inter } from 'next/font/google' @@ -356,7 +356,7 @@ export default function MyApp({ Component, pageProps }) { } ``` -如果您無法使用可變字型,則**需要指定字重**: +如果您無法使用可變字型,則**需要指定一個字重**: ```jsx filename="pages/_app.js" import { Roboto } from 'next/font/google' @@ -388,11 +388,11 @@ const roboto = Roboto({ }) ``` -> **小提示:** 對於包含多個單詞的字型名稱,請使用底線 (\_)。例如,`Roboto Mono` 應導入為 `Roboto_Mono`。 +> **小知識:** 對於包含多個單詞的字型名稱,請使用底線 (\_)。例如,`Roboto Mono` 應匯入為 `Roboto_Mono`。 -### 在 `` 中應用字型 +### 在 `` 中套用字型 您也可以在不使用包裝器和 `className` 的情況下使用字型,方法是將其注入到 `` 中,如下所示: @@ -417,7 +417,7 @@ export default function MyApp({ Component, pageProps }) { ### 單頁使用 -要在單一頁面上使用該字型,請將其新增至特定頁面,如下所示: +要在單一頁面上使用字型,請將其新增至特定頁面,如下所示: ```jsx filename="pages/index.js" import { Inter } from 'next/font/google' @@ -437,9 +437,9 @@ export default function Home() { ### 指定子集 -Google 字型會自動[子集化 (subset)](https://fonts.google.com/knowledge/glossary/subsetting)。這減少了字型檔案的大小並提高了效能。您需要定義要預載入哪些子集。如果 [`preload`](/docs/app/api-reference/components/font#preload) 為 `true` 但未指定任何子集,則會產生警告。 +Google 字型會自動[子集化 (subset)](https://fonts.google.com/knowledge/glossary/subsetting)。這減少了字型檔案的大小並提高了效能。您需要定義您想要預載的子集。如果在 [`preload`](/docs/app/api-reference/components/font#preload) 為 `true` 時未指定任何子集,則會產生警告。 -可以透過將其新增至函式呼叫來完成: +這可以透過將其新增至函式呼叫來完成: @@ -463,11 +463,11 @@ const inter = Inter({ subsets: ['latin'] }) 查看[字型 API 參考](/docs/app/api-reference/components/font)以獲取更多資訊。 -## 使用多種字體 +## 使用多種字型 -您可以在應用程式中導入並使用多種字體。有兩種方法可以實現。 +您可以在應用程式中匯入並使用多種字型。有兩種方法可以實現。 -第一種方法是建立一個工具函數來導出字體,在需要的地方導入並套用其 `className`。這確保字體只在渲染時預先載入: +第一種方法是建立一個匯出字型的工具函式,在需要的地方匯入並套用其 `className`。這確保字型只在渲染時預先載入: ```ts filename="app/fonts.ts" switcher import { Inter, Roboto_Mono } from 'next/font/google' @@ -533,7 +533,7 @@ import { roboto_mono } from './fonts' export default function Page() { return ( <> -

My page

+

我的頁面

) } @@ -545,7 +545,7 @@ import { roboto_mono } from './fonts' export default function Page() { return ( <> -

My page

+

我的頁面

) } @@ -553,9 +553,9 @@ export default function Page() {
-在上面的範例中,`Inter` 將被全域套用,而 `Roboto Mono` 可以在需要時導入並套用。 +在上面的範例中,`Inter` 會全域套用,而 `Roboto Mono` 可以按需匯入並套用。 -或者,您可以建立一個 [CSS 變數](/docs/app/api-reference/components/font#variable) 並搭配您偏好的 CSS 解決方案使用: +或者,您可以建立一個 [CSS 變數](/docs/app/api-reference/components/font#variable) 並與您偏好的 CSS 解決方案一起使用: @@ -583,7 +583,7 @@ export default function RootLayout({ return ( -

My App

+

我的應用程式

{children}
@@ -610,7 +610,7 @@ export default function RootLayout({ children }) { return ( -

My App

+

我的應用程式

{children}
@@ -630,20 +630,20 @@ h1 { } ``` -在上面的範例中,`Inter` 將被全域套用,而任何 `

` 標籤將使用 `Roboto Mono` 樣式。 +在上面的範例中,`Inter` 會全域套用,而任何 `

` 標籤會使用 `Roboto Mono` 樣式。 -> **建議**:謹慎使用多種字體,因為每新增一種字體,客戶端都需要下載額外的資源。 +> **建議**:謹慎使用多種字型,因為每新增一種字型都是客戶端需要下載的額外資源。 -### 本地字體 +### 本地字型 -導入 `next/font/local` 並指定本地字體檔案的 `src`。我們建議使用 [可變字體 (variable fonts)](https://fonts.google.com/variablefonts) 以獲得最佳效能和靈活性。 +匯入 `next/font/local` 並指定本地字型檔案的 `src`。我們建議使用 [可變字型 (variable fonts)](https://fonts.google.com/variablefonts) 以獲得最佳效能和靈活性。 ```tsx filename="app/layout.tsx" switcher import localFont from 'next/font/local' -// 字體檔案可以放在 `app` 目錄內 +// 字型檔案可以放在 `app` 目錄內 const myFont = localFont({ src: './my-font.woff2', display: 'swap', @@ -665,7 +665,7 @@ export default function RootLayout({ ```jsx filename="app/layout.js" switcher import localFont from 'next/font/local' -// 字體檔案可以放在 `app` 目錄內 +// 字型檔案可以放在 `app` 目錄內 const myFont = localFont({ src: './my-font.woff2', display: 'swap', @@ -687,7 +687,7 @@ export default function RootLayout({ children }) { ```jsx filename="pages/_app.js" import localFont from 'next/font/local' -// 字體檔案可以放在 `pages` 目錄內 +// 字型檔案可以放在 `pages` 目錄內 const myFont = localFont({ src: './my-font.woff2' }) export default function MyApp({ Component, pageProps }) { @@ -701,7 +701,7 @@ export default function MyApp({ Component, pageProps }) { -如果想為單一字體家族使用多個檔案,`src` 可以是陣列: +如果想為單一字型家族使用多個檔案,`src` 可以是陣列: ```js const roboto = localFont({ @@ -730,15 +730,15 @@ const roboto = localFont({ }) ``` -查看 [字體 API 參考文件](/docs/app/api-reference/components/font) 以獲取更多資訊。 +檢視 [字型 API 參考文件](/docs/app/api-reference/components/font) 以獲取更多資訊。 -### 搭配 Tailwind CSS +### 與 Tailwind CSS 搭配使用 `next/font` 透過 [CSS 變數](/docs/app/api-reference/components/font#css-variables) 與 [Tailwind CSS](https://tailwindcss.com/) 無縫整合。 -在下面的範例中,我們使用來自 `next/font/google` 的 `Inter` 和 `Roboto_Mono` 字體(您可以使用任何 Google 字體或本地字體)。使用 `variable` 選項定義 CSS 變數名稱,例如分別為這些字體定義 `inter` 和 `roboto_mono`。然後,套用 `inter.variable` 和 `roboto_mono.variable` 將 CSS 變數包含在您的 HTML 文件中。 +在以下範例中,我們使用來自 `next/font/google` 的 `Inter` 和 `Roboto_Mono` 字型(您可以使用任何 Google 字型或本地字型)。使用 `variable` 選項定義 CSS 變數名稱,例如分別為這些字型定義 `inter` 和 `roboto_mono`。然後,套用 `inter.variable` 和 `roboto_mono.variable` 將 CSS 變數包含在您的 HTML 文件中。 -> **須知**:您可以根據偏好、樣式需求或專案要求,將這些變數添加到 `` 或 `` 標籤中。 +> **小提示**:您可以根據偏好、樣式需求或專案要求,將這些變數新增到 `` 或 `` 標籤。 @@ -829,11 +829,11 @@ export default function MyApp({ Component, pageProps }) { -最後,將 CSS 變數添加到您的 [Tailwind CSS 設定檔](/docs/app/guides/tailwind-css#configuring-tailwind): +最後,將 CSS 變數新增到您的 [Tailwind CSS 設定檔](/docs/app/guides/tailwind-css#configuring-tailwind): ### Tailwind CSS v4 -從 [Tailwind v4](https://tailwindcss.com/blog/tailwindcss-v4) 開始,預設不需要任何設定。如果需要設定 Tailwind,可以按照 [官方文件](https://tailwindcss.com/blog/tailwindcss-v4#css-first-configuration) 設定全域 CSS 檔案。 +從 [Tailwind v4](https://tailwindcss.com/blog/tailwindcss-v4) 開始,預設不需要任何設定。如果需要設定 Tailwind,可以遵循 [官方文件](https://tailwindcss.com/blog/tailwindcss-v4#css-first-configuration) 來設定全域 CSS 檔案。 ```js filename="global.css" @import "tailwindcss"; @@ -866,7 +866,7 @@ module.exports = { } ``` -現在您可以使用 `font-sans` 和 `font-mono` 工具類別將字體套用到元素上。 +現在您可以使用 `font-sans` 和 `font-mono` 工具類別將字型套用到元素上。 ```

The quick brown fox ...

@@ -875,7 +875,7 @@ module.exports = { ### 套用樣式 -您可以透過三種方式套用字體樣式: +您可以透過三種方式套用字型樣式: - [`className`](#classname) - [`style`](#style-1) @@ -883,7 +883,7 @@ module.exports = { #### `className` -返回一個唯讀的 CSS `className`,用於將載入的字體傳遞給 HTML 元素。 +返回一個唯讀的 CSS `className`,用於將載入的字型傳遞給 HTML 元素。 ```tsx

Hello, Next.js!

@@ -891,7 +891,7 @@ module.exports = { #### `style` -返回一個唯讀的 CSS `style` 物件,用於將載入的字體傳遞給 HTML 元素,包括 `style.fontFamily` 以存取字體家族名稱和備用字體。 +返回一個唯讀的 CSS `style` 物件,用於將載入的字型傳遞給 HTML 元素,包括 `style.fontFamily` 以存取字型家族名稱和備用字型。 ```tsx

Hello World

@@ -901,7 +901,7 @@ module.exports = { 如果您想在外部樣式表中設定樣式並在那裡指定其他選項,請使用 CSS 變數方法。 -除了導入字體外,還導入定義 CSS 變數的 CSS 檔案,並如下設定字體載入器物件的 `variable` 選項: +除了匯入字型外,還匯入定義 CSS 變數的 CSS 檔案,並如下設定字型載入器物件的 `variable` 選項: ```tsx filename="app/page.tsx" switcher import { Inter } from 'next/font/google' @@ -921,7 +921,7 @@ const inter = Inter({ }) ``` -要使用字體,請將要套用樣式的文字父容器的 `className` 設定為字體載入器的 `variable` 值,並將文字的 `className` 設定為外部 CSS 檔案中的 `styles` 屬性。 +要使用字型,將要設定樣式的文字父容器的 `className` 設定為字型載入器的 `variable` 值,並將文字的 `className` 設定為來自外部 CSS 檔案的 `styles` 屬性。 ```tsx filename="app/page.tsx" switcher
@@ -945,27 +945,27 @@ const inter = Inter({ } ``` -在上面的範例中,文字 `Hello World` 使用 `Inter` 字體和產生的字體備用樣式,並設定 `font-weight: 200` 和 `font-style: italic`。 +在上面的範例中,文字 `Hello World` 使用 `Inter` 字型和產生的字型備用,並設定 `font-weight: 200` 和 `font-style: italic`。 -### 使用字體定義檔案 +### 使用字型定義檔案 -每次呼叫 `localFont` 或 Google 字體函數時,該字體將作為一個實例託管在您的應用程式中。因此,如果需要在多個地方使用相同的字體,應在一個地方載入它,並在需要的地方導入相關的字體物件。這可以透過字體定義檔案來實現。 +每次呼叫 `localFont` 或 Google 字型函式時,該字型都會在您的應用程式中作為一個實例託管。因此,如果需要在多個地方使用相同的字型,應在一個地方載入它,並在需要的地方匯入相關的字型物件。這可以透過字型定義檔案實現。 -例如,在應用程式目錄的根目錄下建立一個 `styles` 資料夾,並在其中建立一個 `fonts.ts` 檔案。 +例如,在應用程式目錄的根目錄中建立一個 `styles` 資料夾,並在其中建立一個 `fonts.ts` 檔案。 -然後,如下指定您的字體定義: +然後,如下指定您的字型定義: ```ts filename="styles/fonts.ts" switcher import { Inter, Lora, Source_Sans_3 } from 'next/font/google' import localFont from 'next/font/local' -// 定義您的可變字體 +// 定義您的可變字型 const inter = Inter() const lora = Lora() -// 定義非可變字體的兩種字重 +// 定義非可變字型的兩種字重 const sourceCodePro400 = Source_Sans_3({ weight: '400' }) const sourceCodePro700 = Source_Sans_3({ weight: '700' }) -// 定義一個自訂本地字體,GreatVibes-Regular.ttf 存放在 styles 資料夾中 +// 定義一個自訂本地字型,GreatVibes-Regular.ttf 存放在 styles 資料夾中 const greatVibes = localFont({ src: './GreatVibes-Regular.ttf' }) export { inter, lora, sourceCodePro400, sourceCodePro700, greatVibes } @@ -975,13 +975,13 @@ export { inter, lora, sourceCodePro400, sourceCodePro700, greatVibes } import { Inter, Lora, Source_Sans_3 } from 'next/font/google' import localFont from 'next/font/local' -// 定義您的可變字體 +// 定義您的可變字型 const inter = Inter() const lora = Lora() -// 定義非可變字體的兩種字重 +// 定義非可變字型的兩種字重 const sourceCodePro400 = Source_Sans_3({ weight: '400' }) const sourceCodePro700 = Source_Sans_3({ weight: '700' }) -// 定義一個自訂本地字體,GreatVibes-Regular.ttf 存放在 styles 資料夾中 +// 定義一個自訂本地字型,GreatVibes-Regular.ttf 存放在 styles 資料夾中 const greatVibes = localFont({ src: './GreatVibes-Regular.ttf' }) export { inter, lora, sourceCodePro400, sourceCodePro700, greatVibes } @@ -995,12 +995,12 @@ import { inter, lora, sourceCodePro700, greatVibes } from '../styles/fonts' export default function Page() { return (
-

使用 Inter 字體的 Hello world

-

使用 Lora 字體的 Hello world

+

使用 Inter 字型的 Hello world

+

使用 Lora 字型的 Hello world

- 使用字重 700 的 Source_Sans_3 字體的 Hello world + 使用字重 700 的 Source_Sans_3 字型的 Hello world

-

使用 Great Vibes 字體的標題

+

使用 Great Vibes 字型的標題

) } @@ -1012,18 +1012,18 @@ import { inter, lora, sourceCodePro700, greatVibes } from '../styles/fonts' export default function Page() { return (
-

使用 Inter 字體的 Hello world

-

使用 Lora 字體的 Hello world

+

使用 Inter 字型的 Hello world

+

使用 Lora 字型的 Hello world

- 使用字重 700 的 Source_Sans_3 字體的 Hello world + 使用字重 700 的 Source_Sans_3 字型的 Hello world

-

使用 Great Vibes 字體的標題

+

使用 Great Vibes 字型的標題

) } ``` -為了更方便地在程式碼中存取字體定義,您可以在 `tsconfig.json` 或 `jsconfig.json` 檔案中定義路徑別名如下: +為了更輕鬆地在程式碼中存取字型定義,您可以在 `tsconfig.json` 或 `jsconfig.json` 檔案中定義路徑別名如下: ```json filename="tsconfig.json" { @@ -1035,7 +1035,7 @@ export default function Page() { } ``` -現在您可以如下導入任何字體定義: +現在您可以如下匯入任何字型定義: ```tsx filename="app/about/page.tsx" switcher import { greatVibes, sourceCodePro400 } from '@/fonts' @@ -1049,26 +1049,26 @@ import { greatVibes, sourceCodePro400 } from '@/fonts' -當在您網站的頁面上呼叫字體函數時,它不會在全域可用,也不會在所有路由上預先載入。相反,字體僅根據使用它的檔案類型在相關路由上預先載入: +當在您網站的頁面上呼叫字型函式時,它不會全域可用,也不會在所有路由上預先載入。相反,字型只會根據使用它的檔案類型在相關路由上預先載入: -- 如果是 [唯一頁面](/docs/app/api-reference/file-conventions/page),則在該頁面的唯一路由上預先載入。 -- 如果是 [佈局 (layout)](/docs/app/building-your-application/routing/layouts-and-templates#layouts),則在該佈局包裝的所有路由上預先載入。 -- 如果是 [根佈局 (root layout)](/docs/app/building-your-application/routing/layouts-and-templates#root-layout-required),則在所有路由上預先載入。 +- 如果是 [唯一頁面](/docs/app/api-reference/file-conventions/page),它會在該頁面的唯一路由上預先載入。 +- 如果是 [佈局](/docs/app/api-reference/file-conventions/layout),它會在佈局包裝的所有路由上預先載入。 +- 如果是 [根佈局](/docs/app/api-reference/file-conventions/layout#root-layout),它會在所有路由上預先載入。 -當在您網站的頁面上呼叫字體函數時,它不會在全域可用,也不會在所有路由上預先載入。相反,字體僅根據使用它的檔案類型在相關路由上預先載入: +當在您網站的頁面上呼叫字型函式時,它不會全域可用,也不會在所有路由上預先載入。相反,字型只會根據使用它的檔案類型在相關路由上預先載入: -- 如果是 [唯一頁面](/docs/pages/building-your-application/routing/pages-and-layouts),則在該頁面的唯一路由上預先載入。 -- 如果在 [自訂 App](/docs/pages/building-your-application/routing/custom-app) 中,則在 `/pages` 下的所有網站路由上預先載入。 +- 如果是 [唯一頁面](/docs/pages/building-your-application/routing/pages-and-layouts),它會在該頁面的唯一路由上預先載入。 +- 如果在 [自訂 App](/docs/pages/building-your-application/routing/custom-app) 中,它會在 `/pages` 下的所有網站路由上預先載入。 ## 版本變更 -| 版本 | 變更內容 | -| ---------- | ----------------------------------------------------------------------- | -| `v13.2.0` | `@next/font` 更名為 `next/font`。不再需要安裝。 | -| `v13.0.0` | 新增 `@next/font`。 | +| 版本 | 變更內容 | +| ---------- | --------------------------------------------------------------------- | +| `v13.2.0` | `@next/font` 更名為 `next/font`。不再需要安裝。 | +| `v13.0.0` | 新增 `@next/font`。 | diff --git a/apps/docs/content/zh-hant/docs/01-app/05-api-reference/03-file-conventions/route-segment-config.mdx b/apps/docs/content/zh-hant/docs/01-app/05-api-reference/03-file-conventions/route-segment-config.mdx index 8296be94..e25ee09f 100644 --- a/apps/docs/content/zh-hant/docs/01-app/05-api-reference/03-file-conventions/route-segment-config.mdx +++ b/apps/docs/content/zh-hant/docs/01-app/05-api-reference/03-file-conventions/route-segment-config.mdx @@ -1,11 +1,11 @@ --- -source-updated-at: 2025-06-01T01:32:20.000Z -translation-updated-at: 2025-06-01T22:31:38.819Z -title: 路由區段配置 -description: 了解如何配置 Next.js 路由區段的選項 +source-updated-at: 2025-06-02T15:30:01.000Z +translation-updated-at: 2025-06-02T19:03:07.482Z +title: 路由區段設定 +description: 了解如何設定 Next.js 路由區段的選項。 --- -> 若啟用了 [`dynamicIO`](/docs/app/api-reference/config/next-config-js/dynamicIO) 標記,本頁所述選項將被停用,並將在未來版本中棄用。 +> 如果啟用了 [`dynamicIO`](/docs/app/api-reference/config/next-config-js/dynamicIO) 標誌,本頁描述的選項將被禁用,並將在未來版本中被棄用。 路由區段選項允許您通過直接導出以下變數來配置 [頁面](/docs/app/api-reference/file-conventions/layout)、[佈局](/docs/app/api-reference/file-conventions/layout) 或 [路由處理器](/docs/app/building-your-application/routing/route-handlers) 的行為: @@ -50,9 +50,9 @@ export const dynamic = 'auto' // 'auto' | 'force-dynamic' | 'error' | 'force-static' ``` -> **須知**:`app` 目錄中的新模型更傾向於在 `fetch` 請求層級進行細粒度的快取控制,而不是像 `pages` 目錄中的 `getServerSideProps` 和 `getStaticProps` 那樣在頁面層級採用全有或全無的二進制模型。`dynamic` 選項是一種方便的方式,可以選擇回到先前的模型,並提供更簡單的遷移路徑。 +> **須知**:`app` 目錄中的新模型更傾向於在 `fetch` 請求層級進行細粒度的快取控制,而不是 `pages` 目錄中頁面層級的 `getServerSideProps` 和 `getStaticProps` 的全有或全無模型。`dynamic` 選項是一種回退到先前模型的便捷方式,並提供了更簡單的遷移路徑。 -- **`'auto'`** (預設):預設選項,盡可能快取而不阻止任何元件選擇動態行為。 +- **`'auto'`** (預設):預設選項,盡可能快取,同時不阻止任何元件選擇動態行為。 - **`'force-dynamic'`**:強制 [動態渲染](/docs/app/getting-started/partial-prerendering#dynamic-rendering),這將導致路由在每次用戶請求時渲染。此選項等同於: - 將佈局或頁面中每個 `fetch()` 請求的選項設置為 `{ cache: 'no-store', next: { revalidate: 0 } }`。 - 將區段配置設置為 `export const fetchCache = 'force-no-store'` @@ -60,7 +60,7 @@ export const dynamic = 'auto' - `pages` 目錄中的 `getStaticProps()`。 - 將佈局或頁面中每個 `fetch()` 請求的選項設置為 `{ cache: 'force-cache' }`。 - 將區段配置設置為 `fetchCache = 'only-cache', dynamicParams = false`。 - - `dynamic = 'error'` 會將 `dynamicParams` 的預設值從 `true` 更改為 `false`。您可以通過手動設置 `dynamicParams = true` 來選擇動態渲染未由 `generateStaticParams` 生成的動態參數的頁面。 + - `dynamic = 'error'` 會將 `dynamicParams` 的預設值從 `true` 更改為 `false`。您可以通過手動設置 `dynamicParams = true` 來選擇為未由 `generateStaticParams` 生成的動態參數動態渲染頁面。 - **`'force-static'`**:強制靜態渲染並快取佈局或頁面的數據,通過強制 [`cookies`](/docs/app/api-reference/functions/cookies)、[`headers()`](/docs/app/api-reference/functions/headers) 和 [`useSearchParams()`](/docs/app/api-reference/functions/use-search-params) 返回空值。可以在使用 `force-static` 渲染的頁面或佈局中進行 [`revalidate`](#revalidate)、[`revalidatePath`](/docs/app/api-reference/functions/revalidatePath) 或 [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag)。 > **須知**: @@ -69,7 +69,7 @@ export const dynamic = 'auto' ### `dynamicParams` -控制訪問未由 [generateStaticParams](/docs/app/api-reference/functions/generate-static-params) 生成的動態區段時的行為。 +控制訪問未通過 [generateStaticParams](/docs/app/api-reference/functions/generate-static-params) 生成的動態區段時的行為。 ```tsx filename="layout.tsx | page.tsx" switcher export const dynamicParams = true // true | false, @@ -85,13 +85,12 @@ export const dynamicParams = true // true | false, > **須知**: > > - 此選項取代了 `pages` 目錄中 `getStaticPaths` 的 `fallback: true | false | blocking` 選項。 -> - 若要靜態渲染所有路徑(首次訪問時),您需要在 `generateStaticParams` 中返回一個空數組,或使用 `export const dynamic = 'force-static'`。 -> - 當 `dynamicParams = true` 時,區段使用 [串流伺服器渲染 (Streaming Server Rendering)](/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense)。 -> - 如果使用 `dynamic = 'error'` 和 `dynamic = 'force-static'`,則會將 `dynamicParams` 的預設值更改為 `false`。 +> - 要在首次訪問時靜態渲染所有路徑,您需要在 `generateStaticParams` 中返回一個空數組,或使用 `export const dynamic = 'force-static'`。 +> - 當 `dynamicParams = true` 時,區段使用 [串流伺服器渲染](/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense)。 ### `revalidate` -設置佈局或頁面的預設重新驗證時間。此選項不會覆蓋個別 `fetch` 請求設置的 `revalidate` 值。 +為佈局或頁面設置預設的重新驗證時間。此選項不會覆蓋個別 `fetch` 請求設置的 `revalidate` 值。 ```tsx filename="layout.tsx | page.tsx | route.ts" switcher export const revalidate = false @@ -103,27 +102,27 @@ export const revalidate = false // false | 0 | number ``` -- **`false`** (預設):預設啟發式方法會快取任何將 `cache` 選項設置為 `'force-cache'` 的 `fetch` 請求,或在 [動態 API](/docs/app/getting-started/partial-prerendering#dynamic-rendering#dynamic-apis) 使用之前發現的請求。語義上等同於 `revalidate: Infinity`,這意味著資源應無限期快取。個別 `fetch` 請求仍可使用 `cache: 'no-store'` 或 `revalidate: 0` 來避免被快取並使路由動態渲染。或者將 `revalidate` 設置為低於路由預設值的正數,以增加路由的重新驗證頻率。 -- **`0`**:確保佈局或頁面始終 [動態渲染](/docs/app/getting-started/partial-prerendering#dynamic-rendering),即使未發現動態 API 或未快取的數據獲取。此選項將未設置 `cache` 選項的 `fetch` 請求的預設值更改為 `'no-store'`,但保留選擇 `'force-cache'` 或使用正數 `revalidate` 的 `fetch` 請求不變。 +- **`false`** (預設):預設啟發式方法,快取任何將 `cache` 選項設置為 `'force-cache'` 的 `fetch` 請求,或在 [動態 API](/docs/app/getting-started/partial-prerendering#dynamic-rendering#dynamic-apis) 使用之前發現的請求。語義上等同於 `revalidate: Infinity`,這意味著資源應無限快取。個別 `fetch` 請求仍可以使用 `cache: 'no-store'` 或 `revalidate: 0` 來避免被快取並使路由動態渲染。或者將 `revalidate` 設置為低於路由預設的正數,以增加路由的重新驗證頻率。 +- **`0`**:確保佈局或頁面始終 [動態渲染](/docs/app/getting-started/partial-prerendering#dynamic-rendering),即使未發現動態 API 或未快取的數據獲取。此選項將未設置 `cache` 選項的 `fetch` 請求的預設值更改為 `'no-store'`,但保留選擇 `'force-cache'` 或使用正數 `revalidate` 的 `fetch` 請求。 - **`number`**:(以秒為單位) 將佈局或頁面的預設重新驗證頻率設置為 `n` 秒。 > **須知**: > -> - 重新驗證值必須是靜態可分析的。例如 `revalidate = 600` 是有效的,但 `revalidate = 60 * 10` 則無效。 +> - 重新驗證值必須是靜態可分析的。例如 `revalidate = 600` 是有效的,但 `revalidate = 60 * 10` 無效。 > - 使用 `runtime = 'edge'` 時,重新驗證值不可用。 > - 在開發環境中,頁面始終按需渲染且永不快取。這使您可以立即看到更改,而無需等待重新驗證期結束。 #### 重新驗證頻率 -- 單個路由中每個佈局和頁面的最低 `revalidate` 值將決定整個路由的重新驗證頻率。這確保子頁面與其父佈局一樣頻繁地重新驗證。 -- 個別 `fetch` 請求可以設置比路由預設 `revalidate` 更低的 `revalidate` 值,以增加整個路由的重新驗證頻率。這允許您根據某些條件動態選擇更頻繁地重新驗證特定路由。 +- 單個路由的所有佈局和頁面中最低的 `revalidate` 值將決定整個路由的重新驗證頻率。這確保子頁面與其父佈局一樣頻繁地重新驗證。 +- 個別 `fetch` 請求可以設置比路由預設 `revalidate` 更低的 `revalidate` 值,以增加整個路由的重新驗證頻率。這允許您根據某些條件動態選擇更頻繁地重新驗證某些路由。 ### `fetchCache`
這是一個高級選項,僅在您需要覆蓋預設行為時使用。 -預設情況下,Next.js 會快取任何在使用 [動態 API](/docs/app/getting-started/partial-prerendering#dynamic-rendering#dynamic-apis) 之前可達的 `fetch()` 請求,並且不會快取在使用動態 API 之後發現的 `fetch` 請求。 +預設情況下,Next.js **會快取** 在 [動態 API](/docs/app/getting-started/partial-prerendering#dynamic-rendering#dynamic-apis) 使用之前可達的任何 `fetch()` 請求,並且 **不會快取** 在動態 API 使用之後發現的 `fetch` 請求。 `fetchCache` 允許您覆蓋佈局或頁面中所有 `fetch` 請求的預設 `cache` 選項。 @@ -139,21 +138,21 @@ export const fetchCache = 'auto' // 'force-cache' | 'force-no-store' | 'default-no-store' | 'only-no-store' ``` -- **`'auto'`** (預設):預設選項,根據 `fetch` 請求提供的 `cache` 選項快取在使用動態 API 之前的請求,不快取在使用動態 API 之後的請求。 -- **`'default-cache'`**:允許將任何 `cache` 選項傳遞給 `fetch`,但如果未提供選項,則將 `cache` 選項設置為 `'force-cache'`。這意味著即使是使用動態 API 之後的 `fetch` 請求也被視為靜態。 +- **`'auto'`** (預設):預設選項,根據 `fetch` 請求提供的 `cache` 選項快取動態 API 之前的請求,不快取動態 API 之後的請求。 +- **`'default-cache'`**:允許將任何 `cache` 選項傳遞給 `fetch`,但如果未提供選項,則將 `cache` 選項設置為 `'force-cache'`。這意味著即使是動態 API 之後的 `fetch` 請求也被視為靜態。 - **`'only-cache'`**:確保所有 `fetch` 請求選擇快取,如果未提供選項,則將預設值更改為 `cache: 'force-cache'`,如果任何 `fetch` 請求使用 `cache: 'no-store'`,則會引發錯誤。 -- **`'force-cache'`**:確保所有 `fetch` 請求選擇快取,通過將所有 `fetch` 請求的 `cache` 選項設置為 `'force-cache'`。 -- **`'default-no-store'`**:允許將任何 `cache` 選項傳遞給 `fetch`,但如果未提供選項,則將 `cache` 選項設置為 `'no-store'`。這意味著即使是使用動態 API 之前的 `fetch` 請求也被視為動態。 +- **`'force-cache'`**:確保所有 `fetch` 請求選擇快取,將所有 `fetch` 請求的 `cache` 選項設置為 `'force-cache'`。 +- **`'default-no-store'`**:允許將任何 `cache` 選項傳遞給 `fetch`,但如果未提供選項,則將 `cache` 選項設置為 `'no-store'`。這意味著即使是動態 API 之前的 `fetch` 請求也被視為動態。 - **`'only-no-store'`**:確保所有 `fetch` 請求選擇不進行快取,如果未提供選項,則將預設值更改為 `cache: 'no-store'`,如果任何 `fetch` 請求使用 `cache: 'force-cache'`,則會引發錯誤。 -- **`'force-no-store'`**:確保所有 `fetch` 請求選擇不進行快取,通過將所有 `fetch` 請求的 `cache` 選項設置為 `'no-store'`。這強制所有 `fetch` 請求在每次請求時重新獲取,即使它們提供了 `'force-cache'` 選項。 +- **`'force-no-store'`**:確保所有 `fetch` 請求選擇不進行快取,將所有 `fetch` 請求的 `cache` 選項設置為 `'no-store'`。這強制所有 `fetch` 請求在每次請求時重新獲取,即使它們提供了 `'force-cache'` 選項。 #### 跨路由區段行為 -- 單個路由中每個佈局和頁面設置的選項需要彼此兼容。 +- 單個路由的每個佈局和頁面中設置的選項需要相互兼容。 - 如果同時提供 `'only-cache'` 和 `'force-cache'`,則 `'force-cache'` 優先。如果同時提供 `'only-no-store'` 和 `'force-no-store'`,則 `'force-no-store'` 優先。強制選項會更改整個路由的行為,因此具有 `'force-*'` 的單個區段將防止由 `'only-*'` 引起的任何錯誤。 - `'only-*'` 和 `'force-*'` 選項的目的是保證整個路由要麼完全靜態,要麼完全動態。這意味著: - - 不允許在單個路由中同時使用 `'only-cache'` 和 `'only-no-store'`。 - - 不允許在單個路由中同時使用 `'force-cache'` 和 `'force-no-store'`。 + - 單個路由中不允許同時使用 `'only-cache'` 和 `'only-no-store'`。 + - 單個路由中不允許同時使用 `'force-cache'` 和 `'force-no-store'`。 - 如果子區段提供 `'auto'` 或 `'*-cache'`,則父區段不能提供 `'default-no-store'`,因為這可能使相同的 `fetch` 具有不同的行為。 - 通常建議將共享的父佈局保留為 `'auto'`,並在子區段分歧處自定義選項。 @@ -161,7 +160,7 @@ export const fetchCache = 'auto' ### `runtime` -我們建議使用 Node.js 運行時來渲染您的應用程序,並使用 Edge 運行時來處理中介軟體。 +我們建議使用 Node.js 運行時來渲染您的應用程式,並使用 Edge 運行時來處理中介軟體。 ```tsx filename="layout.tsx | page.tsx | route.ts" switcher export const runtime = 'nodejs' @@ -197,7 +196,7 @@ export const preferredRegion = 'auto' ### `maxDuration` -預設情況下,Next.js 不會限制伺服器端邏輯(渲染頁面或處理 API)的執行時間。 +預設情況下,Next.js 不限制伺服器端邏輯(渲染頁面或處理 API)的執行時間。 部署平台可以使用 Next.js 構建輸出中的 `maxDuration` 來添加特定的執行限制。 **注意**:此設置需要 Next.js `13.4.10` 或更高版本。 @@ -212,11 +211,11 @@ export const maxDuration = 5 > **須知**: > -> - 如果使用 [伺服器操作 (Server Actions)](/docs/app/building-your-application/data-fetching/server-actions-and-mutations),請在頁面層級設置 `maxDuration` 以更改頁面上使用的所有伺服器操作的預設超時時間。 +> - 如果使用 [伺服器操作](/docs/app/building-your-application/data-fetching/server-actions-and-mutations),請在頁面層級設置 `maxDuration` 以更改頁面上使用的所有伺服器操作的預設超時時間。 ### `generateStaticParams` -`generateStaticParams` 函數可與 [動態路由區段](/docs/app/api-reference/file-conventions/dynamic-routes) 結合使用,以定義在構建時靜態生成的路由區段參數列表,而不是在請求時按需生成。 +`generateStaticParams` 函數可以與 [動態路由區段](/docs/app/api-reference/file-conventions/dynamic-routes) 結合使用,以定義在構建時靜態生成的路由區段參數列表,而不是在請求時按需生成。 詳情請參閱 [API 參考](/docs/app/api-reference/functions/generate-static-params)。 @@ -224,4 +223,4 @@ export const maxDuration = 5 | 版本 | | | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `v15.0.0-RC` | 棄用 `export const runtime = "experimental-edge"`。提供 [代碼修改工具 (codemod)](/docs/app/guides/upgrading/codemods#transform-app-router-route-segment-config-runtime-value-from-experimental-edge-to-edge)。 | \ No newline at end of file +| `v15.0.0-RC` | `export const runtime = "experimental-edge"` 已棄用。提供了一個 [代碼修改工具](/docs/app/guides/upgrading/codemods#transform-app-router-route-segment-config-runtime-value-from-experimental-edge-to-edge)。 | \ No newline at end of file diff --git a/apps/docs/content/zh-hant/docs/01-app/05-api-reference/04-functions/use-report-web-vitals.mdx b/apps/docs/content/zh-hant/docs/01-app/05-api-reference/04-functions/use-report-web-vitals.mdx index 47f6a721..445a9549 100644 --- a/apps/docs/content/zh-hant/docs/01-app/05-api-reference/04-functions/use-report-web-vitals.mdx +++ b/apps/docs/content/zh-hant/docs/01-app/05-api-reference/04-functions/use-report-web-vitals.mdx @@ -1,23 +1,27 @@ --- -source-updated-at: 2025-05-19T22:31:51.000Z -translation-updated-at: 2025-05-25T20:54:22.048Z +source-updated-at: 2025-06-02T15:30:01.000Z +translation-updated-at: 2025-06-02T19:01:57.210Z title: useReportWebVitals -description: 關於 `useReportWebVitals` 函式的 API 參考文件。 +description: 關於 useReportWebVitals 函式的 API 參考文件。 --- -{/* 此文件內容在應用程式路由和頁面路由之間共享。您可以使用 `內容` 元件來新增特定於頁面路由的內容。任何共享內容不應包裹在元件中。 */} +{/* 此文件內容在 App 和 Pages 路由間共享。您可以使用 `內容` 元件來添加特定於 Pages 路由的內容。任何共享內容都不應包裹在元件中。 */} -`useReportWebVitals` 鉤子 (hook) 允許您回報 [核心網頁指標 (Core Web Vitals)](https://web.dev/vitals/),並可與您的分析服務結合使用。 +`useReportWebVitals` 鉤子允許您報告 [核心 Web 指標 (Core Web Vitals)](https://web.dev/vitals/),並可與您的分析服務結合使用。 + +傳遞給 `useReportWebVitals` 的新函式會使用當前可用的指標進行呼叫。為避免報告重複數據,請確保回調函式引用不變更(如下方程式碼範例所示)。 ```jsx filename="pages/_app.js" import { useReportWebVitals } from 'next/web-vitals' +const logWebVitals = (metric) => { + console.log(metric) +} + function MyApp({ Component, pageProps }) { - useReportWebVitals((metric) => { - console.log(metric) - }) + useReportWebVitals(logWebVitals) return } @@ -32,10 +36,12 @@ function MyApp({ Component, pageProps }) { import { useReportWebVitals } from 'next/web-vitals' +const logWebVitals = (metric) => { + console.log(metric) +} + export function WebVitals() { - useReportWebVitals((metric) => { - console.log(metric) - }) + useReportWebVitals(logWebVitals) return null } @@ -56,7 +62,7 @@ export default function Layout({ children }) { } ``` -> 由於 `useReportWebVitals` 鉤子需要 `'use client'` 指令,最有效率的做法是建立一個獨立的元件,並由根佈局 (layout) 匯入。這樣可以將客戶端邊界 (client boundary) 限制在 `WebVitals` 元件內。 +> 由於 `useReportWebVitals` 鉤子需要 `'use client'` 指令,最有效的方法是建立一個由根佈局導入的獨立元件。這樣可以將客戶端邊界僅限於 `WebVitals` 元件。 @@ -64,44 +70,46 @@ export default function Layout({ children }) { 作為鉤子參數傳遞的 `metric` 物件包含以下屬性: -- `id`:當前頁面載入情境中指標的唯一識別碼 -- `name`:效能指標的名稱。可能的值包括特定於網頁應用程式的 [網頁指標 (Web Vitals)](#web-vitals) 名稱(TTFB、FCP、LCP、FID、CLS) -- `delta`:當前值與前一個指標值之間的差異。該值通常以毫秒為單位,表示指標值隨時間的變化 -- `entries`:與指標相關聯的 [效能條目 (Performance Entries)](https://developer.mozilla.org/docs/Web/API/PerformanceEntry) 陣列。這些條目提供與指標相關的效能事件的詳細資訊 +- `id`:當前頁面加載上下文中指標的唯一識別碼 +- `name`:效能指標的名稱。可能的值包括特定於 Web 應用程式的 [Web 指標](#web-vitals) 名稱(TTFB、FCP、LCP、FID、CLS) +- `delta`:指標當前值與先前值的差異。該值通常以毫秒為單位,表示指標值隨時間的變化 +- `entries`:與指標相關聯的 [效能條目 (Performance Entries)](https://developer.mozilla.org/docs/Web/API/PerformanceEntry) 陣列。這些條目提供有關指標相關效能事件的詳細資訊 - `navigationType`:指示觸發指標收集的 [導航類型](https://developer.mozilla.org/docs/Web/API/PerformanceNavigationTiming/type)。可能的值包括 `"navigate"`、`"reload"`、`"back_forward"` 和 `"prerender"` -- `rating`:指標值的定性評級,提供效能評估。可能的值為 `"good"`、`"needs-improvement"` 和 `"poor"`。評級通常是透過將指標值與表示可接受或次優效能的預定義閾值進行比較來確定的 -- `value`:效能條目的實際值或持續時間,通常以毫秒為單位。該值提供了指標追蹤的效能方面的定量測量。值的來源取決於所測量的特定指標,可以來自各種 [效能 API (Performance API)](https://developer.mozilla.org/docs/Web/API/Performance_API) +- `rating`:指標值的定性評級,提供效能評估。可能的值為 `"good"`、`"needs-improvement"` 和 `"poor"`。評級通常是通過將指標值與指示可接受或次優效能的預定義閾值進行比較來確定的 +- `value`:效能條目的實際值或持續時間,通常以毫秒為單位。該值提供指標追蹤的效能方面的定量測量。值的來源取決於所測量的特定指標,可以來自各種 [效能 API (Performance API)](https://developer.mozilla.org/docs/Web/API/Performance_API) -## 網頁指標 (Web Vitals) +## Web 指標 -[網頁指標 (Web Vitals)](https://web.dev/vitals/) 是一組實用的指標,旨在捕捉網頁的使用者體驗。包含以下所有網頁指標: +[Web 指標 (Web Vitals)](https://web.dev/vitals/) 是一組有用的指標,旨在捕捉網頁的用戶體驗。包含以下所有 Web 指標: -- [首位元組時間 (Time to First Byte, TTFB)](https://developer.mozilla.org/docs/Glossary/Time_to_first_byte) -- [首次內容繪製 (First Contentful Paint, FCP)](https://developer.mozilla.org/docs/Glossary/First_contentful_paint) -- [最大內容繪製 (Largest Contentful Paint, LCP)](https://web.dev/lcp/) -- [首次輸入延遲 (First Input Delay, FID)](https://web.dev/fid/) -- [累計版面配置位移 (Cumulative Layout Shift, CLS)](https://web.dev/cls/) -- [下次繪製互動時間 (Interaction to Next Paint, INP)](https://web.dev/inp/) +- [首次位元組時間 (Time to First Byte)](https://developer.mozilla.org/docs/Glossary/Time_to_first_byte) (TTFB) +- [首次內容繪製 (First Contentful Paint)](https://developer.mozilla.org/docs/Glossary/First_contentful_paint) (FCP) +- [最大內容繪製 (Largest Contentful Paint)](https://web.dev/lcp/) (LCP) +- [首次輸入延遲 (First Input Delay)](https://web.dev/fid/) (FID) +- [累積版面配置位移 (Cumulative Layout Shift)](https://web.dev/cls/) (CLS) +- [互動到下一個繪製 (Interaction to Next Paint)](https://web.dev/inp/) (INP) -您可以使用 `name` 屬性處理所有這些指標的結果。 +您可以使用 `name` 屬性處理這些指標的所有結果。 ```jsx filename="pages/_app.js" import { useReportWebVitals } from 'next/web-vitals' -function MyApp({ Component, pageProps }) { - useReportWebVitals((metric) => { - switch (metric.name) { - case 'FCP': { - // 處理 FCP 結果 - } - case 'LCP': { - // 處理 LCP 結果 - } - // ... +const handleWebVitals = (metric) => { + switch (metric.name) { + case 'FCP': { + // 處理 FCP 結果 } - }) + case 'LCP': { + // 處理 LCP 結果 + } + // ... + } +} + +function MyApp({ Component, pageProps }) { + useReportWebVitals(handleWebVitals) return } @@ -116,18 +124,22 @@ function MyApp({ Component, pageProps }) { import { useReportWebVitals } from 'next/web-vitals' -export function WebVitals() { - useReportWebVitals((metric) => { - switch (metric.name) { - case 'FCP': { - // 處理 FCP 結果 - } - case 'LCP': { - // 處理 LCP 結果 - } - // ... +type ReportWebVitalsCallback = Parameters[0] + +const handleWebVitals: ReportWebVitalsCallback = (metric) => { + switch (metric.name) { + case 'FCP': { + // 處理 FCP 結果 + } + case 'LCP': { + // 處理 LCP 結果 } - }) + // ... + } +} + +export function WebVitals() { + useReportWebVitals(handleWebVitals) } ``` @@ -136,18 +148,20 @@ export function WebVitals() { import { useReportWebVitals } from 'next/web-vitals' -export function WebVitals() { - useReportWebVitals((metric) => { - switch (metric.name) { - case 'FCP': { - // 處理 FCP 結果 - } - case 'LCP': { - // 處理 LCP 結果 - } - // ... +const handleWebVitals = (metric) => { + switch (metric.name) { + case 'FCP': { + // 處理 FCP 結果 } - }) + case 'LCP': { + // 處理 LCP 結果 + } + // ... + } +} + +export function WebVitals() { + useReportWebVitals(handleWebVitals) } ``` @@ -163,42 +177,44 @@ export function WebVitals() { - `Next.js-route-change-to-render`:路由變更後頁面開始渲染所需的時間(以毫秒為單位) - `Next.js-render`:路由變更後頁面完成渲染所需的時間(以毫秒為單位) -您可以分別處理這些指標的結果: +您可以單獨處理這些指標的所有結果: ```jsx filename="pages/_app.js" import { useReportWebVitals } from 'next/web-vitals' +function handleCustomMetrics(metrics) { + switch (metric.name) { + case 'Next.js-hydration': + // 處理水合結果 + break + case 'Next.js-route-change-to-render': + // 處理路由變更到渲染的結果 + break + case 'Next.js-render': + // 處理渲染結果 + break + default: + break + } +} + function MyApp({ Component, pageProps }) { - useReportWebVitals((metric) => { - switch (metric.name) { - case 'Next.js-hydration': - // 處理水合結果 - break - case 'Next.js-route-change-to-render': - // 處理路由變更至渲染的結果 - break - case 'Next.js-render': - // 處理渲染結果 - break - default: - break - } - }) + useReportWebVitals(handleCustomMetrics) return } ``` -這些指標適用於所有支援 [使用者計時 API (User Timing API)](https://caniuse.com/#feat=user-timing) 的瀏覽器。 +這些指標適用於所有支援 [用戶計時 API (User Timing API)](https://caniuse.com/#feat=user-timing) 的瀏覽器。 -## 將結果傳送至外部系統 +## 將結果發送到外部系統 -您可以將結果傳送至任何端點,以測量和追蹤網站上的真實使用者效能。例如: +您可以將結果發送到任何端點以測量和追蹤您網站上的真實用戶效能。例如: ```js -useReportWebVitals((metric) => { +function postWebVitals(metrics) { const body = JSON.stringify(metric) const url = 'https://example.com/analytics' @@ -208,10 +224,12 @@ useReportWebVitals((metric) => { } else { fetch(url, { body, method: 'POST', keepalive: true }) } -}) +} + +useReportWebVitals(postWebVitals) ``` -> **須知**:如果您使用 [Google Analytics](https://analytics.google.com/analytics/web/),使用 `id` 值可以讓您手動建構指標分佈(以計算百分位數等) +> **須知**:如果您使用 [Google Analytics](https://analytics.google.com/analytics/web/),使用 `id` 值可以讓您手動構建指標分佈(以計算百分位數等) > ```js > useReportWebVitals(metric => { @@ -219,10 +237,10 @@ useReportWebVitals((metric) => { > // https://github.com/vercel/next.js/blob/canary/examples/with-google-analytics > window.gtag('event', metric.name, { > value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value), // 值必須為整數 -> event_label: metric.id, // 當前頁面載入的唯一識別碼 +> event_label: metric.id, // 當前頁面加載的唯一 id > non_interaction: true, // 避免影響跳出率 > }); > } > ``` > -> 閱讀更多關於 [將結果傳送至 Google Analytics](https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics) 的資訊。 \ No newline at end of file +> 閱讀更多關於 [將結果發送到 Google Analytics](https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics) 的資訊。 \ No newline at end of file diff --git a/apps/docs/content/zh-hant/learn/02-dashboard-app/12-mutating-data.mdx b/apps/docs/content/zh-hant/learn/02-dashboard-app/12-mutating-data.mdx index 01bc9037..4e89feab 100644 --- a/apps/docs/content/zh-hant/learn/02-dashboard-app/12-mutating-data.mdx +++ b/apps/docs/content/zh-hant/learn/02-dashboard-app/12-mutating-data.mdx @@ -1,9 +1,9 @@ --- -source-updated-at: 2025-05-29T18:05:49.000Z -translation-updated-at: 2025-05-29T20:08:29.321Z -title: 資料異動 -headline: 'App Router:資料異動' -description: '使用 React 伺服器動作 (Server Actions) 進行資料異動,並重新驗證 Next.js 快取。' +source-updated-at: 2025-06-02T15:30:01.000Z +translation-updated-at: 2025-06-02T19:05:17.483Z +title: 資料變更 +headline: 'App Router:資料變更' +description: '使用 React 伺服器動作 (Server Actions) 變更資料,並重新驗證 Next.js 快取。' image: 'https://nextjs.org/api/learn-og?title=Mutating%20Data&chapter=12' --- @@ -12,14 +12,14 @@ image: 'https://nextjs.org/api/learn-og?title=Mutating%20Data&chapter=12' [什麼是伺服器動作 (Server Actions)?](#what-are-server-actions) ---------------------------------------------------- -React 伺服器動作 (Server Actions) 讓您可以直接在伺服器上執行非同步程式碼。它們消除了建立 API 端點來異動資料的需求,取而代之的是您可以編寫在伺服器上執行的非同步函數,並從您的客戶端或伺服器元件呼叫。 +React 伺服器動作 (Server Actions) 讓您可以直接在伺服器上執行非同步程式碼。它們消除了建立 API 端點來變更資料的需求,取而代之的是您可以撰寫在伺服器上執行的非同步函數,並從您的客戶端或伺服器元件呼叫。 -安全性是網頁應用的首要考量,因為它們可能面臨各種威脅。這就是伺服器動作的用武之地。它們包含了加密閉包、嚴格輸入檢查、錯誤訊息雜湊、主機限制等功能,共同大幅提升您的應用程式安全性。 +安全性是網頁應用程式的首要考量,因為它們可能面臨各種威脅。這就是伺服器動作的用武之地。它們包含加密閉包、嚴格輸入檢查、錯誤訊息雜湊、主機限制等功能,共同顯著提升您的應用程式安全性。 [在表單中使用伺服器動作](#using-forms-with-server-actions) ------------------------------------------------------------------- -在 React 中,您可以使用 `
` 元素中的 `action` 屬性來呼叫動作。該動作會自動接收原生的 [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) 物件,其中包含捕獲的資料。 +在 React 中,您可以使用 `` 元素中的 `action` 屬性來呼叫動作。該動作會自動接收原生的 [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) 物件,其中包含擷取的資料。 例如: @@ -30,7 +30,7 @@ export default function Page() { async function create(formData: FormData) { 'use server'; - // 異動資料的邏輯... + // 變更資料的邏輯... } // 使用 "action" 屬性呼叫動作 @@ -38,30 +38,30 @@ export default function Page() { } ``` -在伺服器元件中呼叫伺服器動作的一個優勢是漸進增強 (progressive enhancement) — 即使客戶端尚未載入 JavaScript,表單仍能運作。例如,在網路連線較慢的情況下。 +在伺服器元件中呼叫伺服器動作的一個優勢是漸進增強 (progressive enhancement) — 即使在客戶端尚未載入 JavaScript 的情況下,表單仍然可以運作。例如,在網路連線較慢的情況下。 [Next.js 與伺服器動作](#nextjs-with-server-actions) ---------------------------------------------------------- -伺服器動作也與 Next.js [快取](https://nextjs.org/docs/app/building-your-application/caching)深度整合。當透過伺服器動作提交表單時,您不僅可以使用該動作來異動資料,還可以使用 `revalidatePath` 和 `revalidateTag` 等 API 重新驗證相關的快取。 +伺服器動作也與 Next.js [快取](https://nextjs.org/docs/app/building-your-application/caching)深度整合。當透過伺服器動作提交表單時,您不僅可以使用該動作來變更資料,還可以使用 `revalidatePath` 和 `revalidateTag` 等 API 重新驗證相關的快取。 -讓我們看看這一切如何協同運作! +讓我們看看這一切是如何協同工作的! [建立發票](#creating-an-invoice) ------------------------------------------- 以下是建立新發票的步驟: -1. 建立一個表單來捕獲使用者輸入。 -2. 建立一個伺服器動作並從表單中呼叫它。 -3. 在伺服器動作中,從 `formData` 物件提取資料。 +1. 建立表單來擷取使用者輸入。 +2. 建立伺服器動作並從表單呼叫它。 +3. 在您的伺服器動作中,從 `formData` 物件提取資料。 4. 驗證並準備要插入資料庫的資料。 5. 插入資料並處理任何錯誤。 6. 重新驗證快取並將使用者重定向回發票頁面。 ### [1\. 建立新路由和表單](#1-create-a-new-route-and-form) -首先,在 `/invoices` 資料夾中,新增一個名為 `/create` 的路由段,並在其中建立一個 `page.tsx` 檔案: +首先,在 `/invoices` 資料夾中,新增一個名為 `/create` 的路由區段,並在其中建立 `page.tsx` 檔案: 包含嵌套 create 資料夾的 Invoices 資料夾,其中有一個 page.tsx 檔案 ` 元件。為了節省時間,我們已經為您建立了 `` 元件。 +您的頁面是一個伺服器元件,它會擷取 `customers` 並將其傳遞給 `` 元件。為了節省時間,我們已經為您建立了 `` 元件。 導航到 `` 元件,您會看到表單: -* 有一個 `` 元素,其 `type="number"`。 -* 有兩個用於狀態的 `` 元素,其 `type="radio"`。 -* 有一個 `type="submit"` 的按鈕。 +* 有一個 `` 元素用於輸入**金額**,其 `type="number"`。 +* 有兩個 `` 元素用於狀態,其 `type="radio"`。 +* 有一個按鈕,其 `type="submit"`。 在 [http://localhost:3000/dashboard/invoices/create](http://localhost:3000/dashboard/invoices/create),您應該會看到以下使用者介面: @@ -128,11 +128,11 @@ export default async function Page() { 'use server'; ``` -透過新增 `'use server'`,您將檔案中的所有導出函數標記為伺服器動作。這些伺服器函數可以隨後匯入並在客戶端和伺服器元件中使用。此檔案中包含的任何未使用的函數將自動從最終應用程式套件中移除。 +透過新增 `'use server'`,您將檔案中所有匯出的函數標記為伺服器動作。這些伺服器函數可以被匯入並在客戶端和伺服器元件中使用。此檔案中包含的任何未使用的函數都會自動從最終應用程式套件中移除。 -您也可以透過在動作中新增 `"use server"` 直接在伺服器元件中編寫伺服器動作。但在本課程中,我們將把它們全部組織在一個單獨的檔案中。我們建議為您的動作建立一個單獨的檔案。 +您也可以透過在動作中新增 `"use server"` 直接在伺服器元件中撰寫伺服器動作。但在本課程中,我們會將它們全部組織在一個單獨的檔案中。我們建議為您的動作使用一個單獨的檔案。 -在您的 `actions.ts` 檔案中,建立一個接受 `formData` 的新非同步函數: +在您的 `actions.ts` 檔案中,建立一個新的非同步函數,該函數接受 `formData`: ```ts {3} filename="/app/lib/actions.ts" 'use server'; @@ -140,7 +140,7 @@ export default async function Page() { export async function createInvoice(formData: FormData) {} ``` -然後,在您的 `` 元件中,從 `actions.ts` 檔案匯入 `createInvoice`。在 `` 元素中新增一個 `action` 屬性,並呼叫 `createInvoice` 動作。 +然後,在您的 `` 元件中,從 `actions.ts` 檔案匯入 `createInvoice`。在 `` 元素中新增 `action` 屬性,並呼叫 `createInvoice` 動作。 ```tsx {10,18} filename="/app/ui/invoices/create-form.tsx" import { CustomerField } from '@/app/lib/definitions'; @@ -166,15 +166,15 @@ export default function Form({ } ``` -> **小提示**:在 HTML 中,您會將 URL 傳遞給 `action` 屬性。此 URL 是表單資料應提交的目的地(通常是 API 端點)。 +> **好處說明**:在 HTML 中,您會將 URL 傳遞給 `action` 屬性。此 URL 是表單資料應提交的目的地(通常是 API 端點)。 > -> 然而,在 React 中,`action` 屬性被視為一個特殊屬性 — 意味著 React 在其基礎上進行擴展,以允許呼叫動作。 +> 然而,在 React 中,`action` 屬性被視為一個特殊的 prop — 意味著 React 在其基礎上進行擴充,允許呼叫動作。 > > 在幕後,伺服器動作會建立一個 `POST` API 端點。這就是為什麼在使用伺服器動作時不需要手動建立 API 端點。 ### [3\. 從 `formData` 提取資料](#3-extract-the-data-from-formdata) -回到您的 `actions.ts` 檔案,您需要從 `formData` 中提取值,有[幾種方法](https://developer.mozilla.org/en-US/docs/Web/API/FormData)可以使用。對於此範例,我們將使用 [`.get(name)`](https://developer.mozilla.org/en-US/docs/Web/API/FormData/get) 方法。 +回到您的 `actions.ts` 檔案,您需要從 `formData` 中提取值,有[幾種方法](https://developer.mozilla.org/en-US/docs/Web/API/FormData)可以使用。在此範例中,我們將使用 [`.get(name)`](https://developer.mozilla.org/en-US/docs/Web/API/FormData/get) 方法。 ```ts {3,4,5,6,7,8,9,10} filename="/app/lib/actions.ts" 'use server'; @@ -190,15 +190,15 @@ export async function createInvoice(formData: FormData) { } ``` -> **提示**:如果您正在處理具有許多欄位的表單,可以考慮使用 [`entries()`](https://developer.mozilla.org/en-US/docs/Web/API/FormData/entries) 方法與 JavaScript 的 [`Object.fromEntries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries)。 +> **提示**:如果您正在處理具有許多欄位的表單,您可能需要考慮使用 [`entries()`](https://developer.mozilla.org/en-US/docs/Web/API/FormData/entries) 方法與 JavaScript 的 [`Object.fromEntries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries)。 -為了確認一切連接正確,請嘗試使用表單。提交後,您應該會在**終端機**(而非瀏覽器)中看到您剛剛輸入到表單中的資料。 +要檢查一切是否正確連接,請試用表單。提交後,您應該會在**終端機**(而非瀏覽器)中看到您剛剛輸入到表單中的資料記錄。 -現在您的資料以物件的形式呈現,處理起來會更加容易。 +現在您的資料以物件的形式存在,處理起來會容易得多。 ### [4\. 驗證並準備資料](#4-validate-and-prepare-the-data) -在將表單資料發送到資料庫之前,您需要確保其格式和類型正確。如果您還記得課程前面的內容,您的發票表期望資料以下列格式呈現: +在將表單資料傳送到資料庫之前,您需要確保其格式和類型正確。如果您記得課程前面的內容,您的發票表期望的資料格式如下: ```ts filename="/app/lib/definitions.ts" export type Invoice = { @@ -212,19 +212,19 @@ export type Invoice = { 到目前為止,您只有來自表單的 `customer_id`、`amount` 和 `status`。 -#### [類型驗證與強制轉換](#type-validation-and-coercion) +#### [類型驗證和強制轉換](#type-validation-and-coercion) -驗證表單資料與資料庫中預期的類型是否一致非常重要。例如,如果您在動作中新增一個 `console.log`: +驗證表單資料與資料庫中預期的類型是否一致非常重要。例如,如果您在動作中新增 `console.log`: ``` console.log(typeof rawFormData.amount); ``` -您會注意到 `amount` 的類型是 `string` 而非 `number`。這是因為 `type="number"` 的 `input` 元素實際上返回的是字串,而非數字! +您會注意到 `amount` 的類型是 `string` 而非 `number`。這是因為 `type="number"` 的 `input` 元素實際上會回傳字串,而非數字! -要處理類型驗證,您有幾個選項。雖然您可以手動驗證類型,但使用類型驗證函式庫可以節省您的時間和精力。對於您的範例,我們將使用 [Zod](https://zod.dev/),這是一個 TypeScript 優先的驗證函式庫,可以簡化此任務。 +要處理類型驗證,您有幾個選擇。雖然您可以手動驗證類型,但使用類型驗證函式庫可以節省您的時間和精力。在此範例中,我們將使用 [Zod](https://zod.dev/),這是一個 TypeScript 優先的驗證函式庫,可以簡化此任務。 -在您的 `actions.ts` 檔案中,匯入 Zod 並定義一個與表單物件形狀相符的架構 (schema)。此架構將在將 `formData` 儲存到資料庫之前對其進行驗證。 +在您的 `actions.ts` 檔案中,匯入 Zod 並定義一個與表單物件形狀相符的結構描述。此結構描述將在將 `formData` 儲存到資料庫之前對其進行驗證。 ```ts {3,5,6,7,8,9,10,11,13} filename="/app/lib/actions.ts" 'use server'; @@ -246,7 +246,7 @@ export async function createInvoice(formData: FormData) { } ``` -`amount` 欄位專門設定為從字串強制轉換 (coerce) 為數字,同時驗證其類型。 +`amount` 欄位專門設定為從字串強制轉換為數字,同時驗證其類型。 然後,您可以將 `rawFormData` 傳遞給 `CreateInvoice` 來驗證類型: @@ -263,7 +263,7 @@ export async function createInvoice(formData: FormData) { #### [以分為單位儲存值](#storing-values-in-cents) -通常建議在資料庫中以分為單位儲存貨幣值,以避免 JavaScript 浮點數誤差並確保更高的準確性。 +通常,良好的做法是在資料庫中以分為單位儲存貨幣值,以避免 JavaScript 浮點數錯誤並確保更高的準確性。 讓我們將金額轉換為分: @@ -298,9 +298,9 @@ export async function createInvoice(formData: FormData) { ### [5\. 將資料插入資料庫](#5-inserting-the-data-into-your-database) -現在您擁有了資料庫所需的所有值,您可以建立一個 SQL 查詢來將新發票插入資料庫並傳遞變數: +現在您擁有資料庫所需的所有值,您可以建立一個 SQL 查詢來將新發票插入資料庫並傳入變數: -```ts {2,15,16,17,18} filename="/app/lib/actions.ts" +```ts {2,4,17,18,19,20} filename="/app/lib/actions.ts" import { z } from 'zod'; import postgres from 'postgres'; @@ -324,13 +324,13 @@ export async function createInvoice(formData: FormData) { } ``` -目前,我們沒有處理任何錯誤。我們將在下一章討論這一點。現在,讓我們繼續下一步。 +目前,我們沒有處理任何錯誤。我們將在下一章討論這一點。現在,讓我們繼續進行下一步。 ### [6. 重新驗證與重新導向](#6-revalidate-and-redirect) -Next.js 有一個客戶端路由快取,會將路由片段暫時儲存在使用者的瀏覽器中。結合[預取功能 (prefetching)](/docs/app/building-your-application/routing/linking-and-navigating#1-prefetching),這個快取能確保使用者在路由間快速導航,同時減少向伺服器發送的請求數量。 +Next.js 有一個客戶端路由快取 (client-side router cache),會將路由片段暫存在使用者的瀏覽器中一段時間。結合[預取 (prefetching)](/docs/app/building-your-application/routing/linking-and-navigating#1-prefetching) 功能,這個快取機制能確保使用者在路由間快速導航,同時減少向伺服器發送的請求數量。 -由於您正在更新發票路由中顯示的資料,您會希望清除這個快取並觸發向伺服器的新請求。您可以使用 Next.js 的 [`revalidatePath`](/docs/app/api-reference/functions/revalidatePath) 函式來達成: +由於您正在更新發票路由中顯示的資料,您會希望清除這個快取並觸發一個新的伺服器請求。您可以使用 Next.js 的 [`revalidatePath`](/docs/app/api-reference/functions/revalidatePath) 函式來達成: ```ts {4,25} filename="/app/lib/actions.ts" 'use server'; @@ -361,11 +361,11 @@ export async function createInvoice(formData: FormData) { } ``` -當資料庫更新後,`/dashboard/invoices` 路徑將會被重新驗證,並從伺服器取得最新資料。 +當資料庫更新完成後,`/dashboard/invoices` 路徑將會被重新驗證,並從伺服器取得最新的資料。 此時,您也會希望將使用者重新導向回 `/dashboard/invoices` 頁面。您可以使用 Next.js 的 [`redirect`](/docs/app/api-reference/functions/redirect) 函式來達成: -```ts {6,14} filename="/app/lib/actions.ts" +```ts {5,16} filename="/app/lib/actions.ts" 'use server'; import { z } from 'zod'; @@ -385,32 +385,32 @@ export async function createInvoice(formData: FormData) { } ``` -恭喜!您剛剛完成了第一個伺服器動作 (Server Action) 的實作。請透過新增一筆發票來測試功能是否正常運作: +恭喜!您剛剛實作了第一個伺服器動作 (Server Action)。試著新增一張發票來測試看看,如果一切運作正常: -1. 提交後您應該會被重新導向至 `/dashboard/invoices` 路由。 -2. 您應該會在表格頂部看到新增的發票。 +1. 提交後您應該會被重新導向到 `/dashboard/invoices` 路由。 +2. 您應該會在表格頂端看到新增的發票。 [更新發票](#updating-an-invoice) ------------------------------------------- -更新發票的表單與建立發票的表單類似,但您需要傳遞發票的 `id` 來更新資料庫中的記錄。讓我們看看如何取得並傳遞發票的 `id`。 +更新發票的表單與建立發票的表單類似,只是您需要傳遞發票的 `id` 來更新資料庫中的記錄。讓我們看看如何取得並傳遞發票的 `id`。 以下是更新發票的步驟: -1. 使用發票 `id` 建立新的動態路由片段。 -2. 從頁面參數 (params) 讀取發票 `id`。 -3. 從資料庫中取得特定的發票資料。 -4. 預先填寫表單中的發票資料。 +1. 建立一個帶有發票 `id` 的新動態路由片段 (dynamic route segment)。 +2. 從頁面參數 (page params) 讀取發票 `id`。 +3. 從資料庫中取得特定的發票。 +4. 預先填入表單中的發票資料。 5. 更新資料庫中的發票資料。 -### [1. 使用發票 `id` 建立動態路由片段](#1-create-a-dynamic-route-segment-with-the-invoice-id) +### [1. 建立帶有發票 `id` 的動態路由片段](#1-create-a-dynamic-route-segment-with-the-invoice-id) -當您不確定確切的路由片段名稱,並希望根據資料建立路由時,Next.js 允許您建立[動態路由片段 (Dynamic Route Segments)](/docs/app/building-your-application/routing/dynamic-routes)。這可以是部落格文章標題、產品頁面等。您可以透過將資料夾名稱用方括號包起來來建立動態路由片段,例如 `[id]`、`[post]` 或 `[slug]`。 +Next.js 允許您在不知道確切片段名稱時建立[動態路由片段 (Dynamic Route Segments)](/docs/app/building-your-application/routing/dynamic-routes),並根據資料建立路由。這可以用於部落格文章標題、產品頁面等。您可以透過將資料夾名稱用方括號包起來來建立動態路由片段,例如 `[id]`、`[post]` 或 `[slug]`。 -在您的 `/invoices` 資料夾中,建立一個名為 `[id]` 的新動態路由,然後在其中建立一個名為 `edit` 的路由,並新增一個 `page.tsx` 檔案。您的檔案結構應如下所示: +在您的 `/invoices` 資料夾中,建立一個名為 `[id]` 的新動態路由,然後在裡面建立一個名為 `edit` 的路由,並新增一個 `page.tsx` 檔案。您的檔案結構應該如下: Invoices folder with a nested [id] folder, and an edit folder inside it` 元件,並更新 `Link` 的 `href` 以接受 `id` prop。您可以使用模板字面值 (template literals) 來連結至動態路由片段: +導覽到您的 `` 元件,並更新 `Link` 的 `href` 以接受 `id` prop。您可以使用模板字面值 (template literals) 來連結到動態路由片段: ```tsx {9} filename="/app/ui/invoices/buttons.tsx" import { PencilIcon, PlusIcon, TrashIcon } from '@heroicons/react/24/outline'; @@ -458,7 +458,7 @@ export function UpdateInvoice({ id }: { id: string }) { } ``` -### [2. 從頁面參數 (params) 讀取發票 `id`](#2-read-the-invoice-id-from-page-params) +### [2. 從頁面 `params` 讀取發票 `id`](#2-read-the-invoice-id-from-page-params) 回到您的 `` 元件,貼上以下程式碼: @@ -472,9 +472,9 @@ export default async function Page() {
` 元件以接收這個 prop: +除了 `searchParams` 之外,頁面元件也接受一個名為 `params` 的 prop,您可以用它來存取 `id`。更新您的 `` 元件以接收這個 prop: -```tsx {5,6} filename="/app/dashboard/invoices/[id]/edit/page.tsx" +```tsx {5,6,7} filename="/app/dashboard/invoices/[id]/edit/page.tsx" import Form from '@/app/ui/invoices/edit-form'; import Breadcrumbs from '@/app/ui/invoices/breadcrumbs'; import { fetchCustomers } from '@/app/lib/data'; @@ -502,7 +502,7 @@ export default async function Page(props: { params: Promise<{ id: string }> }) { } ``` -### [3. 取得特定的發票](#3-fetch-the-specific-invoice) +### [3. 取得特定發票](#3-fetch-the-specific-invoice) 接著: @@ -527,12 +527,12 @@ export default async function Page(props: { params: Promise<{ id: string }> }) { } ``` -您會在終端機中看到一個暫時的 TypeScript 錯誤,因為 `invoice` prop 可能為 `undefined`。現在不用擔心,您將在下一章新增錯誤處理時解決這個問題。 +您會在終端機中看到一個暫時的 TypeScript 錯誤,針對 `invoice` prop,因為 `invoice` 可能為 `undefined`。現在不用擔心,您將在下一章節加入錯誤處理時解決這個問題。 -太好了!現在,測試所有功能是否正確連接。請訪問 [http://localhost:3000/dashboard/invoices](http://localhost:3000/dashboard/invoices) 並點擊鉛筆圖標來編輯一筆發票。導航後,您應該會看到一個預先填寫了發票詳細資料的表單: +太好了!現在測試一下所有功能是否正確連接。造訪 [http://localhost:3000/dashboard/invoices](http://localhost:3000/dashboard/invoices) 並點擊鉛筆圖示來編輯一張發票。導覽後,您應該會看到一個預先填入發票詳細資料的表單: Edit invoices page with breadcrumbs and form }) { > **UUID 與自動遞增鍵的比較** > -> 我們使用 UUID 而不是遞增鍵(例如 1、2、3 等)。這會使網址變長;然而,UUID 消除了 ID 衝突的風險,具有全域唯一性,並降低了枚舉攻擊 (enumeration attacks) 的風險,這使得它們非常適合大型資料庫。 +> 我們使用 UUID 而非遞增鍵(例如 1、2、3 等)。這會讓網址變長;然而,UUID 消除了 ID 衝突的風險,具有全域唯一性,並降低了枚舉攻擊 (enumeration attacks) 的風險,這使得它們非常適合大型資料庫。 > -> 但是,如果您偏好更簡潔的網址,您可能會傾向使用自動遞增鍵。 +> 不過,如果您偏好更簡潔的網址,您可能會傾向使用自動遞增鍵。 -### [4. 將 `id` 傳遞給伺服器動作 (Server Action)](#4-pass-the-id-to-the-server-action) +### [4. 將 `id` 傳遞給伺服器動作](#4-pass-the-id-to-the-server-action) -最後,您會希望將 `id` 傳遞給伺服器動作,以便更新資料庫中的正確記錄。您**不能**像這樣將 `id` 作為引數傳遞: +最後,您會希望將 `id` 傳遞給伺服器動作,這樣您才能更新資料庫中正確的記錄。您**不能**像這樣將 `id` 作為引數傳遞: ```tsx filename="/app/ui/invoices/edit-form.tsx" -// 將 id 作為引數傳遞是無效的 +// 將 id 作為引數傳遞是行不通的 ``` @@ -575,7 +575,7 @@ export default function EditInvoiceForm({ } ``` -> **注意:** 在表單中使用隱藏輸入欄位也是可行的(例如 ``)。然而,這些值會以純文字形式出現在 HTML 原始碼中,這對於敏感資料來說並不理想。 +> **注意:**在表單中使用隱藏的 input 欄位也可以達成(例如 ``)。然而,這些值會以純文字形式出現在 HTML 原始碼中,這對於敏感資料來說並不理想。 接著,在您的 `actions.ts` 檔案中,建立一個新的動作 `updateInvoice`: @@ -611,15 +611,15 @@ export async function updateInvoice(id: string, formData: FormData) { 2. 使用 Zod 驗證型別。 3. 將金額轉換為分。 4. 將變數傳遞給您的 SQL 查詢。 -5. 呼叫 `revalidatePath` 以清除客戶端快取並發起新的伺服器請求。 -6. 呼叫 `redirect` 以將使用者重新導向至發票頁面。 +5. 呼叫 `revalidatePath` 來清除客戶端快取並發起新的伺服器請求。 +6. 呼叫 `redirect` 來將使用者重新導向到發票頁面。 -請透過編輯一筆發票來測試功能。提交表單後,您應該會被重新導向至發票頁面,且發票應該會被更新。 +試著編輯一張發票來測試看看。提交表單後,您應該會被重新導向到發票頁面,且發票應該會被更新。 [刪除發票](#deleting-an-invoice) ------------------------------------------- -要使用伺服器動作刪除一筆發票,請將刪除按鈕包裝在 `` 元素中,並使用 `bind` 將 `id` 傳遞給伺服器動作: +要使用伺服器動作來刪除發票,請將刪除按鈕包在 `` 元素中,並使用 `bind` 將 `id` 傳遞給伺服器動作: ```tsx {1,6,9} filename="/app/ui/invoices/buttons.tsx" import { deleteInvoice } from '@/app/lib/actions'; @@ -632,7 +632,7 @@ export function DeleteInvoice({ id }: { id: string }) { return ( @@ -654,6 +654,6 @@ export async function deleteInvoice(id: string) { [延伸閱讀](#further-reading) ----------------------------------- -在本章中,您學習了如何使用伺服器動作來變更資料。您也學習了如何使用 `revalidatePath` API 來重新驗證 Next.js 快取,以及使用 `redirect` 來將使用者重新導向至新頁面。 +在本章節中,您學會了如何使用伺服器動作來變更資料。您也學會了如何使用 `revalidatePath` API 來重新驗證 Next.js 快取,以及使用 `redirect` 來將使用者重新導向到新頁面。 -您也可以閱讀更多關於[伺服器動作的安全性](https://nextjs.org/blog/security-nextjs-server-components-actions)以進一步學習。 +您也可以閱讀更多關於[伺服器動作的安全性](https://nextjs.org/blog/security-nextjs-server-components-actions)來進一步學習。