Skip to content

Update translations #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/update-docs-ci.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
name: Update Docs CI

on:
push:
pull_request:
types:
- opened
- synchronize
branches:
- docs/update-translations
- docs/sync-nextjs-documentation


jobs:
deploy-and-update-index:
Expand Down
394 changes: 394 additions & 0 deletions apps/docs/content/zh-hans/blog/building-apis-with-nextjs.mdx

Large diffs are not rendered by default.

202 changes: 202 additions & 0 deletions apps/docs/content/zh-hans/blog/composable-caching.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
---
source-updated-at: 2025-05-29T18:05:49.000Z
translation-updated-at: 2025-05-29T19:43:25.738Z
title: Next.js 的可组合式缓存方案
description: 深入了解 'use cache' 的 API 设计及其优势
author:
- name: Lee Robinson
image: /static/team/lee.jpg
date: 2025-01-03T14:00:00.507Z
image: >-
https://h8DxKfmAPhn8O0p3.public.blob.vercel-storage.com/static/blog/composable-caching/twitter-card.png
---

我们正在为 Next.js 开发一套简单而强大的缓存模型。在之前的文章中,我们探讨了[缓存探索之旅](/blog/our-journey-with-caching)以及如何最终实现 `'use cache'` 指令。

本文将重点讨论 `'use cache'` 的 API 设计及其优势。

