diff --git a/apps/docs/content/zh-hans/docs/01-app/01-getting-started/01-installation.mdx b/apps/docs/content/zh-hans/docs/01-app/01-getting-started/01-installation.mdx
index 8af0dbae..affe04e2 100644
--- a/apps/docs/content/zh-hans/docs/01-app/01-getting-started/01-installation.mdx
+++ b/apps/docs/content/zh-hans/docs/01-app/01-getting-started/01-installation.mdx
@@ -1,23 +1,23 @@
---
-source-updated-at: '2025-05-16T04:52:11.000Z'
-translation-updated-at: '2025-05-17T02:12:53.432Z'
+source-updated-at: 2025-05-21T18:33:43.000Z
+translation-updated-at: 2025-05-21T19:18:02.138Z
title: 如何新建 Next.js 项目
nav_title: 安装指南
description: 使用 `create-next-app` CLI 创建新的 Next.js 应用,并配置 TypeScript、ESLint 和模块路径别名。
---
-{/* 本文档内容在应用路由和页面路由间共享。可使用 `内容` 组件添加仅适用于页面路由的内容。任何共享内容不应包裹在组件中。*/}
+{/* 本文档内容在应用路由和页面路由间共享。如需添加仅适用于页面路由的内容,可使用 `内容` 组件。共享内容不应包裹在任何组件中。*/}
## 系统要求
开始前请确保系统满足以下要求:
- [Node.js 18.18](https://nodejs.org/) 或更高版本
-- macOS、Windows(含 WSL)或 Linux
+- macOS、Windows(含 WSL)或 Linux 系统
## 自动安装
-创建 Next.js 应用最快的方式是使用 [`create-next-app`](/docs/app/api-reference/cli/create-next-app),它会自动完成所有设置。运行以下命令创建项目:
+创建 Next.js 应用最快的方式是使用 [`create-next-app`](/docs/app/api-reference/cli/create-next-app),它会自动完成所有配置。运行以下命令创建项目:
```bash filename="终端"
npx create-next-app@latest
@@ -26,18 +26,18 @@ npx create-next-app@latest
安装过程中会看到以下提示:
```txt filename="终端"
-项目名称是什么?my-app
+请输入项目名称?my-app
是否使用 TypeScript?否 / 是
是否使用 ESLint?否 / 是
是否使用 Tailwind CSS?否 / 是
-是否将代码放在 `src/` 目录中?否 / 是
-是否使用 App Router?(推荐)否 / 是
+是否将代码放在 `src/` 目录下?否 / 是
+是否使用应用路由(推荐)?否 / 是
是否在 `next dev` 中使用 Turbopack?否 / 是
是否自定义导入别名(默认为 `@/*`)?否 / 是
-要配置什么导入别名?@/*
+请输入配置的导入别名?@/*
```
-完成提示后,[`create-next-app`](/docs/app/api-reference/cli/create-next-app) 会创建以项目名命名的文件夹并安装所需依赖。
+完成提示后,[`create-next-app`](/docs/app/api-reference/cli/create-next-app) 会创建项目文件夹并安装所需依赖。
## 手动安装
@@ -65,7 +65,7 @@ npm install next@latest react@latest react-dom@latest
- `next dev`:启动开发服务器
- `next build`:构建生产环境应用
- `next start`:启动生产服务器
-- `next lint`:运行 ESLint
+- `next lint`:运行 ESLint 检查
@@ -103,20 +103,20 @@ export default function RootLayout({ children }) {
```tsx filename="app/page.tsx" switcher
export default function Page() {
- return Hello, Next.js!
+ return 你好,Next.js!
}
```
```jsx filename="app/page.js" switcher
export default function Page() {
- return Hello, Next.js!
+ return 你好,Next.js!
}
```
-当用户访问应用根路径 (`/`) 时,`layout.tsx` 和 `page.tsx` 会被渲染。
+当用户访问应用根路径 (`/`) 时,`layout.tsx` 和 `page.tsx` 将同时渲染。
**须知**:
>
-> - 如果忘记创建根布局,运行 `next dev` 启动开发服务器时 Next.js 会自动创建该文件
-> - 可选择在项目根目录使用 [`src` 文件夹](/docs/app/api-reference/file-conventions/src-folder) 来分离应用代码和配置文件
+> - 如果忘记创建根布局,运行 `next dev` 启动开发服务器时 Next.js 会自动生成
+> - 可选择使用项目根目录下的 [`src` 文件夹](/docs/app/api-reference/file-conventions/src-folder) 来分离应用代码和配置文件
@@ -136,21 +136,21 @@ export default function Page() {
Next.js 使用文件系统路由,意味着应用路由由文件结构决定。
-在项目根目录创建 `pages` 文件夹,然后添加 `index.tsx` 文件作为首页 (`/`):
+在项目根目录创建 `pages` 文件夹,然后添加 `index.tsx` 作为首页 (`/`):
```tsx filename="pages/index.tsx" switcher
export default function Page() {
- return
Hello, Next.js!
+ return 你好,Next.js!
}
```
```jsx filename="pages/index.js" switcher
export default function Page() {
- return Hello, Next.js!
+ return 你好,Next.js!
}
```
-接着在 `pages/` 中添加 `_app.tsx` 文件定义全局布局。了解更多关于[自定义 App 文件](/docs/pages/building-your-application/routing/custom-app)。
+接着在 `pages/` 下添加 `_app.tsx` 定义全局布局。了解更多关于[自定义 App 文件](/docs/pages/building-your-application/routing/custom-app)。
```tsx filename="pages/_app.tsx" switcher
import type { AppProps } from 'next/app'
@@ -166,7 +166,7 @@ export default function App({ Component, pageProps }) {
}
```
-最后在 `pages/` 中添加 `_document.tsx` 文件控制服务器初始响应。了解更多关于[自定义 Document 文件](/docs/pages/building-your-application/routing/custom-document)。
+最后在 `pages/` 下添加 `_document.tsx` 控制服务器初始响应。了解更多关于[自定义 Document 文件](/docs/pages/building-your-application/routing/custom-document)。
```tsx filename="pages/_document.tsx" switcher
import { Html, Head, Main, NextScript } from 'next/document'
@@ -204,7 +204,7 @@ export default function Document() {
### 创建 `public` 文件夹(可选)
-在项目根目录创建 [`public` 文件夹](/docs/app/api-reference/file-conventions/public-folder) 存放图片、字体等静态资源。`public` 内的文件可通过根路径 (`/`) 引用。
+在项目根目录创建 [`public` 文件夹](/docs/app/api-reference/file-conventions/public-folder) 存放静态资源(如图片、字体等)。`public` 中的文件可通过根路径 (`/`) 引用。
例如 `public/profile.png` 可引用为 `/profile.png`:
@@ -212,7 +212,7 @@ export default function Document() {
import Image from 'next/image'
export default function Page() {
- return
+ return
}
```
@@ -220,33 +220,33 @@ export default function Page() {
import Image from 'next/image'
export default function Page() {
- return
+ return
}
```
## 运行开发服务器
-1. 运行 `npm run dev` 启动开发服务器
+1. 执行 `npm run dev` 启动开发服务器
2. 访问 `http://localhost:3000` 查看应用
-3. 编辑`app/page.tsx` `pages/index.tsx` 文件并保存,浏览器中即可看到更新结果
+3. 编辑 `app/page.tsx``pages/index.tsx` 文件并保存,浏览器中即可看到更新结果
## 配置 TypeScript
-> 最低 TypeScript 版本要求:`v4.5.2`
+> TypeScript 最低版本要求:`v4.5.2`
-Next.js 内置 TypeScript 支持。要添加 TypeScript 到项目,只需将文件重命名为 `.ts` / `.tsx` 并运行 `next dev`。Next.js 会自动安装必要依赖并添加包含推荐配置的 `tsconfig.json` 文件。
+Next.js 内置 TypeScript 支持。要将 TypeScript 添加到项目,只需将文件重命名为 `.ts`/`.tsx` 并运行 `next dev`。Next.js 会自动安装必要依赖并创建包含推荐配置的 `tsconfig.json` 文件。
### IDE 插件
-Next.js 包含自定义 TypeScript 插件和类型检查器,可供 VSCode 等代码编辑器实现高级类型检查和自动补全。
+Next.js 包含自定义 TypeScript 插件和类型检查器,可供 VSCode 等编辑器实现高级类型检查和自动补全。
在 VS Code 中启用插件:
1. 打开命令面板 (`Ctrl/⌘` + `Shift` + `P`)
-2. 搜索 "TypeScript: Select TypeScript Version"
-3. 选择 "Use Workspace Version"
+2. 搜索 "TypeScript: 选择 TypeScript 版本"
+3. 选择 "使用工作区版本"
-更多信息请参考 [TypeScript 参考文档](/docs/app/api-reference/config/next-config-js/typescript)。
+更多信息请参考 [TypeScript 配置文档](/docs/app/api-reference/config/next-config-js/typescript)。
## 配置 ESLint
-Next.js 内置 ESLint 支持。使用 `create-next-app` 创建新项目时会自动安装必要包并配置正确设置。
+Next.js 内置 ESLint 支持。使用 `create-next-app` 创建项目时会自动安装必要包并配置正确设置。
-要为现有项目手动添加 ESLint,在 `package.json` 中添加 `next lint` 脚本:
+要为现有项目手动添加 ESLint,请在 `package.json` 中添加 `next lint` 脚本:
```json filename="package.json"
{
@@ -274,35 +274,35 @@ Next.js 内置 ESLint 支持。使用 `create-next-app` 创建新项目时会自
}
```
-然后运行 `npm run lint`,将引导完成安装和配置流程:
+然后运行 `npm run lint`,安装向导将引导完成配置过程:
```bash filename="终端"
npm run lint
```
-您会看到如下提示:
+您将看到如下提示:
-> ? 您希望如何配置 ESLint?
+> ? 希望如何配置 ESLint?
>
> ❯ 严格模式(推荐)
> 基础模式
> 取消
-- **严格模式**:包含 Next.js 基础 ESLint 配置及更严格的 Core Web Vitals 规则集。初次设置 ESLint 时推荐此配置
+- **严格模式**:包含 Next.js 基础 ESLint 配置及更严格的 Core Web Vitals 规则集。首次配置 ESLint 时推荐使用
- **基础模式**:仅包含 Next.js 基础 ESLint 配置
-- **取消**:跳过配置。如计划自定义 ESLint 配置请选此项
+- **取消**:跳过配置。如需自定义 ESLint 配置请选择此项
选择"严格"或"基础"后,Next.js 会自动安装 `eslint` 和 `eslint-config-next` 依赖,并在项目根目录创建包含所选配置的 `.eslintrc.json` 文件。
-现在可随时运行 `next lint` 执行 ESLint 检查。ESLint 设置完成后,每次构建 (`next build`) 时也会自动运行。错误会导致构建失败,而警告不会。
+此后可通过运行 `next lint` 执行 ESLint 检查。ESLint 配置完成后,每次构建 (`next build`) 时也会自动运行。错误会导致构建失败,而警告不会。
更多信息请参考 [ESLint 插件文档](/docs/app/api-reference/config/next-config-js/eslint)。
## 配置绝对导入和模块路径别名
-Next.js 原生支持 `tsconfig.json` 和 `jsconfig.json` 文件中的 `"paths"` 和 `"baseUrl"` 选项。
+Next.js 原生支持 `tsconfig.json` 和 `jsconfig.json` 中的 `"paths"` 和 `"baseUrl"` 选项。
-这些选项允许将项目目录别名化为绝对路径,使模块导入更简洁。例如:
+这些选项允许将项目目录映射为绝对路径,使模块导入更清晰简洁。例如:
```jsx
// 之前
@@ -312,7 +312,7 @@ import { Button } from '../../../components/button'
import { Button } from '@/components/button'
```
-要配置绝对导入,在 `tsconfig.json` 或 `jsconfig.json` 中添加 `baseUrl` 配置选项。例如:
+要配置绝对导入,请在 `tsconfig.json` 或 `jsconfig.json` 中添加 `baseUrl` 配置:
```json filename="tsconfig.json 或 jsconfig.json"
{
@@ -322,7 +322,7 @@ import { Button } from '@/components/button'
}
```
-除配置 `baseUrl` 路径外,还可使用 `"paths"` 选项设置模块路径"别名"。
+除配置 `baseUrl` 外,还可使用 `"paths"` 选项设置模块路径"别名"。
例如以下配置将 `@/components/*` 映射到 `components/*`:
@@ -338,4 +338,4 @@ import { Button } from '@/components/button'
}
```
-每个 `"paths"` 都相对于 `baseUrl` 位置。
+每个 `"paths"` 都相对于 `baseUrl` 指定的位置。
\ No newline at end of file
diff --git a/apps/docs/content/zh-hans/docs/01-app/01-getting-started/08-partial-prerendering.mdx b/apps/docs/content/zh-hans/docs/01-app/01-getting-started/08-partial-prerendering.mdx
index 6580dd42..1320461f 100644
--- a/apps/docs/content/zh-hans/docs/01-app/01-getting-started/08-partial-prerendering.mdx
+++ b/apps/docs/content/zh-hans/docs/01-app/01-getting-started/08-partial-prerendering.mdx
@@ -1,21 +1,21 @@
---
-source-updated-at: 2025-05-19T22:31:51.000Z
-translation-updated-at: 2025-05-19T23:06:15.558Z
+source-updated-at: 2025-05-21T18:33:43.000Z
+translation-updated-at: 2025-05-21T19:17:34.430Z
title: 如何使用部分预渲染 (Partial Prerendering)
-nav_title: 部分预渲染 (PPR)
-description: 学习如何通过部分预渲染 (Partial Prerendering) 结合静态渲染与动态渲染的优势。
+nav_title: 部分预渲染
+description: 了解如何通过部分预渲染 (Partial Prerendering) 结合静态渲染与动态渲染的优势。
version: experimental
related:
title: 后续步骤
- description: 了解更多关于部分预渲染 (PPR) 的配置选项。
+ description: 深入了解部分预渲染 (Partial Prerendering) 的配置选项。
links:
- app/api-reference/config/next-config-js/ppr
---
-部分预渲染 (Partial Prerendering, PPR) 是一种渲染策略,允许你在同一路由中结合静态与动态内容。这既能提升初始页面性能,又能支持个性化的动态数据。
+部分预渲染 (PPR) 是一种渲染策略,允许您在同一路由中结合静态与动态内容。这种方式既提升了初始页面性能,又能支持个性化动态数据。
**🎥 观看视频:** 为什么需要 PPR 及其工作原理 → [YouTube (10 分钟)](https://www.youtube.com/watch?v=MTcPrTIBkpA)。
+> **🎥 观看视频:** PPR 的原理与价值 → [YouTube (10分钟)](https://www.youtube.com/watch?v=MTcPrTIBkpA)
## 部分预渲染如何工作?
-要理解部分预渲染,需要先熟悉 Next.js 提供的渲染策略。
+理解部分预渲染前,需要先熟悉 Next.js 提供的渲染策略。
-### 静态渲染 (Static Rendering)
+### 静态渲染
-静态渲染会提前生成 HTML —— 可以在构建时或通过 [重新验证 (revalidation)](/docs/app/building-your-application/data-fetching/incremental-static-regeneration) 完成。结果会被缓存并在用户和请求间共享。
+静态渲染会提前生成 HTML——在构建时或通过 [重新验证](/docs/app/guides/incremental-static-regeneration) 完成。结果会被缓存并在用户和请求间共享。
-在部分预渲染中,Next.js 会为路由预渲染一个 **静态外壳 (static shell)**,包含布局和任何不依赖请求时数据的组件。
+在部分预渲染中,Next.js 会为路由预渲染一个 **静态外壳**,可包含布局和任何不依赖请求时数据的组件。
-### 动态渲染 (Dynamic Rendering)
+### 动态渲染
-动态渲染会在 **请求时 (request time)** 生成 HTML,从而根据请求时的数据提供个性化内容。
+动态渲染会在 **请求时** 生成 HTML,使您能基于请求时数据提供个性化内容。
-组件在以下情况下会变为动态:
+组件在以下情况会变为动态:
- 使用 [`cookies`](/docs/app/api-reference/functions/cookies)
- 使用 [`headers`](/docs/app/api-reference/functions/headers)
@@ -52,19 +52,19 @@ related:
- 使用 [`draftMode`](/docs/app/api-reference/functions/draft-mode)
- 使用 [`searchParams` 属性](/docs/app/api-reference/file-conventions/page#searchparams-optional)
- 使用 [`unstable_noStore`](/docs/app/api-reference/functions/unstable_noStore)
-- 使用 `{ cache: 'no-store' }` 的 [`fetch`](/docs/app/api-reference/functions/fetch)
+- 使用 [`fetch`](/docs/app/api-reference/functions/fetch) 并设置 `{ cache: 'no-store' }`
-在部分预渲染中,使用这些 API 会抛出特殊的 React 错误,提示 Next.js 该组件无法静态渲染,导致构建错误。你可以使用 [Suspense](#suspense) 将渲染推迟到运行时。
+在部分预渲染中,使用这些 API 会抛出特殊 React 错误,提示 Next.js 该组件无法静态渲染,导致构建错误。您可以使用 [Suspense](#suspense) 延迟渲染至运行时。
### Suspense
-React 的 [Suspense](https://react.dev/reference/react/Suspense) 用于延迟渲染部分应用,直到满足某些条件。
+React 的 [Suspense](https://react.dev/reference/react/Suspense) 用于延迟渲染部分应用直到满足特定条件。
-在部分预渲染中,Suspense 用于标记组件树中的 **动态边界 (dynamic boundaries)**。
+在部分预渲染中,Suspense 用于标记组件树中的 **动态边界**。
-构建时,Next.js 会预渲染静态内容和 `fallback` UI。动态内容会 **推迟 (postponed)** 到用户请求路由时加载。
+构建时,Next.js 会预渲染静态内容和 `fallback` UI。动态内容会 **延迟** 至用户请求路由时加载。
-用 Suspense 包裹组件不会使组件本身变为动态(动态性由 API 使用决定),而是作为封装动态内容的边界,并启用 [流式传输 (streaming)](#streaming)。
+用 Suspense 包裹组件不会使组件本身变为动态(动态性由 API 使用决定),而是作为封装动态内容的边界,实现 [流式传输](#streaming)
```jsx filename="app/page.js"
import { Suspense } from 'react'
@@ -86,33 +86,33 @@ export default function Page() {
}
```
-### 流式传输 (Streaming)
+### 流式传输
-流式传输将路由拆分为多个块,并在准备就绪时逐步传输到客户端。这样用户可以在整个内容完成渲染前立即看到部分页面。
+流式传输将路由拆分为多个块,并在准备就绪时逐步流式传输至客户端。这使得用户能在全部内容完成渲染前立即看到部分页面。
-在部分预渲染中,包裹在 Suspense 中的动态组件会从服务器并行流式传输。
+在部分预渲染中,包裹在 Suspense 中的动态组件会从服务器并行开始流式传输。
-为了减少网络开销,完整响应(包括静态 HTML 和流式传输的动态部分)会在 **单个 HTTP 请求** 中发送。这避免了额外的往返,提升了初始加载和整体性能。
+为减少网络开销,完整响应(包括静态 HTML 和流式动态部分)会通过 **单次 HTTP 请求** 发送。这避免了额外往返,提升了初始加载和整体性能。
## 启用部分预渲染
-你可以通过在 `next.config.ts` 文件中添加 [`ppr`](https://rc.nextjs.org/docs/app/api-reference/next-config-js/ppr) 选项来启用 PPR:
+您可以通过在 `next.config.ts` 文件中添加 [`ppr`](https://rc.nextjs.org/docs/app/api-reference/next-config-js/ppr) 选项来启用 PPR:
```ts filename="next.config.ts" highlight={5} switcher
import type { NextConfig } from 'next'
@@ -135,7 +135,7 @@ const nextConfig = {
}
```
-`'incremental'` 值允许你为特定路由启用 PPR:
+`'incremental'` 值允许您为特定路由启用 PPR:
```tsx filename="/app/dashboard/layout.tsx"
export const experimental_ppr = true
@@ -153,18 +153,18 @@ export default function Layout({ children }) {
}
```
-未设置 `experimental_ppr` 的路由会默认为 `false`,不会使用 PPR 预渲染。你需要为每个路由显式启用 PPR。
+未设置 `experimental_ppr` 的路由默认为 `false`,不会使用 PPR 预渲染。您需要为每个路由显式启用 PPR。
> **须知:**
>
-> - `experimental_ppr` 会应用到路由段的所有子节点,包括嵌套布局和页面。你无需在每个文件中添加,只需在路由的顶层段设置。
-> - 要为子段禁用 PPR,可以在子段中将 `experimental_ppr` 设为 `false`。
+> - `experimental_ppr` 会应用于路由段的所有子级,包括嵌套布局和页面。您无需在每个文件中添加,只需在路由的顶层段设置。
+> - 要为子段禁用 PPR,可在子段中将 `experimental_ppr` 设为 `false`。
## 示例
### 动态 API
-当使用需要查看请求的动态 API 时,Next.js 会为该路由启用动态渲染。要继续使用 PPR,请用 Suspense 包裹组件。例如,`` 组件因使用 `cookies` API 而变为动态:
+当使用需要查看传入请求的动态 API 时,Next.js 会为该路由启用动态渲染。要继续使用 PPR,请用 Suspense 包裹组件。例如,`` 组件因使用 `cookies` API 而变为动态:
```jsx filename="app/user.js" switcher
import { cookies } from 'next/headers'
@@ -184,7 +184,7 @@ export async function User() {
}
```
-`` 组件会流式传输,而 `` 中的其他内容会被预渲染并成为静态外壳的一部分。
+`` 组件将被流式传输,而 `` 中的其他内容会被预渲染并成为静态外壳的一部分。
```tsx filename="app/page.tsx" switcher
import { Suspense } from 'react'
@@ -195,7 +195,7 @@ export const experimental_ppr = true
export default function Page() {
return (
- 这部分会被预渲染
+ 这部分内容将被预渲染
}>
@@ -213,7 +213,7 @@ export const experimental_ppr = true
export default function Page() {
return (
- 这部分会被预渲染
+ 这部分内容将被预渲染
}>
@@ -224,7 +224,7 @@ export default function Page() {
### 传递动态属性
-组件仅在访问值时才会启用动态渲染。例如,如果你从 `` 组件读取 `searchParams`,可以将其作为属性传递给其他组件:
+只有在访问值时组件才会启用动态渲染。例如,如果您从 `` 组件读取 `searchParams`,可以将其作为属性传递给其他组件:
```tsx filename="app/page.tsx" switcher
import { Table, TableSkeleton } from './table'
@@ -237,7 +237,7 @@ export default function Page({
}) {
return (
- 这部分会被预渲染
+ 这部分内容将被预渲染
}>
@@ -253,7 +253,7 @@ import { Suspense } from 'react'
export default function Page({ searchParams }) {
return (
- 这部分会被预渲染
+ 这部分内容将被预渲染
}>
@@ -262,7 +262,7 @@ export default function Page({ searchParams }) {
}
```
-在表格组件内部,访问 `searchParams` 的值会使该组件变为动态,而页面的其余部分会被预渲染。
+在表格组件内部,访问 `searchParams` 的值会使该组件变为动态,而页面其余部分会被预渲染。
```tsx filename="app/table.tsx" switcher
export async function Table({
diff --git a/apps/docs/content/zh-hans/docs/01-app/01-getting-started/09-fetching-data.mdx b/apps/docs/content/zh-hans/docs/01-app/01-getting-started/09-fetching-data.mdx
index 5e75197e..91f0987c 100644
--- a/apps/docs/content/zh-hans/docs/01-app/01-getting-started/09-fetching-data.mdx
+++ b/apps/docs/content/zh-hans/docs/01-app/01-getting-started/09-fetching-data.mdx
@@ -1,31 +1,33 @@
---
-source-updated-at: 2025-05-19T22:31:51.000Z
-translation-updated-at: 2025-05-19T23:06:18.784Z
+source-updated-at: 2025-05-21T18:33:43.000Z
+translation-updated-at: 2025-05-21T19:19:09.115Z
title: 如何获取数据并实现流式传输
nav_title: 数据获取
description: 开始在您的应用中获取数据并流式传输内容。
related:
title: API 参考
- description: 通过阅读 API 参考了解更多本页提到的功能。
+ description: 通过阅读 API 参考文档了解更多本页提到的功能特性。
links:
- app/api-reference/functions/fetch
- app/api-reference/file-conventions/loading
+ - app/api-reference/config/next-config-js/logging
+ - app/api-reference/config/next-config-js/taint
---
-本页将引导您了解如何在 [服务端组件 (Server Components)](#server-components) 和 [客户端组件 (Client Components)](#client-components) 中获取数据,以及如何对依赖数据的内容进行 [流式传输 (streaming)](#streaming)。
+本页将引导您了解如何在 [服务端与客户端组件](/docs/app/getting-started/server-and-client-components) 中获取数据,以及如何流式传输依赖数据的组件。
-## 获取数据
+## 数据获取
### 服务端组件
-您可以通过以下方式在服务端组件中获取数据:
+您可以在服务端组件中通过以下方式获取数据:
1. 使用 [`fetch` API](#with-the-fetch-api)
2. 使用 [ORM 或数据库](#with-an-orm-or-database)
#### 使用 `fetch` API
-要在服务端组件中使用 `fetch` API 获取数据,将组件转换为异步函数,并等待 `fetch` 调用。例如:
+要通过 `fetch` API 获取数据,将您的组件转换为异步函数,并 await `fetch` 调用。例如:
```tsx filename="app/blog/page.tsx" switcher
export default async function Page() {
@@ -55,9 +57,14 @@ export default async function Page() {
}
```
+> **须知:**
+>
+> - `fetch` 响应默认不会被缓存。但 Next.js 会对路由进行 [预渲染](/docs/app/getting-started/partial-prerendering#static-rendering),输出结果会被缓存以提升性能。如需启用 [动态渲染](/docs/app/getting-started/partial-prerendering#dynamic-rendering),请使用 `{ cache: 'no-store' }` 选项。参阅 [`fetch` API 参考](/docs/app/api-reference/functions/fetch)。
+> - 开发环境下,您可以记录 `fetch` 调用以便更好地调试和观察。参阅 [`logging` API 参考](/docs/app/api-reference/config/next-config-js/logging)。
+
#### 使用 ORM 或数据库
-由于服务端组件在服务器端渲染,您可以安全地使用 ORM 或数据库客户端进行查询。将组件转换为异步函数,并等待调用:
+由于服务端组件在服务器端渲染,您可以安全地使用 ORM 或数据库客户端进行查询。将组件转换为异步函数,并 await 调用:
```tsx filename="app/blog/page.tsx" switcher
import { db, posts } from '@/lib/db'
@@ -91,21 +98,21 @@ export default async function Page() {
### 客户端组件
-在客户端组件中获取数据有两种方式:
+在客户端组件中有两种获取数据的方式:
1. 使用 React 的 [`use` 钩子](https://react.dev/reference/react/use)
2. 使用社区库如 [SWR](https://swr.vercel.app/) 或 [React Query](https://tanstack.com/query/latest)
#### 使用 `use` 钩子流式传输数据
-您可以使用 React 的 [`use` 钩子](https://react.dev/reference/react/use) 将数据从服务器 [流式传输 (streaming)](#streaming) 到客户端。首先在服务端组件中获取数据,然后将 Promise 作为 prop 传递给客户端组件:
+您可以使用 React 的 [`use` 钩子](https://react.dev/reference/react/use) 将数据从服务器 [流式传输](#streaming) 到客户端。首先在服务端组件中获取数据,然后将 Promise 作为 prop 传递给客户端组件:
```tsx filename="app/blog/page.tsx" switcher
import Posts from '@/app/ui/posts
import { Suspense } from 'react'
export default function Page() {
- // 不要等待数据获取函数
+ // 不要 await 数据获取函数
const posts = getPosts()
return (
@@ -121,7 +128,7 @@ import Posts from '@/app/ui/posts
import { Suspense } from 'react'
export default function Page() {
- // 不要等待数据获取函数
+ // 不要 await 数据获取函数
const posts = getPosts()
return (
@@ -132,7 +139,7 @@ export default function Page() {
}
```
-然后,在客户端组件中使用 `use` 钩子读取 Promise:
+然后在客户端组件中使用 `use` 钩子读取 Promise:
```tsx filename="app/ui/posts.tsx" switcher
'use client'
@@ -172,11 +179,11 @@ export default function Posts({ posts }) {
}
```
-在上面的示例中,您需要将 `` 组件包裹在 [`` 边界](https://react.dev/reference/react/Suspense) 中。这意味着在 Promise 解析期间将显示 fallback 内容。了解更多关于 [流式传输 (streaming)](#streaming) 的信息。
+在上例中,您需要用 [`` 边界](https://react.dev/reference/react/Suspense) 包裹 `` 组件。这意味着在 Promise 解析期间会显示 fallback UI。了解更多关于 [流式传输](#streaming) 的内容。
#### 社区库
-您可以使用社区库如 [SWR](https://swr.vercel.app/) 或 [React Query](https://tanstack.com/query/latest) 在客户端组件中获取数据。这些库有自己的缓存、流式传输和其他功能的语义。例如,使用 SWR:
+您可以使用社区库如 [SWR](https://swr.vercel.app/) 或 [React Query](https://tanstack.com/query/latest) 在客户端组件中获取数据。这些库有自己的缓存、流式传输等特性语义。例如使用 SWR:
```tsx filename="app/blog/page.tsx" switcher
'use client'
@@ -205,6 +212,7 @@ export default function BlogPage() {
```jsx filename="app/blog/page.js" switcher
'use client'
+
import useSWR from 'swr'
const fetcher = (url) => fetch(url).then((r) => r.json())
@@ -230,28 +238,28 @@ export default function BlogPage() {
## 流式传输
-> **警告:** 以下内容假设您的应用中启用了 [`dynamicIO` 配置选项](/docs/app/api-reference/config/next-config-js/dynamicIO)。该标志在 Next.js 15 canary 中引入。
+> **警告:** 以下内容假设您的应用已启用 [`dynamicIO` 配置选项](/docs/app/api-reference/config/next-config-js/dynamicIO)。该标志在 Next.js 15 canary 版本中引入。
-在服务端组件中使用 `async/await` 时,Next.js 将选择 **动态渲染 (dynamic rendering)**。这意味着数据将在每次用户请求时在服务器端获取和渲染。如果有任何慢速数据请求,整个路由将被阻塞渲染。
+在服务端组件中使用 `async/await` 时,Next.js 会启用 **动态渲染**。这意味着数据将在每次用户请求时在服务器端获取并渲染。如果有任何慢速数据请求,整个路由的渲染都会被阻塞。
-为了改善初始加载时间和用户体验,您可以使用流式传输将页面的 HTML 拆分为较小的块,并逐步将这些块从服务器发送到客户端。
+为了改善初始加载时间和用户体验,您可以使用流式传输将页面的 HTML 拆分为小块,并逐步从服务器发送到客户端。
-您可以通过以下两种方式在应用中实现流式传输:
+有两种方式可以在应用中实现流式传输:
1. 使用 [`loading.js` 文件](#with-loadingjs)
2. 使用 React 的 [`` 组件](#with-suspense)
### 使用 `loading.js`
-您可以在页面所在文件夹中创建 `loading.js` 文件,以便在数据获取期间流式传输 **整个页面**。例如,要流式传输 `app/blog/page.js`,请在 `app/blog` 文件夹中添加该文件。
+您可以在页面所在文件夹中创建 `loading.js` 文件,在数据获取期间流式传输 **整个页面**。例如要为 `app/blog/page.js` 添加流式传输,请在 `app/blog` 文件夹中添加该文件。
-在后台,`loading.js` 将被嵌套在 `layout.js` 中,并自动将 `page.js` 文件及其子内容包裹在 `` 边界中。
+在底层,`loading.js` 会被嵌套在 `layout.js` 中,并自动将 `page.js` 文件及其子组件包裹在 `` 边界内。
-这种方法适用于路由段(布局和页面),但对于更细粒度的流式传输,您可以使用 ``。
+这种方法适用于路由段(布局和页面),如需更细粒度的流式传输,可以使用 ``。
### 使用 ``
-`` 允许您更精细地控制页面的哪些部分进行流式传输。例如,您可以立即显示 `` 边界之外的任何页面内容,并流式传输边界内的博客文章列表。
+`` 允许您更精细地控制页面的哪些部分需要流式传输。例如,您可以立即显示 `` 边界外的页面内容,而边界内的博客列表则进行流式传输。
```tsx filename="app/blog/page.tsx" switcher
import { Suspense } from 'react'
@@ -309,13 +317,13 @@ import BlogListSkeleton from '@/components/BlogListSkeleton'
export default function BlogPage() {
return (
- {/* 这部分内容将立即发送到客户端 */}
+ {/* 这部分内容会立即发送到客户端 */}
- {/* 任何包裹在 边界中的内容将被流式传输 */}
+ {/* 任何包裹在 边界内的内容都会流式传输 */}
}>
@@ -333,13 +341,13 @@ import BlogListSkeleton from '@/components/BlogListSkeleton'
export default function BlogPage() {
return (
- {/* 这部分内容将立即发送到客户端 */}
+ {/* 这部分内容会立即发送到客户端 */}
- {/* 任何包裹在 边界中的内容将被流式传输 */}
+ {/* 任何包裹在 边界内的内容都会流式传输 */}
}>
@@ -351,6 +359,269 @@ export default function BlogPage() {
### 创建有意义的加载状态
-即时加载状态是在导航后立即向用户显示的 fallback UI。为了获得最佳用户体验,我们建议设计有意义的加载状态,帮助用户理解应用正在响应。例如,您可以使用骨架屏和加载动画,或者未来屏幕的一小部分但有意义的内容,如封面照片、标题等。
+即时加载状态是导航后立即向用户显示的 fallback UI。为了最佳用户体验,我们建议设计有意义的加载状态,帮助用户理解应用正在响应。例如可以使用骨架屏和加载动画,或未来屏幕中的一小部分有意义内容如封面图片、标题等。
+
+开发时,您可以使用 [React Devtools](https://react.dev/learn/react-developer-tools) 预览和检查组件的加载状态。
+
+## 示例
+
+### 顺序数据获取
+
+顺序数据获取发生在树形结构的嵌套组件各自获取数据且请求未被 [去重](/docs/app/deep-dive/caching#request-memoization) 时,会导致响应时间变长。
+
+
+
+有时您可能需要这种模式,因为某个获取依赖于另一个获取的结果。
+
+例如,`` 组件只有在 `` 组件完成数据获取后才会开始获取数据,因为 `` 依赖 `artistID` prop:
+
+```tsx filename="app/artist/[username]/page.tsx" switcher
+export default async function Page({
+ params,
+}: {
+ params: Promise<{ username: string }>
+}) {
+ const { username } = await params
+ // 获取艺术家信息
+ const artist = await getArtist(username)
+
+ return (
+ <>
+ {artist.name}
+ {/* 在 Playlists 组件加载时显示 fallback UI */}
+ Loading... }>
+ {/* 将艺术家 ID 传递给 Playlists 组件 */}
+
+
+ >
+ )
+}
+
+async function Playlists({ artistID }: { artistID: string }) {
+ // 使用艺术家 ID 获取播放列表
+ const playlists = await getArtistPlaylists(artistID)
+
+ return (
+
+ {playlists.map((playlist) => (
+ - {playlist.name}
+ ))}
+
+ )
+}
+```
+
+```jsx filename="app/artist/[username]/page.js" switcher
+export default async function Page({ params }) {
+ const { username } = await params
+ // 获取艺术家信息
+ const artist = await getArtist(username)
+
+ return (
+ <>
+ {artist.name}
+ {/* 在 Playlists 组件加载时显示 fallback UI */}
+ Loading... }>
+ {/* 将艺术家 ID 传递给 Playlists 组件 */}
+
+
+ >
+ )
+}
+
+async function Playlists({ artistID }) {
+ // 使用艺术家 ID 获取播放列表
+ const playlists = await getArtistPlaylists(artistID)
+
+ return (
+
+ {playlists.map((playlist) => (
+ - {playlist.name}
+ ))}
+
+ )
+}
+```
+
+为了改善用户体验,您应该使用 [React ``](/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense) 在数据获取时显示 `fallback`。这将启用 [流式传输](#streaming) 并防止整个路由被顺序数据请求阻塞。
+
+### 并行数据获取
+
+当路由中的数据请求被主动发起并同时开始时,就会发生并行数据获取。
+
+默认情况下,[布局和页面](/docs/app/getting-started/layouts-and-pages)是并行渲染的。因此每个路由段会尽可能早地开始获取数据。
+
+然而,在_任何_组件内部,如果将多个 `async`/`await` 请求按顺序放置,它们仍然会串行执行。例如,`getAlbums` 会阻塞直到 `getArtist` 完成解析:
+
+```tsx filename="app/artist/[username]/page.tsx" switcher
+import { getArtist, getAlbums } from '@/app/lib/data'
+
+export default async function Page({ params }) {
+ // 这些请求将串行执行
+ const { username } = await params
+ const artist = await getArtist(username)
+ const albums = await getAlbums(username)
+ return {artist.name}
+}
+```
+
+你可以通过在数据使用组件外部定义请求,并使用 [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) 一起解析它们来实现并行请求:
+
+```tsx filename="app/artist/[username]/page.tsx" highlight={3,8,23} switcher
+import Albums from './albums'
+
+async function getArtist(username: string) {
+ const res = await fetch(`https://api.example.com/artist/${username}`)
+ return res.json()
+}
+
+async function getAlbums(username: string) {
+ const res = await fetch(`https://api.example.com/artist/${username}/albums`)
+ return res.json()
+}
+
+export default async function Page({
+ params,
+}: {
+ params: Promise<{ username: string }>
+}) {
+ const { username } = await params
+ const artistData = getArtist(username)
+ const albumsData = getAlbums(username)
+
+ // 并行发起两个请求
+ const [artist, albums] = await Promise.all([artistData, albumsData])
+
+ return (
+ <>
+ {artist.name}
+
+ >
+ )
+}
+```
+
+```jsx filename="app/artist/[username]/page.js" highlight={3,8,19} switcher
+import Albums from './albums'
+
+async function getArtist(username) {
+ const res = await fetch(`https://api.example.com/artist/${username}`)
+ return res.json()
+}
+
+async function getAlbums(username) {
+ const res = await fetch(`https://api.example.com/artist/${username}/albums`)
+ return res.json()
+}
+
+export default async function Page({ params }) {
+ const { username } = await params
+ const artistData = getArtist(username)
+ const albumsData = getAlbums(username)
+
+ // 并行发起两个请求
+ const [artist, albums] = await Promise.all([artistData, albumsData])
+
+ return (
+ <>
+ {artist.name}
+
+ >
+ )
+}
+```
+
+> **须知:** 使用 `Promise.all` 时,如果其中一个请求失败,整个操作都会失败。要处理这种情况,可以使用 [`Promise.allSettled`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled) 方法替代。
+
+### 数据预加载
+
+你可以通过创建一个工具函数,在阻塞请求之前主动调用来预加载数据。`- ` 组件会根据 `checkIsAvailable()` 函数的返回值条件渲染。
+
+你可以在 `checkIsAvailable()` 之前调用 `preload()` 来主动初始化 `
` 的数据依赖。当 ` ` 渲染时,其数据已经获取完成。
-在开发过程中,您可以使用 [React Devtools](https://react.dev/learn/react-developer-tools) 预览和检查组件的加载状态。
\ No newline at end of file
+```tsx filename="app/item/[id]/page.tsx" switcher
+import { getItem } from '@/lib/data'
+
+export default async function Page({
+ params,
+}: {
+ params: Promise<{ id: string }>
+}) {
+ const { id } = await params
+ // 开始加载项目数据
+ preload(id)
+ // 执行另一个异步任务
+ const isAvailable = await checkIsAvailable()
+
+ return isAvailable ? : null
+}
+
+export const preload = (id: string) => {
+ // void 会执行给定表达式并返回 undefined
+ // https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/void
+ void getItem(id)
+}
+export async function Item({ id }: { id: string }) {
+ const result = await getItem(id)
+ // ...
+}
+```
+
+```jsx filename="app/item/[id]/page.js" switcher
+import { getItem } from '@/lib/data'
+
+export default async function Page({ params }) {
+ const { id } = await params
+ // 开始加载项目数据
+ preload(id)
+ // 执行另一个异步任务
+ const isAvailable = await checkIsAvailable()
+
+ return isAvailable ? : null
+}
+
+export const preload = (id) => {
+ // void 会执行给定表达式并返回 undefined
+ // https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/void
+ void getItem(id)
+}
+export async function Item({ id }) {
+ const result = await getItem(id)
+ // ...
+```
+
+此外,你还可以使用 React 的 [`cache` 函数](https://react.dev/reference/react/cache) 和 [`server-only` 包](https://www.npmjs.com/package/server-only) 来创建一个可复用的工具函数。这种方法可以缓存数据获取函数,并确保它只在服务器端执行。
+
+```ts filename="utils/get-item.ts" switcher
+import { cache } from 'react'
+import 'server-only'
+import { getItem } from '@/lib/data'
+
+export const preload = (id: string) => {
+ void getItem(id)
+}
+
+export const getItem = cache(async (id: string) => {
+ // ...
+})
+```
+
+```js filename="utils/get-item.js" switcher
+import { cache } from 'react'
+import 'server-only'
+import { getItem } from '@/lib/data'
+
+export const preload = (id) => {
+ void getItem(id)
+}
+
+export const getItem = cache(async (id) => {
+ // ...
+})
+```
diff --git a/apps/docs/content/zh-hans/docs/01-app/01-getting-started/11-error-handling.mdx b/apps/docs/content/zh-hans/docs/01-app/01-getting-started/11-error-handling.mdx
index 68d2a187..11146637 100644
--- a/apps/docs/content/zh-hans/docs/01-app/01-getting-started/11-error-handling.mdx
+++ b/apps/docs/content/zh-hans/docs/01-app/01-getting-started/11-error-handling.mdx
@@ -1,9 +1,9 @@
---
-source-updated-at: '2025-05-19T22:31:51.000Z'
-translation-updated-at: '2025-05-19T22:31:51.000Z'
+source-updated-at: 2025-05-21T18:33:43.000Z
+translation-updated-at: 2025-05-21T19:17:12.404Z
title: 错误处理指南
nav_title: 错误处理
-description: 学习如何显示预期错误并处理未捕获异常。
+description: 学习如何显示预期错误并处理未捕获的异常。
related:
title: API 参考
description: 通过阅读 API 参考文档深入了解本页提到的功能。
@@ -14,17 +14,17 @@ related:
- app/api-reference/file-conventions/not-found
---
-错误可分为两类:[预期错误](#handling-expected-errors) 和 [未捕获异常](#handling-uncaught-exceptions)。本文将指导您如何在 Next.js 应用中处理这些错误。
+错误可分为两类:[预期错误](#handling-expected-errors) 和 [未捕获异常](#handling-uncaught-exceptions)。本指南将介绍如何在 Next.js 应用中处理这些错误。
## 处理预期错误
-预期错误指应用正常运行过程中可能出现的错误,例如[服务端表单验证](/docs/app/building-your-application/data-fetching/server-actions-and-mutations#server-side-form-validation)失败或请求失败。这类错误应显式处理并返回给客户端。
+预期错误指应用正常运行过程中可能出现的错误,例如来自 [服务端表单验证 (server-side form validation)](/docs/app/building-your-application/data-fetching/server-actions-and-mutations#server-side-form-validation) 的验证错误或失败的请求。这类错误应被显式处理并返回给客户端。
### 服务端函数
-您可以使用 [`useActionState`](https://react.dev/reference/react/useActionState) 钩子来处理[服务端函数 (Server Functions)](https://react.dev/reference/rsc/server-functions)中的预期错误。
+您可以使用 [`useActionState`](https://react.dev/reference/react/useActionState) 钩子来处理 [服务端函数 (Server Functions)](https://react.dev/reference/rsc/server-functions) 中的预期错误。
-对于这类错误,应避免使用 `try`/`catch` 代码块抛出错误,而应将预期错误建模为返回值。
+对于这类错误,应避免使用 `try`/`catch` 代码块和抛出错误,而应将预期错误建模为返回值。
```ts filename="app/actions.ts" switcher
'use server'
@@ -81,12 +81,12 @@ export function Form() {
return (
)
}
@@ -107,12 +107,12 @@ export function Form() {
return (
)
}
@@ -120,7 +120,7 @@ export function Form() {
### 服务端组件
-在服务端组件中获取数据时,您可以使用响应条件渲染错误信息或执行[重定向 (redirect)](/docs/app/api-reference/functions/redirect)。
+在服务端组件中获取数据时,您可以使用响应来条件渲染错误信息或执行 [`redirect`](/docs/app/api-reference/functions/redirect) 重定向。
```tsx filename="app/page.tsx" switcher
export default async function Page() {
@@ -128,7 +128,7 @@ export default async function Page() {
const data = await res.json()
if (!res.ok) {
- return 'There was an error.'
+ return '发生错误。'
}
return '...'
@@ -141,7 +141,7 @@ export default async function Page() {
const data = await res.json()
if (!res.ok) {
- return 'There was an error.'
+ return '发生错误。'
}
return '...'
@@ -150,7 +150,7 @@ export default async function Page() {
### 未找到页面
-您可以在路由段内调用 [`notFound`](/docs/app/api-reference/functions/not-found) 函数,并使用 [`not-found.js`](/docs/app/api-reference/file-conventions/not-found) 文件显示 404 UI。
+您可以在路由段中调用 [`notFound`](/docs/app/api-reference/functions/not-found) 函数,并使用 [`not-found.js`](/docs/app/api-reference/file-conventions/not-found) 文件来显示 404 UI。
```tsx filename="app/blog/[slug]/page.tsx" switcher
import { getPostBySlug } from '@/lib/posts'
@@ -184,23 +184,23 @@ export default async function Page({ params }) {
```tsx filename="app/blog/[slug]/not-found.tsx" switcher
export default function NotFound() {
- return 404 - Page Not Found
+ return 404 - 页面未找到
}
```
```jsx filename="app/blog/[slug]/not-found.js" switcher
export default function NotFound() {
- return 404 - Page Not Found
+ return 404 - 页面未找到
}
```
## 处理未捕获异常
-未捕获异常是意外错误,表明应用中存在不应出现的 bug 或问题。这类错误应通过抛出异常来处理,随后会被错误边界捕获。
+未捕获异常是意外错误,表明应用中存在不应在正常流程中出现的错误或问题。这类错误应通过抛出错误来处理,然后由错误边界捕获。
### 嵌套错误边界
-Next.js 使用错误边界 (error boundaries) 处理未捕获异常。错误边界会捕获子组件中的错误,并显示备用 UI 而非崩溃的组件树。
+Next.js 使用错误边界来处理未捕获异常。错误边界会捕获其子组件中的错误,并显示备用 UI 而非崩溃的组件树。
通过在路由段内添加 [`error.js`](/docs/app/api-reference/file-conventions/error) 文件并导出 React 组件来创建错误边界:
@@ -223,14 +223,14 @@ export default function Error({
return (
-
Something went wrong!
+ 出错了!
)
@@ -250,21 +250,21 @@ export default function Error({ error, reset }) {
return (
-
Something went wrong!
+ 出错了!
)
}
```
-错误会冒泡到最近的父级错误边界。通过在[路由层级结构](/docs/app/getting-started/project-structure#component-hierarchy)的不同层级放置 `error.tsx` 文件,可以实现细粒度的错误处理。
+错误会冒泡到最近的父级错误边界。通过在 [路由层级结构 (route hierarchy)](/docs/app/getting-started/project-structure#component-hierarchy) 的不同层级放置 `error.tsx` 文件,可以实现细粒度的错误处理。
` 和 `` 标签,因为它在激活时会替换根布局或模板。
+虽然不常见,但您可以通过根应用目录中的 [`global-error.js`](/docs/app/api-reference/file-conventions/error#global-error) 文件处理根布局中的错误,即使在使用 [国际化 (internationalization)](/docs/app/guides/internationalization) 时也是如此。全局错误 UI 必须定义自己的 `` 和 `` 标签,因为它在激活时会替换根布局或模板。
```tsx filename="app/global-error.tsx" switcher
'use client' // 错误边界必须是客户端组件
@@ -292,8 +292,8 @@ export default function GlobalError({
// global-error 必须包含 html 和 body 标签
- Something went wrong!
-
+ 出错了!
+
)
@@ -308,10 +308,10 @@ export default function GlobalError({ error, reset }) {
// global-error 必须包含 html 和 body 标签
- Something went wrong!
-
+ 出错了!
+
)
}
-```
+```
\ No newline at end of file
diff --git a/apps/docs/content/zh-hans/docs/01-app/02-guides/incremental-static-regeneration.mdx b/apps/docs/content/zh-hans/docs/01-app/02-guides/incremental-static-regeneration.mdx
index b016a1a7..5aa3f78c 100644
--- a/apps/docs/content/zh-hans/docs/01-app/02-guides/incremental-static-regeneration.mdx
+++ b/apps/docs/content/zh-hans/docs/01-app/02-guides/incremental-static-regeneration.mdx
@@ -1,8 +1,9 @@
---
-source-updated-at: 2025-05-19T22:31:51.000Z
-translation-updated-at: 2025-05-20T22:51:15.382Z
-title: 增量静态再生 (ISR)
-description: 了解如何通过增量静态再生在运行时创建或更新静态页面。
+source-updated-at: 2025-05-21T18:33:43.000Z
+translation-updated-at: 2025-05-21T19:18:43.136Z
+title: 如何实现增量静态再生 (ISR)
+nav_title: ISR
+description: 学习如何通过增量静态再生在运行时创建或更新静态页面。
---
@@ -18,8 +19,8 @@ description: 了解如何通过增量静态再生在运行时创建或更新静
- 无需重建整个站点即可更新静态内容
- 通过为大多数请求提供预渲染的静态页面来减少服务器负载
-- 确保自动为页面添加正确的 `cache-control` 头
-- 处理大量内容页面而无需漫长的 `next build` 时间
+- 确保自动为页面添加正确的 `cache-control` 标头
+- 处理大量内容页面而无需长时间的 `next build` 构建
以下是一个最小示例:
@@ -32,7 +33,7 @@ interface Post {
content: string
}
-// Next.js 将在请求到来时使缓存失效,最多每 60 秒一次。
+// Next.js 将在请求到达时使缓存失效,最多每 60 秒一次。
export const revalidate = 60
// 我们仅在构建时预渲染来自 `generateStaticParams` 的参数。
@@ -68,7 +69,7 @@ export default async function Page({
```
```jsx filename="app/blog/[id]/page.js" switcher
-// Next.js 将在请求到来时使缓存失效,最多每 60 秒一次。
+// Next.js 将在请求到达时使缓存失效,最多每 60 秒一次。
export const revalidate = 60
// 我们仅在构建时预渲染来自 `generateStaticParams` 的参数。
@@ -140,7 +141,7 @@ export const getStaticProps: GetStaticProps = async ({
return {
props: { post },
- // Next.js 将在请求到来时使缓存失效,最多每 60 秒一次。
+ // Next.js 将在请求到达时使缓存失效,最多每 60 秒一次。
revalidate: 60,
}
}
@@ -176,7 +177,7 @@ export async function getStaticProps({ params }) {
return {
props: { post },
- // Next.js 将在请求到来时使缓存失效,最多每 60 秒一次。
+ // Next.js 将在请求到达时使缓存失效,最多每 60 秒一次。
revalidate: 60,
}
}
@@ -193,13 +194,13 @@ export default function Page({ post }) {
-这个示例的工作原理:
+此示例的工作原理:
-1. 在 `next build` 期间,所有已知的博客文章都会被生成(此示例中有 25 篇)
-2. 对这些页面的所有请求(例如 `/blog/1`)都会被缓存并立即响应
+1. 在 `next build` 期间,所有已知的博客文章(本示例中有 25 篇)会被生成
+2. 对这些页面(例如 `/blog/1`)的所有请求都会被缓存并即时响应
3. 60 秒后,下一个请求仍会显示缓存的(过时的)页面
4. 缓存失效,新版本的页面开始在后台生成
-5. 生成成功后,Next.js 将显示并缓存更新后的页面
+5. 成功生成后,Next.js 将显示并缓存更新后的页面
6. 如果请求 `/blog/26`,Next.js 将按需生成并缓存此页面
## 参考
@@ -233,7 +234,7 @@ export default function Page({ post }) {
### 基于时间的重新验证
-此示例在 `/blog` 上获取并显示博客文章列表。一小时后,该页面的缓存在下一次访问时失效。然后,在后台生成包含最新博客文章的新版本页面。
+此示例在 `/blog` 上获取并显示博客文章列表。一小时后,此页面的缓存将在下一次访问时失效。然后,在后台,将使用最新的博客文章生成新版本的页面。
```tsx filename="app/blog/page.tsx" switcher
interface Post {
@@ -285,7 +286,7 @@ export default async function Page() {
对于更精确的重新验证方法,可以使用 `revalidatePath` 函数按需使页面失效。
-例如,此服务器操作将在添加新文章后调用。无论您在服务器组件中使用 `fetch` 还是连接到数据库来获取数据,这将清除整个路由的缓存,并允许服务器组件获取新数据。
+例如,此服务器操作将在添加新文章后调用。无论您在服务器组件中使用 `fetch` 还是连接到数据库获取数据,这将清除整个路由的缓存,并允许服务器组件获取新数据。
```ts filename="app/actions.ts" switcher
'use server'
@@ -309,7 +310,7 @@ export async function createPost() {
}
```
-[查看演示](https://on-demand-isr.vercel.app) 和 [探索源代码](https://github.com/vercel/on-demand-isr)。
+[查看演示](https://on-demand-isr.vercel.app) 并 [探索源代码](https://github.com/vercel/on-demand-isr)。
### 使用 `revalidateTag` 进行按需重新验证
@@ -373,7 +374,7 @@ export default async function Page() {
}
```
-然后可以在[服务器操作](/docs/app/building-your-application/data-fetching/server-actions-and-mutations)或[路由处理程序](/docs/app/building-your-application/routing/route-handlers)中使用 `revalidateTag`:
+然后可以在[服务器操作](/docs/app/building-your-application/data-fetching/server-actions-and-mutations)或[路由处理器](/docs/app/building-your-application/routing/route-handlers)中使用 `revalidateTag`:
```ts filename="app/actions.ts" switcher
'use server'
@@ -381,7 +382,7 @@ export default async function Page() {
import { revalidateTag } from 'next/cache'
export async function createPost() {
- // 使缓存中标记为 'posts' 的所有数据失效
+ // 使缓存中所有标记为 'posts' 的数据失效
revalidateTag('posts')
}
```
@@ -392,7 +393,7 @@ export async function createPost() {
import { revalidateTag } from 'next/cache'
export async function createPost() {
- // 使缓存中标记为 'posts' 的所有数据失效
+ // 使缓存中所有标记为 'posts' 的数据失效
revalidateTag('posts')
}
```
@@ -405,7 +406,7 @@ export async function createPost() {
对于更精确的重新验证方法,可以使用 `res.revalidate` 从 API 路由按需生成新页面。
-例如,可以调用此 API 路由 `/api/revalidate?secret=` 来重新验证给定的博客文章。创建一个只有您的 Next.js 应用知道的密钥令牌。此密钥将用于防止未经授权访问重新验证 API 路由。
+例如,可以调用 `/api/revalidate?secret=` 此 API 路由来重新验证给定的博客文章。创建一个只有您的 Next.js 应用知道的密钥令牌。此密钥将用于防止未经授权访问重新验证 API 路由。
```ts filename="pages/api/revalidate.ts" switcher
import type { NextApiRequest, NextApiResponse } from 'next'
@@ -414,19 +415,19 @@ export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
- // 检查密钥以确认这是一个有效请求
+ // 检查密钥以确认这是有效请求
if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
return res.status(401).json({ message: '无效令牌' })
}
try {
- // 这应该是实际路径而非重写路径
+ // 这应该是实际路径而不是重写路径
// 例如,对于 "/posts/[id]",这应该是 "/posts/1"
await res.revalidate('/posts/1')
return res.json({ revalidated: true })
} catch (err) {
// 如果发生错误,Next.js 将继续
- // 显示最后成功生成的页面
+ // 显示上次成功生成的页面
return res.status(500).send('重新验证时出错')
}
}
@@ -434,19 +435,19 @@ export default async function handler(
```js filename="pages/api/revalidate.js" switcher
export default async function handler(req, res) {
- // 检查密钥以确认这是一个有效请求
+ // 检查密钥以确认这是有效请求
if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
return res.status(401).json({ message: '无效令牌' })
}
try {
- // 这应该是实际路径而非重写路径
+ // 这应该是实际路径而不是重写路径
// 例如,对于 "/posts/[id]",这应该是 "/posts/1"
await res.revalidate('/posts/1')
return res.json({ revalidated: true })
} catch (err) {
// 如果发生错误,Next.js 将继续
- // 显示最后成功生成的页面
+ // 显示上次成功生成的页面
return res.status(500).send('重新验证时出错')
}
}
@@ -460,13 +461,13 @@ export default async function handler(req, res) {
-如果在尝试重新验证数据时抛出错误,将继续从缓存中提供最后成功生成的数据。在下一个后续请求中,Next.js 将重试重新验证数据。[了解更多关于错误处理的信息](/docs/app/building-your-application/routing/error-handling)。
+如果在尝试重新验证数据时抛出错误,将继续从缓存中提供上次成功生成的数据。在下一个后续请求中,Next.js 将重试重新验证数据。[了解更多关于错误处理的信息](/docs/app/building-your-application/routing/error-handling)。
-如果在处理后台重新生成时 `getStaticProps` 内部发生错误,或者您手动抛出错误,将继续显示最后成功生成的页面。在下一个后续请求中,Next.js 将重试调用 `getStaticProps`。
+如果在处理后台重新生成时 `getStaticProps` 内部发生错误,或者您手动抛出错误,将继续显示上次成功生成的页面。在下一个后续请求中,Next.js 将重试调用 `getStaticProps`。
```tsx filename="pages/blog/[id].tsx" switcher
import type { GetStaticProps } from 'next'
@@ -487,21 +488,21 @@ export const getStaticProps: GetStaticProps = async ({
params: { id: string }
}) => {
// 如果此请求抛出未捕获的错误,Next.js 将
- // 不会使当前显示的页面失效,并在
- // 下一个请求时重试 getStaticProps。
+ // 不会使当前显示的页面失效,并且
+ // 在下一个请求上重试 getStaticProps。
const res = await fetch(`https://api.vercel.app/blog/${params.id}`)
const post: Post = await res.json()
if (!res.ok) {
- // 如果服务器错误,您可能希望
+ // 如果存在服务器错误,您可能希望
// 抛出错误而不是返回,以便缓存不会更新
- // 直到下一个成功的请求。
+ // 直到下一次成功请求。
throw new Error(`获取文章失败,收到状态码 ${res.status}`)
}
return {
props: { post },
- // Next.js 将在请求到来时使缓存失效,最多每 60 秒一次。
+ // Next.js 将在请求到达时使缓存失效,最多每 60 秒一次。
revalidate: 60,
}
}
@@ -510,21 +511,21 @@ export const getStaticProps: GetStaticProps = async ({
```jsx filename="pages/blog/[id].jsx" switcher
export async function getStaticProps({ params }) {
// 如果此请求抛出未捕获的错误,Next.js 将
- // 不会使当前显示的页面失效,并在
- // 下一个请求时重试 getStaticProps。
+ // 不会使当前显示的页面失效,并且
+ // 在下一个请求上重试 getStaticProps。
const res = await fetch(`https://api.vercel.app/blog/${params.id}`)
const post = await res.json()
if (!res.ok) {
- // 如果服务器错误,您可能希望
+ // 如果存在服务器错误,您可能希望
// 抛出错误而不是返回,以便缓存不会更新
- // 直到下一个成功的请求。
+ // 直到下一次成功请求。
throw new Error(`获取文章失败,收到状态码 ${res.status}`)
}
return {
props: { post },
- // Next.js 将在请求到来时使缓存失效,最多每 60 秒一次。
+ // Next.js 将在请求到达时使缓存失效,最多每 60 秒一次。
revalidate: 60,
}
}
@@ -540,7 +541,7 @@ export async function getStaticProps({ params }) {
### 在本地开发中调试缓存数据
-如果使用 `fetch` API,可以添加额外的日志记录来了解哪些请求被缓存或未被缓存。[了解更多关于 `logging` 选项的信息](/docs/app/api-reference/config/next-config-js/logging)。
+如果使用 `fetch` API,可以添加额外的日志记录以了解哪些请求被缓存或未被缓存。[了解更多关于 `logging` 选项的信息](/docs/app/api-reference/config/next-config-js/logging)。
```jsx filename="next.config.js"
module.exports = {
@@ -562,45 +563,45 @@ module.exports = {
NEXT_PRIVATE_DEBUG_CACHE=1
```
-这将使 Next.js 服务器在控制台输出 ISR 缓存的命中与未命中情况。您可以检查输出结果,查看哪些页面是在 `next build` 期间生成的,以及访问路径时页面是如何按需更新的。
+这将使 Next.js 服务器在控制台输出 ISR 缓存的命中和未命中情况。您可以检查输出结果,查看哪些页面是在 `next build` 期间生成的,以及访问路径时页面是如何按需更新的。
## 注意事项
-- ISR 仅在使用 Node.js 运行时(默认)时受支持。
+- ISR 仅在 Node.js 运行时(默认)下支持。
- 创建 [静态导出 (Static Export)](/docs/app/guides/static-exports) 时不支持 ISR。
-- 如果在静态渲染的路由中有多个 `fetch` 请求,且每个请求的 `revalidate` 频率不同,则 ISR 会使用最短的时间。但这些重新验证频率仍会由 [数据缓存 (Data Cache)](/docs/app/deep-dive/caching#data-cache) 遵循。
-- 如果路由中使用的任何 `fetch` 请求的 `revalidate` 时间为 `0` 或显式设置为 `no-store`,则该路由将进行 [动态渲染 (dynamic rendering)](/docs/app/getting-started/partial-prerendering#dynamic-rendering)。
-- 按需 ISR 请求不会执行中间件,这意味着中间件中的任何路径重写或逻辑都不会生效。请确保重新验证的是确切路径。例如,使用 `/post/1` 而非重写后的 `/post-1`。
+- 如果在静态渲染的路由中有多个 `fetch` 请求,且每个请求的 `revalidate` 频率不同,ISR 将使用最短的时间。但这些重新验证频率仍会被 [数据缓存 (Data Cache)](/docs/app/deep-dive/caching#data-cache) 遵循。
+- 如果路由中使用的任何 `fetch` 请求的 `revalidate` 时间为 `0`,或显式设置为 `no-store`,则该路由将 [动态渲染 (dynamically rendered)](/docs/app/getting-started/partial-prerendering#dynamic-rendering)。
+- 中间件不会在按需 ISR 请求时执行,这意味着中间件中的任何路径重写或逻辑都不会生效。请确保您重新验证的是确切的路径。例如,使用 `/post/1` 而非重写后的 `/post-1`。
-- ISR 仅在使用 Node.js 运行时(默认)时受支持。
+- ISR 仅在 Node.js 运行时(默认)下支持。
- 创建 [静态导出 (Static Export)](/docs/app/guides/static-exports) 时不支持 ISR。
-- 按需 ISR 请求不会执行中间件,这意味着中间件中的任何路径重写或逻辑都不会生效。请确保重新验证的是确切路径。例如,使用 `/post/1` 而非重写后的 `/post-1`。
+- 中间件不会在按需 ISR 请求时执行,这意味着中间件中的任何路径重写或逻辑都不会生效。请确保您重新验证的是确切的路径。例如,使用 `/post/1` 而非重写后的 `/post-1`。
## 平台支持
-| 部署选项 | 是否支持 |
-| ------------------------------------------------------------ | ---------------- |
-| [Node.js 服务器](/docs/app/getting-started/deploying#nodejs-server) | 是 |
-| [Docker 容器](/docs/app/getting-started/deploying#docker) | 是 |
-| [静态导出](/docs/app/getting-started/deploying#static-export) | 否 |
-| [适配器](/docs/app/getting-started/deploying#adapters) | 视平台而定 |
+| 部署选项 | 是否支持 |
+| ----------------------------------------------------------------------- | ----------------- |
+| [Node.js 服务器](/docs/app/getting-started/deploying#nodejs-server) | 是 |
+| [Docker 容器](/docs/app/getting-started/deploying#docker) | 是 |
+| [静态导出](/docs/app/getting-started/deploying#static-export) | 否 |
+| [适配器](/docs/app/getting-started/deploying#adapters) | 取决于具体平台 |
了解如何 [配置 ISR](/docs/app/guides/self-hosting#caching-and-isr) 以自托管 Next.js。
## 版本历史
-| 版本 | 变更 |
-| ---------- | ---------------------------------------------------------------------------------- |
-| `v14.1.0` | 自定义 `cacheHandler` 功能稳定。 |
-| `v13.0.0` | 引入了应用路由 (App Router)。 |
-| `v12.2.0` | 页面路由 (Pages Router):按需 ISR 功能稳定 |
-| `v12.0.0` | 页面路由 (Pages Router):新增 [针对爬虫的 ISR 回退机制](/blog/next-12#bot-aware-isr-fallback)。 |
-| `v9.5.0` | 页面路由 (Pages Router):[正式引入 ISR 功能](/blog/next-9-5)。 |
+| 版本 | 变更 |
+| ---------- | ----------------------------------------------------------------------------------- |
+| `v14.1.0` | 自定义 `cacheHandler` 功能稳定。 |
+| `v13.0.0` | 引入了应用路由 (App Router)。 |
+| `v12.2.0` | 页面路由 (Pages Router):按需 ISR 功能稳定。 |
+| `v12.0.0` | 页面路由:新增 [Bot-aware ISR 回退](/blog/next-12#bot-aware-isr-fallback) 功能。 |
+| `v9.5.0` | 页面路由:[引入稳定的 ISR 功能](/blog/next-9-5)。 |
diff --git a/apps/docs/content/zh-hans/docs/01-app/02-guides/index.mdx b/apps/docs/content/zh-hans/docs/01-app/02-guides/index.mdx
index 4fd38c25..a5ff0928 100644
--- a/apps/docs/content/zh-hans/docs/01-app/02-guides/index.mdx
+++ b/apps/docs/content/zh-hans/docs/01-app/02-guides/index.mdx
@@ -1,23 +1,23 @@
---
-source-updated-at: 2025-05-16T04:52:11.000Z
-translation-updated-at: 2025-05-19T23:03:48.700Z
+source-updated-at: 2025-05-21T18:33:43.000Z
+translation-updated-at: 2025-05-21T19:16:04.383Z
title: 指南
-description: 学习如何使用 Next.js 实现常见的 UI 模式和应用场景
+description: 学习如何使用 Next.js 实现常见的 UI 模式和使用场景
---
### 数据获取
-- [使用 `fetch` API](/docs/app/building-your-application/data-fetching/fetching#fetching-data-on-the-server-with-the-fetch-api)
-- [使用 ORM 或数据库客户端](/docs/app/building-your-application/data-fetching/fetching#fetching-data-on-the-server-with-an-orm-or-database)
+- [使用 `fetch` API](/docs/app/getting-started/fetching-data#with-the-fetch-api)
+- [使用 ORM 或数据库客户端](/docs/app/getting-started/fetching-data#with-an-orm-or-database)
- [在服务端读取搜索参数](/docs/app/api-reference/file-conventions/page)
- [在客户端读取搜索参数](/docs/app/api-reference/functions/use-search-params)
### 数据重新验证
-- [使用 ISR 定时重新验证数据](/docs/app/building-your-application/data-fetching/incremental-static-regeneration#time-based-revalidation)
-- [使用 ISR 按需重新验证数据](/docs/app/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation-with-revalidatepath)
+- [使用 ISR 定时重新验证数据](/docs/app/guides/incremental-static-regeneration#time-based-revalidation)
+- [使用 ISR 按需重新验证数据](/docs/app/guides/incremental-static-regeneration#on-demand-revalidation-with-revalidatepath)
-### 表单
+### 表单处理
- [提交表单时显示待处理状态](/docs/app/building-your-application/data-fetching/server-actions-and-mutations#pending-states)
- [服务端表单验证](/docs/app/building-your-application/data-fetching/server-actions-and-mutations#server-side-form-validation)
@@ -26,15 +26,15 @@ description: 学习如何使用 Next.js 实现常见的 UI 模式和应用场景
- [显示乐观 UI 更新](/docs/app/building-your-application/data-fetching/server-actions-and-mutations#optimistic-updates)
- [编程式表单提交](/docs/app/building-your-application/data-fetching/server-actions-and-mutations#programmatic-form-submission)
-### 服务端操作 (Server Actions)
+### 服务端操作
- [传递额外参数](/docs/app/building-your-application/data-fetching/server-actions-and-mutations#passing-additional-arguments)
- [重新验证数据](/docs/app/building-your-application/data-fetching/server-actions-and-mutations#revalidating-data)
-- [重定向](/docs/app/building-your-application/data-fetching/server-actions-and-mutations#redirecting)
+- [重定向处理](/docs/app/building-your-application/data-fetching/server-actions-and-mutations#redirecting)
- [设置 Cookies](/docs/app/api-reference/functions/cookies#setting-a-cookie)
- [删除 Cookies](/docs/app/api-reference/functions/cookies#deleting-cookies)
-### 元数据 (Metadata)
+### 元数据
- [创建 RSS 订阅源](/docs/app/building-your-application/routing/route-handlers#non-ui-responses)
- [创建 Open Graph 图片](/docs/app/api-reference/file-conventions/metadata/opengraph-image)
@@ -43,12 +43,12 @@ description: 学习如何使用 Next.js 实现常见的 UI 模式和应用场景
- [创建自定义 404 页面](/docs/app/api-reference/file-conventions/not-found)
- [创建自定义 500 页面](/docs/app/api-reference/file-conventions/error)
-### 认证 (Auth)
+### 认证授权
- [创建注册表单](/docs/app/guides/authentication#sign-up-and-login-functionality)
-- [无状态基于 Cookie 的会话管理](/docs/app/guides/authentication#stateless-sessions)
-- [有状态基于数据库的会话管理](/docs/app/guides/authentication#database-sessions)
-- [管理授权](/docs/app/guides/authentication#authorization)
+- [无状态 Cookie 会话管理](/docs/app/guides/authentication#stateless-sessions)
+- [基于数据库的有状态会话管理](/docs/app/guides/authentication#database-sessions)
+- [权限管理](/docs/app/guides/authentication#authorization)
### 测试
diff --git a/apps/docs/content/zh-hans/docs/01-app/02-guides/internationalization.mdx b/apps/docs/content/zh-hans/docs/01-app/02-guides/internationalization.mdx
index acc082df..742cb4f3 100644
--- a/apps/docs/content/zh-hans/docs/01-app/02-guides/internationalization.mdx
+++ b/apps/docs/content/zh-hans/docs/01-app/02-guides/internationalization.mdx
@@ -1,24 +1,24 @@
---
-source-updated-at: 2025-05-19T22:31:51.000Z
-translation-updated-at: 2025-05-20T22:49:56.235Z
+source-updated-at: 2025-05-21T18:33:43.000Z
+translation-updated-at: 2025-05-21T19:16:25.701Z
title: 国际化
description: 通过国际化路由和本地化内容添加多语言支持。
---
-Next.js 允许您配置路由和内容渲染以支持多语言。使您的网站适应不同区域设置包括翻译内容(本地化)和国际化路由。
+Next.js 允许您配置路由和内容渲染以支持多种语言。使您的网站适应不同区域设置包括翻译内容(本地化)和国际化路由。
## 术语
-- **区域设置 (Locale):** 一组语言和格式偏好的标识符。通常包括用户首选语言及其可能的地理区域。
+- **区域设置 (Locale):** 用于标识一组语言和格式偏好的标识符。通常包括用户首选语言及其可能的地理区域。
- `en-US`: 美国使用的英语
- `nl-NL`: 荷兰使用的荷兰语
- `nl`: 荷兰语,无特定区域
## 路由概述
-建议使用浏览器中的用户语言偏好来选择区域设置。更改首选语言将修改应用程序接收到的 `Accept-Language` 请求头。
+建议使用浏览器中的用户语言偏好来选择区域设置。更改首选语言会修改发送到您应用的 `Accept-Language` 请求头。
-例如,使用以下库可以检查传入的 `Request`,根据请求头、计划支持的区域设置和默认区域设置来决定选择哪个区域。
+例如,使用以下库可以根据请求头 `Headers`、您计划支持的区域设置和默认区域设置,检查传入的 `Request` 以确定选择哪个区域设置。
```js filename="middleware.js"
import { match } from '@formatjs/intl-localematcher'
@@ -32,7 +32,7 @@ let defaultLocale = 'en-US'
match(languages, locales, defaultLocale) // -> 'en-US'
```
-路由可以通过子路径 (`/fr/products`) 或域名 (`my-site.fr/products`) 实现国际化。利用这些信息,您现在可以基于[中间件](/docs/app/building-your-application/routing/middleware)中的区域设置重定向用户。
+路由可以通过子路径 (`/fr/products`) 或域名 (`my-site.fr/products`) 实现国际化。有了这些信息,您现在可以根据 [中间件](/docs/app/building-your-application/routing/middleware) 中的区域设置重定向用户。
```js filename="middleware.js"
import { NextResponse } from "next/server";
@@ -43,7 +43,7 @@ let locales = ['en-US', 'nl-NL', 'nl']
function getLocale(request) { ... }
export function middleware(request) {
- // 检查路径中是否存在支持的区域设置
+ // 检查路径中是否有支持的区域设置
const { pathname } = request.nextUrl
const pathnameHasLocale = locales.some(
(locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
@@ -51,11 +51,11 @@ export function middleware(request) {
if (pathnameHasLocale) return
- // 无区域设置时重定向
+ // 如果没有区域设置则重定向
const locale = getLocale(request)
request.nextUrl.pathname = `/${locale}${pathname}`
- // 例如:传入请求为 /products
- // 新 URL 变为 /en-US/products
+ // 例如传入请求是 /products
+ // 新 URL 现在是 /en-US/products
return NextResponse.redirect(request.nextUrl)
}
@@ -63,17 +63,17 @@ export const config = {
matcher: [
// 跳过所有内部路径 (_next)
'/((?!_next).*)',
- // 可选:仅在根 URL (/) 运行
+ // 可选:仅在根 URL (/) 上运行
// '/'
],
}
```
-最后,确保 `app/` 下的所有特殊文件都嵌套在 `app/[lang]` 目录下。这使得 Next.js 路由器能够动态处理路由中的不同区域设置,并将 `lang` 参数传递给每个布局和页面。例如:
+最后,确保 `app/` 下的所有特殊文件都嵌套在 `app/[lang]` 下。这使得 Next.js 路由器能够动态处理路由中的不同区域设置,并将 `lang` 参数传递给每个布局和页面。例如:
```tsx filename="app/[lang]/page.tsx" switcher
// 您现在可以访问当前区域设置
-// 例如 /en-US/products -> `lang` 为 "en-US"
+// 例如 /en-US/products -> `lang` 是 "en-US"
export default async function Page({
params,
}: {
@@ -86,7 +86,7 @@ export default async function Page({
```jsx filename="app/[lang]/page.js" switcher
// 您现在可以访问当前区域设置
-// 例如 /en-US/products -> `lang` 为 "en-US"
+// 例如 /en-US/products -> `lang` 是 "en-US"
export default async function Page({ params }) {
const { lang } = await params
return ...
@@ -97,9 +97,9 @@ export default async function Page({ params }) {
## 本地化
-根据用户首选区域设置更改显示内容(即本地化)并非 Next.js 特有功能。以下描述的模式适用于任何 Web 应用程序。
+根据用户首选区域设置更改显示内容(即本地化)并非 Next.js 特有功能。以下描述的模式在任何 Web 应用中同样适用。
-假设我们希望在应用程序中同时支持英语和荷兰语内容。我们可以维护两个不同的“字典”,这些字典对象提供了从某个键到本地化字符串的映射。例如:
+假设我们希望应用中同时支持英语和荷兰语内容。我们可以维护两个不同的“字典”,这些字典是从某个键到本地化字符串的映射对象。例如:
```json filename="dictionaries/en.json"
{
@@ -168,9 +168,9 @@ export default async function Page({ params }) {
}
```
-由于 `app/` 目录中的所有布局和页面默认都是[服务器组件](/docs/app/getting-started/server-and-client-components),我们无需担心翻译文件的大小影响客户端 JavaScript 包大小。此代码**仅在服务器上运行**,只有生成的 HTML 会发送到浏览器。
+由于 `app/` 目录中的所有布局和页面默认都是 [服务端组件](/docs/app/getting-started/server-and-client-components),我们无需担心翻译文件的大小会影响客户端 JavaScript 包的大小。此代码 **仅在服务器上运行**,只有生成的 HTML 会发送到浏览器。
-## 静态生成
+## 静态渲染
要为给定的一组区域设置生成静态路由,我们可以在任何页面或布局中使用 `generateStaticParams`。这可以是全局的,例如在根布局中:
diff --git a/apps/docs/content/zh-hans/docs/01-app/02-guides/migrating/app-router-migration.mdx b/apps/docs/content/zh-hans/docs/01-app/02-guides/migrating/app-router-migration.mdx
index ae65446c..6e3c41fc 100644
--- a/apps/docs/content/zh-hans/docs/01-app/02-guides/migrating/app-router-migration.mdx
+++ b/apps/docs/content/zh-hans/docs/01-app/02-guides/migrating/app-router-migration.mdx
@@ -1,18 +1,18 @@
---
-source-updated-at: 2025-05-19T22:31:51.000Z
-translation-updated-at: 2025-05-20T23:00:04.349Z
-title: 如何从 Pages 路由迁移到 App 路由
+source-updated-at: 2025-05-21T18:33:43.000Z
+translation-updated-at: 2025-05-21T19:25:27.767Z
+title: 如何从 Pages 路由迁移至 App 路由
nav_title: App 路由
-description: 了解如何将现有的 Next.js 应用从 Pages 路由升级到 App 路由。
+description: 学习如何将现有的 Next.js 应用从 Pages 路由升级至 App 路由。
---
本指南将帮助您:
-- [将 Next.js 应用从版本 12 更新到版本 13](#nextjs-version)
+- [将 Next.js 应用从版本 12 升级至版本 13](#nextjs-version)
- [升级同时适用于 `pages` 和 `app` 目录的功能](#upgrading-new-features)
-- [将现有应用从 `pages` 逐步迁移到 `app`](#migrating-from-pages-to-app)
+- [将现有应用从 `pages` 逐步迁移至 `app`](#migrating-from-pages-to-app)
-## 升级步骤
+## 升级准备
### Node.js 版本要求
@@ -20,7 +20,7 @@ description: 了解如何将现有的 Next.js 应用从 Pages 路由升级到 Ap
### Next.js 版本升级
-要更新到 Next.js 13 版本,请使用您偏好的包管理器运行以下命令:
+要升级至 Next.js 13 版本,请使用您偏好的包管理器运行以下命令:
```bash filename="终端"
npm install next@latest react@latest react-dom@latest
@@ -28,41 +28,41 @@ npm install next@latest react@latest react-dom@latest
### ESLint 版本升级
-如果您使用 ESLint,需要升级 ESLint 版本:
+如果使用 ESLint,需要升级 ESLint 版本:
```bash filename="终端"
npm install -D eslint-config-next@latest
```
-> **须知**:您可能需要重启 VS Code 中的 ESLint 服务器才能使更改生效。打开命令面板(Mac 上按 `cmd+shift+p`;Windows 上按 `ctrl+shift+p`)并搜索 `ESLint: 重启 ESLint 服务器`。
+> **须知**:您可能需要重启 VS Code 中的 ESLint 服务器使更改生效。打开命令面板(Mac 使用 `cmd+shift+p`;Windows 使用 `ctrl+shift+p`)并搜索 `ESLint: 重启 ESLint 服务器`。
## 后续步骤
-更新完成后,请参阅以下部分进行后续操作:
+升级完成后,请参考以下章节进行后续操作:
-- [升级新功能](#upgrading-new-features):帮助您升级到新功能的指南,例如改进的 Image 和 Link 组件。
-- [从 `pages` 迁移到 `app` 目录](#migrating-from-pages-to-app):逐步指南,帮助您从 `pages` 目录逐步迁移到 `app` 目录。
+- [升级新功能](#upgrading-new-features):指导您升级至改进后的 Image 组件、Link 组件等新功能。
+- [从 `pages` 迁移至 `app` 目录](#migrating-from-pages-to-app):分步指南帮助您逐步从 `pages` 迁移至 `app` 目录。
## 升级新功能
-Next.js 13 引入了新的 [App 路由](/docs/app/building-your-application/routing),包含新功能和约定。新路由位于 `app` 目录中,可与 `pages` 目录共存。
+Next.js 13 引入了新的 [App 路由 (App Router)](/docs/app/building-your-application/routing),包含新功能和约定。新路由在 `app` 目录中可用,并与 `pages` 目录共存。
-升级到 Next.js 13 **不**强制要求使用 App 路由。您可以继续使用 `pages` 目录,同时享受适用于两个目录的新功能,例如更新的 [Image 组件](#image-component)、[Link 组件](#link-component)、[Script 组件](#script-component) 和 [字体优化](#font-optimization)。
+升级至 Next.js 13 **不强制** 要求使用 App 路由。您可以继续使用 `pages` 目录,同时享受适用于两个目录的新功能,例如更新后的 [Image 组件](#image-component)、[Link 组件](#link-component)、[Script 组件](#script-component) 和 [字体优化 (Font optimization)](#font-optimization)。
### `` 组件
-Next.js 12 通过临时导入 `next/future/image` 对 Image 组件进行了改进。这些改进包括减少客户端 JavaScript、更简单的图像扩展和样式设置、更好的可访问性以及原生浏览器懒加载。
+Next.js 12 通过临时导入 `next/future/image` 对 Image 组件进行了改进。这些改进包括减少客户端 JavaScript、更简便的图像扩展和样式设置、更好的可访问性以及原生浏览器懒加载。
在版本 13 中,这些新行为已成为 `next/image` 的默认行为。
-有两个代码修改工具可帮助您迁移到新的 Image 组件:
+有两个代码修改工具 (codemod) 可帮助您迁移至新的 Image 组件:
-- [**`next-image-to-legacy-image` 代码修改工具**](/docs/app/guides/upgrading/codemods#next-image-to-legacy-image):安全自动地将 `next/image` 导入重命名为 `next/legacy/image`,现有组件将保持相同行为。
-- [**`next-image-experimental` 代码修改工具**](/docs/app/guides/upgrading/codemods#next-image-experimental):危险地添加内联样式并移除未使用的属性。这将更改现有组件的行为以匹配新默认值。使用此工具前需先运行 `next-image-to-legacy-image` 代码修改工具。
+- [**`next-image-to-legacy-image` codemod**](/docs/app/guides/upgrading/codemods#next-image-to-legacy-image):安全自动地将 `next/image` 导入重命名为 `next/legacy/image`,现有组件将保持相同行为。
+- [**`next-image-experimental` codemod**](/docs/app/guides/upgrading/codemods#next-image-experimental):风险较高地添加内联样式并移除未使用的属性。这将改变现有组件的行为以匹配新默认值。使用此 codemod 前需先运行 `next-image-to-legacy-image` codemod。
### `` 组件
-[`` 组件](/docs/app/building-your-application/routing/linking-and-navigating#link-component) 不再需要手动添加 `` 标签作为子元素。此行为在 [12.2 版本](https://nextjs.org/blog/next-12-2) 中作为实验性选项添加,现已成为默认行为。在 Next.js 13 中,`` 始终渲染 `` 并允许您将属性传递给底层标签。
+[`` 组件](/docs/app/building-your-application/routing/linking-and-navigating#link-component) 不再需要手动添加 `` 标签作为子元素。此行为在 [12.2 版本](https://nextjs.org/blog/next-12-2) 作为实验性选项引入,现已成为默认行为。在 Next.js 13 中,`` 始终渲染 `` 并允许您将属性传递给底层标签。
例如:
@@ -80,62 +80,62 @@ import Link from 'next/link'
```
-要将链接升级到 Next.js 13,可以使用 [`new-link` 代码修改工具](/docs/app/guides/upgrading/codemods#new-link)。
+要升级至 Next.js 13 的链接,可以使用 [`new-link` codemod](/docs/app/guides/upgrading/codemods#new-link)。
### `