---
description: 了解 Doctrine 的开发服务器、静态构建、导航、搜索、多语言和信任边界。
---

# 功能与运行时行为

Doctrine 在开发 SSR、生产预渲染和浏览器 hydration 中使用同一套路由模型与 React 外壳。两个
命令的生命周期不同，但渲染的是同一种页面模型。

<Badge>MDX 优先</Badge>

## 开发服务器

`doctrine dev` 会创建自定义 Vite 服务器。每次收到文档请求时，它加载当前 server entry、查找
归一化路由、import MDX 模块、渲染 HTML，再交给 Vite 转换后返回。

Watcher 处理两类相关源码：

- MDX 的新增、修改与删除会更新 `@amamo/mdx` 记录。
- `meta.ts` 和 `meta.<locale>.ts` 变化会让导航与图标 virtual module 失效，并触发 full reload。

Serve 模式允许相关文件依次保存时出现短暂导航不一致。新页面只有在最近的导航文件引用后才会
进入路由列表；生产构建则执行严格校验。

## 静态网站生成

`doctrine build` 会执行完整流水线：

1. 校验输出安全性、配置、locale 和导航。
2. 用 Vite 构建浏览器与 SSR environment。
3. Import SSR entry 并列出文档路由。
4. 预渲染每条路由和共享的 `404.html`。
5. 启用页面操作时，输出与路由对应的原始 MDX 源码。
6. 移除 Vite 私有 manifest。
7. 根据最终输出目录生成 Pagefind 索引。

每篇文档页面都包含 `<html lang>`、title、description、canonical URL、匹配的语言 alternate 和
共享资源。存在默认 locale 翻译时，Doctrine 还会输出 `x-default` alternate。

## 路由与导航

内容路径会成为路由 slug：

```text
docs/index.mdx                  -> /
docs/guide/page.mdx             -> /guide/
docs/reference/config.mdx       -> /reference/config/
docs/reference/config.zh-CN.mdx -> /zh-CN/reference/config/
```

`index.mdx` 和 `page.mdx` 都代表所在目录，因此同一目录、同一 locale 不能同时使用两者。其他
basename 会变成带结尾斜杠的路由。

导航只来自就近元数据。每个条目只能命名直属页面或直属子目录；数组定义准确顺序、页面标题、
分组标题和可选图标。生产构建会拒绝遗漏、重复或不存在的引用。

页面条目也可以指向同目录的 `.tsx` 文件。Doctrine 只 import 最近语言导航文件明确列出的 TSX，
未列出的文件仍是普通组件，不会被当作页面解析。TSX 页面沿用导航标题和路由，但只放进共享的
顶部导航与页脚，不渲染文档侧栏、正文排版或本页目录。同一 locale 下，MDX 与 TSX 不能使用
相同的页面 basename。

```ts
export default {
  items: [{ page: 'landing', title: '落地页' }],
}
```

```tsx
export default function LandingPage() {
  return <main>在这里构建任意 React 布局。</main>
}
```

Doctrine 默认的 MDX 链接组件会给根相对链接补上部署 base。外部 `https://...` 链接在新标签页
打开。注册自定义 `a` 组件会替换这套行为。

## 页面操作与原始 Markdown

默认情况下，每个 MDX 正文页都有 **Copy Page** 操作，其菜单还会提供 **View as Markdown**。
配置 `githubUrl`，且能够推导仓库内源码根目录或显式设置 `githubSourceRoot` 时，还会显示
**Open in GitHub**。GitHub 操作通过 `blob/HEAD` 指向当前 locale 的真实源文件，让 GitHub 解析到
仓库默认分支。因此，翻译路由会打开带 locale 后缀的源文件，而不是默认 locale 文档。

Doctrine 在开发环境提供同一份原始源码，并在生产构建中将其写入路由对应的 `.md` URL：

```text
/                         -> /index.md
/index/                   -> /index/index.md
/guide/install/           -> /guide/install.md
/zh-CN/guide/install/     -> /zh-CN/guide/install.md
```

额外的 `index` 段用于避免站点根页与真实的 `/index/` 文档争用同一个文件。

这里输出的是原始 MDX，不是从渲染后 HTML 还原的 Markdown，因此可以包含 frontmatter、ESM import、
JavaScript 表达式和 JSX。**Copy Page** 复制同一份源码，**View as Markdown** 则打开对应的 `.md`
URL。

独立 TSX 页面不显示这些操作，也不会生成 `.md` 源码路由。设置 `pageActions: false` 可以在整个
站点关闭页面操作和 MDX 源码输出，但不会移除 Header 中独立的 GitHub 链接。

## 搜索

生产搜索会索引渲染后的 HTML，包括自定义 MDX 组件输出的文字。浏览器只在用户输入查询后加载
Pagefind，短暂等待后最多显示八条结果。<kbd>⌘K</kbd> 和 <kbd>Ctrl+K</kbd> 都能打开搜索。

开发环境不会写永久索引。第一次搜索请求根据当前 SSR 结果在内存中构建 Pagefind 文件。内容或
导航变化会让缓存失效，下一次搜索请求再重建。

## 多语言

默认 locale 没有路由前缀，其他 locale 使用编码后的前缀。只有另一个文档具有相同 slug 时，
语言按钮才会显示。

导航和页面元数据都不会跨 locale fallback；语言切换标签缺失时直接显示 locale code。

Doctrine 内置界面文案只区分两组：名称以 `zh` 开头的 locale 使用中文，其余使用英文。内容、
导航、网站元数据、路由前缀和 `<html lang>` 仍使用完整的配置 locale。

## 主题与自定义

主题控件把 `light` 或 `dark` 写入 `localStorage`。Head 中的内联脚本会在页面 body 渲染前应用
已保存选择；首次访问则使用操作系统偏好。

Doctrine 提供预编译界面 CSS，以及 `Badge`、`Callout`、`Card`、`CardGrid`、`CodeBlock`、
`FileTree`、`FileTreeFolder`、`FileTreeFile`、`InstallTabs`、`LivePreview`、`Step`、`Steps`、`Tab` 和
`Tabs`。语义化的折叠内容、定义列表、图片说明、键盘输入、重点、引用和表格无需包装组件也会
获得完整样式。配置的 CSS 在默认样式之后加载；自定义组件映射在内置映射之后合并，并由 SSR 和
浏览器 hydration 共用。

内置能力与示例见 [MDX 组件](/zh-CN/mdx-components/)。

Tailwind 是可选项。只有文档项目根 `package.json` 在 dependencies、dev dependencies 或
optional dependencies 中声明 `tailwindcss` 时，Doctrine 才启用对应 Vite 插件。

## 部署子路径

`siteUrl` 的 pathname 会成为 Vite 公开 base。Doctrine 把它应用到资源、默认 MDX 链接、语言
路由、Pagefind 结果、canonical URL 和 404 页资源，但不会嵌套输出目录。

## 信任边界

静态产物不会让源码失去执行能力。MDX 可以包含 ESM 和 JavaScript 表达式；被导航引用的 TSX
页面、`doctrine.config.*`、导航模块和自定义组件也都是可执行模块。它们可能在加载配置、打包、
SSR 或 hydration 时运行。只能构建你愿意授予构建机器与部署网站权限的可信源码。

内置 frontmatter schema 只校验可选字符串 `description`，它不是沙箱。编译器的详细边界见
[`@amamo/mdx` 安全模型](https://jikkai.github.io/mdx/security/)。