[什么是 `'use cache'`?](#what-is-use-cache)
--------------------------------------------

`'use cache'` 通过按需缓存数据或组件来提升应用性能。

这是一个 JavaScript "指令"——您添加到代码中的字符串字面量——它会告知 Next.js 编译器进入不同的"边界"。例如,从服务端切换到客户端。

这与 React 的 `'use client'` 和 `'use server'` 等指令理念相似。指令是定义代码运行位置的编译器指示,让框架能为您优化和编排各个代码片段。

[工作原理](#how-does-it-work)
--------------------------------------

从一个简单示例开始:

```
async function getUser(id) {
'use cache';
let res = await fetch(`https://api.vercel.app/user/${id}`);
return res.json();
}
```

在底层,由于 `'use cache'` 指令,Next.js 会将此代码转换为服务端函数。编译期间,会找到该缓存条目的"依赖项"并将其作为缓存键的一部分。

例如,`id` 成为缓存键的一部分。如果我们多次调用 `getUser(1)`,会返回缓存服务端函数的记忆化输出。更改此值将在缓存中创建新条目。

再看一个在服务端组件中使用缓存函数和[闭包](https://v0.dev/chat/5kD47RIecQK?b=b_rCP4CvfbFFW)的例子:

```
function Profile({ id }) {
async function getNotifications(index, limit) {
'use cache';
return await db
.select()
.from(notifications)
.limit(limit)
.offset(index)
.where(eq(notifications.userId, id));
}

return <User notifications={getNotifications} />;
}
```

这个例子更复杂。您能找出需要作为缓存键的所有依赖项吗?

参数 `index` 和 `limit` 很直观——如果这些值改变,我们会选择通知的不同分片。但用户 `id` 呢?它的值来自父组件。

编译器能理解 `getNotifications` 也依赖于 `id`,其值会自动包含在缓存键中。这避免了因缓存键中依赖项错误或缺失导致的整个缓存问题类别。

[为何不使用缓存函数?](#why-not-use-a-cache-function)
--------------------------------------------------------------

回顾上一个例子。我们是否可以用 `cache()` 函数替代指令?

```
function Profile({ id }) {
async function getNotifications(index, limit) {
return await cache(async () => {
return await db
.select()
.from(notifications)
.limit(limit)
.offset(index)
// 糟糕!如何将 id 包含在缓存键中?
.where(eq(notifications.userId, id));
});
}

return <User notifications={getNotifications} />;
}
```

`cache()` 函数无法查看闭包并识别 `id` 值应作为缓存键的一部分。您需要手动指定 `id` 为键的一部分。如果忘记或操作不当,可能导致缓存冲突或数据过时。

闭包可以捕获各种局部变量。简单处理可能会意外包含(或遗漏)您未预期的变量。这会导致缓存错误数据,或者如果敏感信息泄露到缓存键中,可能引发缓存污染风险。

`'use cache'` 为编译器提供了足够上下文来安全处理闭包并正确生成缓存键。纯运行时方案(如 `cache()`)需要您手动完成所有操作——很容易出错。相比之下,指令可通过静态分析可靠处理所有底层依赖。

[如何处理不可序列化的输入值?](#how-are-non-serialized-input-values-handled)
--------------------------------------------------------------------------------------------

我们有两种不同类型的缓存输入值:

* **可序列化**:指输入可转换为稳定、基于字符串的格式且不丢失意义。虽然很多人首先想到 `JSON.stringify`,但我们实际使用 React 的序列化(如通过服务端组件)处理更广泛的输入——包括 Promise、循环数据结构和其它复杂对象。这超出了普通 JSON 的能力范围。
* **不可序列化**:这些输入不是缓存键的一部分。尝试缓存这些值时,我们会返回服务端"引用"。Next.js 在运行时使用该引用恢复原始值。

假设我们记得在缓存键中包含 `id`:

```
await cache(async () => {
return await db
.select()
.from(notifications)
.limit(limit)
.offset(index)
.where(eq(notifications.userId, id));
}, [id, index, limit]);
```

如果输入值可序列化,这能正常工作。但如果 `id` 是 React 元素或更复杂的值,我们就需要手动序列化输入键。考虑一个基于 `id` 属性获取当前用户的服务端组件:

```
async function Profile({ id, children }) {
'use cache';
const user = await getUser(id);

return (
<>
<h1>{user.name}</h1>
{/* 更改 children 不会破坏缓存...为什么? */}
{children}
</>
);
}
```

逐步解析其工作原理:

1. 编译期间,Next.js 看到 `'use cache'` 指令并将代码转换为支持缓存的特殊服务端函数。编译时不进行缓存,而是 Next.js 建立运行时缓存所需的机制。
2. 当代码调用"缓存函数"时,Next.js 序列化函数参数。任何不能直接序列化的内容(如 JSX)会被替换为"引用"占位符。
3. Next.js 检查给定序列化参数是否存在缓存结果。如果未找到结果,函数会计算新值进行缓存。
4. 函数完成后,返回值被序列化。返回值的不可序列化部分会转换回引用。
5. 调用缓存函数的代码反序列化输出并评估引用。这使得 Next.js 能用实际对象或值替换引用,意味着像 `children` 这样的不可序列化输入可以保持其原始、未缓存的值。

这意味着我们可以安全地仅缓存 `<Profile>` 组件而不缓存子组件。在后续渲染中,不会再次调用 `getUser()`。`children` 的值可能是动态的,或是具有不同缓存生命周期的单独缓存元素。这就是可组合式缓存。

[似曾相识...](#this-seems-familiar)
--------------------------------------------

如果您觉得"这感觉与服务端和客户端组合的模式相同"——完全正确。这有时被称为"甜甜圈"模式:

* **外层**甜甜圈是处理数据获取或重型逻辑的服务端组件
* **中间**孔洞是可能具有交互性的子组件

```tsx filename="app/page.tsx"
export default function Page() {
return (
<ServerComponent>
{/* 创建一个通向客户端的孔洞 */}
<ClientComponent />
<ServerComponent />
);
}
```

`'use cache'` 同理。甜甜圈是外层组件的缓存值,孔洞是在运行时填充的引用。这就是为什么更改 `children` 不会使整个缓存输出失效。子组件只是稍后填充的引用。

[标签与失效机制](#what-about-tagging-and-invalidation)
----------------------------------------------------------------------------

您可以通过不同[配置文件](/docs/app/api-reference/functions/cacheLife)定义缓存生命周期。我们提供一组默认配置,但您也可以根据需要定义自定义值。

```
async function getUser(id) {
'use cache';
cacheLife('hours');
let res = await fetch(`https://api.vercel.app/user/${id}`);
return res.json();
}
```

要使特定缓存条目失效,您可以[标记缓存](/docs/app/api-reference/functions/cacheTag)然后调用 `revalidateTag()`。一个强大模式是您可以在获取数据后(例如从 CMS)标记缓存:

```
async function getPost(postId) {
'use cache';
let res = await fetch(`https://api.vercel.app/blog/${postId}`);
let data = await res.json();
cacheTag(postId, data.authorId);
return data;
}
```

[简单而强大](#simple-and-powerful)
-------------------------------------------

我们设计 `'use cache'` 的目标是让缓存逻辑编写既简单又强大。

* **简单**:您可以通过局部推理创建缓存条目。无需担心全局副作用,如遗忘缓存键条目或意外更改代码库其它部分。
* **强大**:您可以缓存不仅仅是静态可分析代码。例如,运行时可能变化的值,但您仍希望缓存评估后的输出结果。

`'use cache` 在 Next.js 中仍处于**实验阶段**。我们期待您试用后的早期反馈。

[查阅文档了解更多](/docs/app/api-reference/directives/use-cache)。
38 changes: 38 additions & 0 deletions apps/docs/content/zh-hans/blog/create-next-app.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
source-updated-at: 2025-05-29T18:05:49.000Z
translation-updated-at: 2025-05-29T19:42:29.584Z
title: 介绍 Create Next App
description: >-
今天我们很高兴推出全新的 Create Next App。通过一条命令,Create Next App 就能搭建一个基于 Next.js 的现代化 React 应用。
author:
- name: Joe Haddad
image: /static/team/timer.jpg
- name: Tim Neutkens
image: /static/team/tim.jpg
date: 2019-10-09T15:02:30.543Z
image: >-
https://h8DxKfmAPhn8O0p3.public.blob.vercel-storage.com/static/blog/create-next-app/twitter-card.png
---

今天我们很高兴向大家介绍全新的 Create Next App。

通过一条命令,Create Next App 就能搭建一个基于 Next.js 的现代化 React 应用。

只需运行以下命令即可开始使用:

```bash filename="Terminal"
npx create-next-app
```

Create Next App 经过彻底重构,旨在提供最佳的开发者体验:

* **交互式体验**:现在运行不带参数的 `npx create-next-app` 会启动交互式引导流程,帮助您完成项目设置。
* **零依赖**:项目初始化现在最快只需 **1 秒**。Create Next App 零依赖,安装包仅 **604 kB**。优化前的旧版本为 **5.38 MB**,体积减少了超过 **4.7 MB**!
* **离线支持**:Create Next App 会自动检测离线状态,并使用本地包缓存来初始化项目。
* **新的默认项目模板**:采用专为现代 Next.js 应用设计的新模板。由于 Create Next App 现在与 Next.js 同步维护,该模板将始终与最新版 Next.js 保持同步!
* **示例支持**:可以从 [Next.js 示例库](https://github.com/vercel/next.js/tree/canary/examples) 中选择示例来初始化应用(例如 `npx create-next-app --example api-routes`)。
* **经过测试**:该包属于 Next.js 单体仓库,使用与 Next.js 相同的集成测试套件进行测试,确保每个版本都能如期工作。

Create Next App 之前是一个由 [社区维护](https://open.segment.com/create-next-app/) 的项目,但我们认为有必要精心打造用户对 Next.js 的第一印象。特别是当我们在 [Next.js 示例库](https://github.com/vercel/next.js/tree/canary/examples) 中推荐它时。

我们与 [Segment](https://segment.com/) 合作完成了包的归属权转移,特别感谢 [Fouad Matin](https://twitter.com/fouadmatin) 等人前期的维护工作。
108 changes: 108 additions & 0 deletions apps/docs/content/zh-hans/blog/incremental-adoption.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
---
source-updated-at: 2025-05-29T18:05:49.000Z
translation-updated-at: 2025-05-29T19:42:36.412Z
title: 渐进式采用 Next.js
description: >-
了解将 Next.js 逐步引入开发工作流的不同策略。
author:
- name: Lee Robinson
image: /static/team/lee.jpg
date: 2020-11-18T14:00:00.507Z
image: >-
https://h8DxKfmAPhn8O0p3.public.blob.vercel-storage.com/static/blog/incremental-adoption/twitter-card.png
---

[Next.js](https://nextjs.org/) 专为渐进式采用而设计。通过 Next.js,您可以继续使用现有代码并按需添加任意多(或少)的 React 功能。从小规模开始并逐步添加更多页面,可以避免完全重写导致的功能开发脱轨。

许多公司需要现代化其技术栈以降低成本、提高开发效率并为客户提供最佳体验。组件驱动开发显著提升了现代代码库的部署速度和可复用性。

凭借每月超过 [800 万次下载量](https://www.npmtrends.com/react),React 已成为开发者首选的组件驱动方案。作为生产级 React 框架,Next.js 让您能够逐步采用 React。

[动机](#motivation)
-------------------------

在移动优先的时代,优化和追踪 [核心网页指标](/analytics) 对成功至关重要。您的客户可能分布在全球各地,网络速度参差不齐。页面加载或操作完成每多花一秒(甚至毫秒),都可能影响销售、展示或转化效果。

若您正在现代化技术栈,可能面临以下挑战:

* 应用存在多年遗留代码,难以维护且完全重写需耗费数年(及数百万美元)
* 随着应用规模和复杂度增长,页面加载时间持续增加,简单营销页与复杂页面同样缓慢
* 尝试扩展开发团队时,难以在现有代码库中新增开发人员
* CI/CD 和 DevOps 流程过时,降低开发效率且难安全可靠地发布变更
* 应用未针对移动设备优化,且无法更新全局样式而不破坏其他部分

您知道需要采取行动,但可能对[从何开始](https://www.psychologytoday.com/us/blog/mindfully-present-fully-alive/201804/the-only-way-eat-elephant)感到茫然。通过渐进式采用 Next.js,您可以逐步解决上述问题并改造应用。下面探讨几种在现有技术栈中引入 Next.js 的策略。

[策略](#strategies)
-------------------------

### [子路径](#subpath)

首项策略是配置服务器或代理,使特定子路径下的所有内容指向 Next.js 应用。例如,现有网站位于 `example.com`,可配置代理使 `example.com/store` 提供 Next.js 电商应用。

使用 [`basePath`](/docs/pages/api-reference/next-config-js/basePath) 可配置 Next.js 应用的资源与链接,使其自动适配新子路径 `/store`。由于 Next.js 中每个页面都是[独立路由](/docs/pages/building-your-application/routing),`pages/products.js` 等页面将路由至应用内的 `example.com/store/products`。

```js filename="next.config.js"
module.exports = {
basePath: '/store',
};
```

了解更多关于 `basePath` 的信息,请参阅[文档](/docs/pages/api-reference/next-config-js/basePath)。

(**注意:** 此功能需 Next.js 9.5 及以上版本。若使用旧版,请先升级。)

### [重定向](#rewrites)

第二项策略是创建指向域名根 URL 的新 Next.js 应用,然后在 `next.config.js` 中使用 [`rewrites`](/docs/pages/api-reference/next-config-js/rewrites) 将部分子路径代理至现有应用。

例如,假设您创建了从 `example.com` 提供服务的 Next.js 应用,并配置如下 `next.config.js`。现在,已添加到该 Next.js 应用的页面请求(如添加了 `pages/about.js` 后的 `/about`)将由 Next.js 处理,其他路由请求(如 `/dashboard`)将代理至 `proxy.example.com`。

```js filename="next.config.js"
module.exports = {
async rewrites() {
return [
// 需定义无操作重定向以优先检查所有页面/静态文件
{
source: '/:path*',
destination: '/:path*',
},
{
source: '/:path*',
destination: `https://proxy.example.com/:path*`,
},
];
},
};
```

了解更多关于重定向的信息,请参阅[文档](/docs/pages/api-reference/next-config-js/rewrites)。

### [微前端与单体仓库及子域名](#micro-frontends-with-monorepos-and-subdomains)

Next.js 与 [Vercel](https://vercel.com) 能轻松实现[微前端](https://martinfowler.com/articles/micro-frontends.html)架构,并支持以[单体仓库](https://vercel.com/blog/monorepos)形式部署。这允许通过[子域名](https://zh.wikipedia.org/wiki/子域名)逐步引入新应用。微前端的优势包括:

* 更小、内聚性更强且更易维护的代码库
* 组织结构更灵活,团队可解耦自治
* 能以渐进方式升级、更新甚至重写部分前端

![](https://h8DxKfmAPhn8O0p3.public.blob.vercel-storage.com/static/blog/incremental-adoption/light-arch.png)

> 部署至 Vercel 的单体仓库架构示例

设置好单体仓库后,照常推送更改至 Git 仓库,您将看到提交被部署到已连接的 Vercel 项目。告别过时的 CI/CD 流程。

![](https://h8DxKfmAPhn8O0p3.public.blob.vercel-storage.com/static/blog/incremental-adoption/dark-comment.png)

> Git 集成提供的部署 URL 示例

[结论](#conclusion)
-------------------------

Next.js 专为渐进式融入现有技术栈而设计。Vercel 平台通过与 GitHub、GitLab 和 Bitbucket 无缝集成,为每次代码变更提供部署预览,使其成为协作式体验。

* 通过[快速刷新](/docs/architecture/fast-refresh)即时本地预览变更,提升开发效率
* 推送变更创建[分支预览](https://vercel.com/github),优化与利益相关者的协作
* 合并 PR 后通过 [Vercel](https://vercel.com) 部署至生产环境,无需复杂 DevOps

了解更多,请阅读关于[子路径](/docs/pages/api-reference/next-config-js/basePath)和[重定向](/docs/pages/api-reference/next-config-js/rewrites)的文档,或[部署微前端示例](https://vercel.com/import/project?template=https://github.com/vercel/next.js/tree/canary/examples/with-zones)。
Loading