包含多个产品或大型 API 参考的站点,每次改动都会全量重建。部分构建接收一个集合或一组集合,只构建这些内容:它们的页面、智能体文件和搜索索引。每个切片都是独立的静态站点;由主机把每个切片的路径前缀路由到对应切片,因此访客看到的仍是同一个主机名。
构建单元是集合:某个产品的指南、某个 API 参考,或任何你希望单独维护的部分。请先按无切片时的通常方式建立这些集合,即新集合或 API 参考。
构建切片
将 NIMBUS_BUILD_ONLY 设为一个集合名,或逗号分隔的多个集合名,然后构建:
NIMBUS_BUILD_ONLY=sdk pnpm build
NIMBUS_BUILD_ONLY=docs,api pnpm build在该构建中,其他所有页面集合都是空的:Astro 不会编译、渲染或索引它们的页面,构建时间和内存随切片规模变化。构建会说明它包含什么:
[@cloudflare/nimbus-docs] building only sdk (NIMBUS_BUILD_ONLY); docs, api are empty in this build.只有 astro build 会遵循该变量。astro dev、astro sync 和 nimbus-docs check 使用整个站点,并会提示一次,因此作业级变量不会改变 CI 作业中其他步骤的行为。
以下情况构建会失败:
- 名称不是站点的页面集合之一。页面集合在内容同步后才可知,因此拼错的名称需要先做一次同步才会失败;
- docs 版本集合(
docs及所有docs-<version>)被拆分到多个切片。它们必须一起构建。
切片会写入什么
持有 docs 的切片是根切片:它的写入位置与全站构建相同,并负责提供首页、robots.txt、llms.txt、.well-known 以及源站根目录的其他内容。
其他切片会把站点级文件写在其前缀下,由自己的部署提供,并丢弃根切片负责的根文件:
位于切片前缀下(/sdk/…) |
从输出中丢弃 | 位于资源根目录 |
|---|---|---|
sitemap-index.xml、sitemap-0.xml、sitemap.xml |
/、/robots.txt、/og.png、/nimbus-api/coordinates.json |
_headers,仅包含该切片文件的规则 |
pagefind/ |
/llms.txt、/llms-full.txt、/index.md |
404.html |
llms-full.txt,切片中每个集合一份 |
/.well-known/ |
_redirects(站点输出该文件时);对读取这些文件的主机同时提供两者 |
_nimbus/shiki.css、_astro/(打包的 CSS 和 JS) |
其他集合的 openapi.json 和导航列表 |
包含多个集合的切片会将上述内容放在第一个集合的前缀下。OG 图片保持其路径:/sdk/ 下页面的图片为 /og/sdk/…,集合索引页为 /og/sdk.png,因此切片也会接管这些路径(见下文)。
根切片的 llms.txt 会链接每个集合的 llms.txt,包括未构建的切片,因此智能体可以找到全部内容。未构建的集合以其集合名(sdk 对应 Sdk)或 API 标签命名。
其 sitemap 索引只列出自己的文件;robots.txt 会声明其他切片的 sitemap(见下文)。
部署每个切片
将每个切片的 dist/ 作为独立站点部署,然后在主机名上按路径前缀路由。其余内容都交给根切片,最具体的前缀优先,切片可以按任意顺序部署。每个切片需要四个路径:
- 前缀本身
/sdk,以免不带斜杠的形式落到根切片; - 其下的所有内容
/sdk/*; - 其页面的 OG 图片
/og/sdk/*; - 其索引页的 OG 图片
/og/sdk.png。
切片的页面会从自己的前缀下加载 CSS、JS、样式表和搜索包,因此切片从不依赖另一个切片的部署是否为最新。public/ 中的文件(favicon、字体、根路径图片)保持根路径,并由根切片提供,因此改动这些文件需要部署根切片。
把每个集合恰好分配给一个切片:两个同时持有 api 的部署会响应相同的 URL。任何按前缀路由的主机都可以。以下三种方式:
Cloudflare,每个切片一个 Worker,使用 zone 路由(workers.dev 主机名不支持路由):
{
"name": "docs-site-es",
"compatibility_date": "2026-10-01",
"assets": { "directory": "./dist", "not_found_handling": "404-page" },
"routes": [
{ "pattern": "docs.example.com/sdk", "zone_name": "example.com" },
{ "pattern": "docs.example.com/sdk/*", "zone_name": "example.com" },
{ "pattern": "docs.example.com/og/sdk/*", "zone_name": "example.com" },
{ "pattern": "docs.example.com/og/sdk.png", "zone_name": "example.com" }
]
}根切片的 wrangler.jsonc 保留 "pattern": "docs.example.com/*"。然后执行 NIMBUS_BUILD_ONLY=sdk pnpm build && npx wrangler deploy --config wrangler.sdk.jsonc。
反向代理,为每个切片放置一个静态源站:
location = /sdk { proxy_pass https://sdk.internal; }
location /sdk/ { proxy_pass https://sdk.internal; }
location /og/sdk/ { proxy_pass https://sdk.internal; }
location = /og/sdk.png { proxy_pass https://sdk.internal; }
location / { proxy_pass https://docs-root.internal; }支持重写的静态主机,例如 Netlify 或 Vercel:把每个切片部署为独立站点,并在根站点上添加指向切片部署的重写规则,例如在 _redirects 中写 /sdk/* https://sdk.example.app/sdk/:splat 200,或在 vercel.json 中添加等价的 rewrites 条目,覆盖这四个路径。
每个切片的 _headers 和 _redirects 覆盖该切片的文件;不读取这些文件的主机需要在自身配置中设置相同规则,与全站构建的做法相同(见面向智能体的文档)。
在 base 路径下
在 astro.config 中设置 base: "/docs" 时,切片写入的文件相同,但它提供和声明的每个 URL 都位于 base 下。每个切片的前缀变为 <base><prefix>:
| 位于源站根目录 | 在 base: "/docs" 下 |
|
|---|---|---|
| 切片的路由 | /sdk、/sdk/*、/og/sdk/*、/og/sdk.png |
/docs/sdk、/docs/sdk/*、/docs/og/sdk/*、/docs/og/sdk.png |
| 根切片的路由 | /* |
/docs、/docs/* |
| 切片的 sitemap | /sdk/sitemap-index.xml |
/docs/sdk/sitemap-index.xml |
按全站在 base 下的部署方式部署每个切片:其源站在 /docs 下提供输出,路由器原样传递路径。Astro 不会把 base 放进 dist/,因此切片的页面位于 dist/sdk/…,并通过 /docs/sdk/… 提供。
**Cloudflare:**添加 @astrojs/cloudflare(~14.3.0,配套 wrangler ^4.127.1)并保持 output: "static"。该适配器会把输出嵌套到 base 下(dist/client/docs/…),并把 _headers 和 _redirects 保留在 dist/client,Workers 会在这里读取它们;404.html 仍位于 base 下,Workers 的 404-page 处理仍能找到它。需要手动安装:nimbus-docs add adapter-cloudflare 还会把站点切换为 output: "server"。
import cloudflare from "@astrojs/cloudflare";
export default defineConfig({
base: "/docs",
output: "static",
adapter: cloudflare({ prerenderEnvironment: "node" }),
// …
});{
"name": "docs-site-es",
"compatibility_date": "2026-10-01",
"assets": { "directory": "./dist/client", "not_found_handling": "404-page" },
"routes": [
{ "pattern": "example.com/docs/sdk", "zone_name": "example.com" },
{ "pattern": "example.com/docs/sdk/*", "zone_name": "example.com" },
{ "pattern": "example.com/docs/og/sdk/*", "zone_name": "example.com" },
{ "pattern": "example.com/docs/og/sdk.png", "zone_name": "example.com" }
]
}根切片的文件具有相同的 assets 块,以及 example.com/docs 和 example.com/docs/* 路由。
适配器还会把根切片的发现文件复制到源站根目录。如果源站根目录归你所有,请把 example.com/.well-known/* 添加到其路由;否则由拥有该根目录的站点提供它们。
使用 npx wrangler deploy --config wrangler.jsonc 部署每个切片,包括根切片。不带 --config 时,wrangler 会使用适配器生成的配置,而不是你的配置。
**反向代理:**使用同样的 location 块,并在前面加上 /docs(location /docs/sdk/ { proxy_pass https://sdk.internal; })。不带路径的 proxy_pass 会原样传递请求路径(包含 base),因此每个源站在 /docs 下提供自己的切片。
**支持重写的静态主机:**发布每个切片时把输出放在 /docs 下(发布前将 dist/ 的内容移入 dist/docs/),并把 /docs/sdk/* 重写为 https://sdk.example.app/docs/sdk/:splat。
要在本地检查切片,在 base 下提供每个输出(使用适配器时,npx serve dist/client 已经这样做了),并在 compose.mjs 中按 base 匹配每个切片。OG 图片也位于 base 下(/docs/og/sdk/…),因此 base 也要加在 /og 前面,而不仅是前缀前面:
const base = "/docs";
// …
const slice = slices.find(
({ prefix }) =>
path === `${base}${prefix}` ||
path.startsWith(`${base}${prefix}/`) ||
path.startsWith(`${base}/og${prefix}/`) ||
path === `${base}/og${prefix}.png`,
);声明所有 sitemap
robots.txt 由你提供(src/pages/robots.txt.ts),搜索引擎会从中读取 sitemap。为每个切片添加一行 Sitemap::
`Sitemap: ${new URL(withBase("/sitemap-index.xml", import.meta.env.BASE_URL), config.site).href}`,
`Sitemap: ${new URL(withBase("/sdk/sitemap-index.xml", import.meta.env.BASE_URL), config.site).href}`,搜索引擎只从源站根目录读取 robots.txt。在 base 下,站点自己的文件会在 /docs/robots.txt 提供,因此请把 Sitemap: 行加到源站的 robots.txt,或直接向搜索引擎提交每个切片的 sitemap。
上线前检查切片
切片自己的部署只提供自己的页面,因此请在该部署的预览 URL 上检查这些页面。跨切片的组合与链接需要前面的路由层:一个具有相同前缀的预发布主机名,或在本地为每个切片的 dist/ 启动一个静态服务器,并在前面放一个小路由器来应用规则:
// node scripts/compose.mjs → http://localhost:8788,每个切片的
// dist/ 由 `npx serve -l <port> <dir>`(或任意静态服务器)提供。
import { createServer, request } from "node:http";
const slices = [
{ prefix: "/sdk", port: 8791 },
{ prefix: "/api", port: 8792 },
];
const root = 8790;
createServer((req, res) => {
const path = new URL(req.url, "http://x").pathname;
const slice = slices.find(
({ prefix }) =>
path === prefix || path.startsWith(`${prefix}/`) || path.startsWith(`/og${prefix}/`) || path === `/og${prefix}.png`,
);
const upstream = request({ port: slice?.port ?? root, path: req.url, method: req.method, headers: req.headers }, (up) => {
res.writeHead(up.statusCode ?? 502, up.headers);
up.pipe(res);
});
req.pipe(upstream);
}).listen(8788);跨切片搜索
每个切片构建自己的 Pagefind 索引。搜索客户端加载本切片的包,并把其他切片的包合并进来,因此一个搜索框仍覆盖整个站点。合并在首次搜索时产生开销:40 页的切片约 127 KB,并随每个切片的规模增长。
起始模板的 provider 会这样做。自定义 provider 从 virtual:nimbus/config 读取相同配置:
import { searchIndex } from "virtual:nimbus/config";
searchIndex.bundle; // sdk 切片中为 "/sdk/pagefind/",否则为 "/pagefind/"
searchIndex.merge; // 其他切片的包;全站构建中为 []两者都是实际提供的 URL:在 base: "/docs" 下,包为 /docs/sdk/pagefind/。按原样获取它们,并把 Pagefind 的 baseUrl 设为带一个结尾斜杠的 base(/docs/)。
包含多个集合的切片只写一个包;其他集合的包 URL 返回 404,客户端会跳过它们。
切片之间的链接
切片构建只知道自己的路由。它会从上次构建的 .nimbus/routes.json 中保留其他切片的路由,因此 nimbus-docs lint 仍会检查指向这些切片的链接,并以警告形式报告。没有上次构建时,这些链接会被跳过并给出一次提示。全站构建会恢复完整的检查。
在 CI 中,缓存基础分支全站构建产生的 .nimbus/routes.json,并在切片构建前恢复它;仅构建变更切片展示了该工作流。
仅构建拉取请求触及的内容
要仅构建和预览拉取请求更改的切片,参见仅构建变更切片。
限制
- 按请求渲染只覆盖该切片:切片的服务器在请求时渲染自己集合的页面,不渲染其他内容。Nimbus 没有需要失效的缓存层。
- 站点的 WebMCP 搜索使用根切片的包。
- 不支持跨切片的版本选择器:docs 版本集合必须一起构建。
- 切片不能横跨集合的一部分。
- 切片只获取其集合和
api条目读取的内容源;当src/content.config.ts以构建无法读取的方式设置内容源时,会获取所有源:参见无内容源构建。