首页
/ Ant Design 项目开发指南:从目录结构、导入规范到 PR 与 Changelog 全流程实战

Ant Design 项目开发指南:从目录结构、导入规范到 PR 与 Changelog 全流程实战

2026-09-06 13:06:39作者:何将鹤

Ant Design(npm 包名 antd)是一个企业级 React 组件库,仓库中的 CLAUDE.md 是其面向开发者(尤其是 AI 编程助手)的完整开发规范文档,涵盖项目结构、编码规范、样式合并优先级、Demo/测试导入规则、文档与国际化规范、PR 流程以及 Changelog 编写准则。本文以该文档为主体,结合仓库源码逐项印证,帮助你快速掌握在 ant-design 仓库中提交高质量代码的完整方法论。

一、项目概况与目录结构

文档开篇定义了 ant-design 的基本技术画像,这些事实均可在当前仓库中得到验证:

  • React 组件库,以 npm 包 antd 发布;
  • TypeScript + React 技术栈,tsconfig.json 开启了 strictstrictNullChecksnoUnusedLocals 等严格模式;
  • CSS-in-JS 架构,基于 @ant-design/cssinjspackage.json 中依赖版本为 ^2.1.2);
  • 支持 Design Token 主题系统、暗色模式、RTL 布局、SSR、国际化(150+ 语言),对应 components/theme 主题系统与 components/locale 国际化目录(当前包含 70 余个语言文件)。

文档给出的仓库结构如下,这也是理解后文所有规范的空间坐标系:

ant-design/
├── components/              # 组件源代码(84+ 组件)
│   ├── component-name/      # 单个组件目录
│   │   ├── ComponentName.tsx      # 主组件实现
│   │   ├── demo/                  # 演示代码(*.tsx 和 *.md)
│   │   ├── style/                 # 样式系统(index.ts / token.ts)
│   │   ├── __tests__/            # 单元测试
│   │   ├── index.en-US.md        # 英文文档
│   │   ├── index.zh-CN.md        # 中文文档
│   │   └── index.tsx             # 导出入口
│   ├── _util/                   # 共享工具函数库
│   ├── theme/                   # 主题系统
│   └── locale/                  # 国际化文本
├── tests/                       # 测试工具和共享测试
├── docs/                        # 站点文档
├── CHANGELOG.zh-CN.md           # 中文更新日志
└── CHANGELOG.en-US.md           # 英文更新日志

当前 components 目录下确有 84 个条目,与"84+ 组件"的描述一致。以一个典型组件为例,components/button/ 完整体现了这套目录约定:Button.tsx 是主实现,demo/ 下有 23 个演示文件,style/ 下放样式 token,__tests__/ 放测试,index.en-US.md / index.zh-CN.md 是双语文档。

二、通用编码规范:类型判断优先复用现有工具

文档在"通用编码规范"一节给出了一条优先级明确的规则:

  • 判断数据类型时,优先使用 @rc-component/util 中的 isNonNullableisReactRenderable components/_util/is.ts 中已有的方法,例如 isNumberisStringisPlainObjectisFunctionisThenableisPrimitive
  • 仅当 components/_util/is.ts 中没有合适方法,或场景需要更严格、更特殊的判断逻辑时,才允许使用内联 typeofinstanceof 等判断方式。

阅读 components/_util/is.ts 源码,可以看到这些工具方法的实际定义与文档完全对应,且都提供了 TypeScript 类型收窄(type predicate):

  • isNumbertypeof val === 'number' && !Number.isNaN(val),注意它排除了 NaN;
  • isStringisPlainObjectisFunction:分别收窄为 string、泛型对象、带参数/返回类型推导的函数类型;
  • isThenable:通过 isNonNullable(val) && isFunction(val.then) 判断 PromiseLike 对象,直接体现了"复用 @rc-component/util 工具"这一规范;
  • isPrimitive:非 object 且非函数,或为 null
  • 此外还有 isTransitionEventisWindowisDocumentisHTMLElement 等面向特定场景的判断,均遵循"先用工具函数收窄、再判断"的写法。

这条规范的工程价值在于:统一判断入口可以保证 SSR 环境下(如 isWindowundefined 的保护)行为一致,避免组件各自内联 typeof 造成行为漂移。

三、样式优先级规范:四个 style 入口的合并顺序

文档对"组件根节点同时支持多个样式入口"的场景给出了明确的优先级(从低到高):

ConfigProvider styles.root → ConfigProvider style → 组件 styles.root → 组件 style

并特别强调:如在 util 层预合并根节点样式,预合并结果内部应保持前三者的同样顺序,组件自身 style 保持最终最高优先级。

这条规则的实际含义是:用户"离自己最近"的写法赢。全局注入的样式(ConfigProvider)只能作为兜底,组件实例上显式传入的 style 拥有最终决定权。在 util 层做样式预合并(例如通过 getResetContainerStyle 之类的工具)时,开发者需要自行保证合并对象中 key 的覆盖顺序与上述优先级一致,否则会出现"全局配置覆盖组件显式样式"的反直觉行为。

四、Demo 导入规范:绝对路径优先,例外场景明确

文档对 components/**/demo/ 下的演示文件给出了严格的导入约束:

  1. 常规 demo 文件引入 Ant Design 组件、组件内部模块、工具方法、变量、类型定义时,一律使用绝对路径导入,不使用相对路径导入
  2. 例外components/**/demo/_semantic*.tsx 属于语义文档专用 demo,允许通过相对路径引用 .dumi/hooks/useLocale.dumi/theme/common/* 等站点侧辅助模块;
  3. .dumi/ 目录内部的站点实现文件可按现有目录结构使用相对路径引用本目录模块;当引用仓库内 Ant Design 组件入口时,优先使用项目公开入口或已配置别名;
  4. 允许的导入形式应优先使用项目公开入口或已配置别名:antdantd/es/*antd/lib/*antd/locale/*@@/*
  5. .dumi/* 不是仓库通用的 TS 路径别名,引用 .dumi 内部模块须按文件位置使用相对路径;
  6. 常规 demo 中禁止使用 ..../xxx./xxx 等相对路径引用组件实现或内部模块,包括跨 demo、跨目录复用场景;
  7. 常规 demo 与 .dumi 文件之间不要互相相对引用_semantic*.tsx 等语义 demo 除外);需要复用少量逻辑时优先内联,或提取到可通过绝对路径访问的公共位置。

这些别名之所以可用,根源在 tsconfig.jsonpaths 配置中:

"paths": {
  "@@/*": [".dumi/tmp/*"],
  "antd": ["./components/index.ts"],
  "antd/es/*": ["./components/*"],
  "antd/lib/*": ["./components/*"],
  "antd/locale/*": ["./components/locale/*"]
}

components/button/demo/basic.tsx 为例,可以看到规范的落地形态——组件一律从 antd 公开入口导入:

import React from 'react';
import { Button, Flex } from 'antd';

const App: React.FC = () => (
  <Flex gap="small" wrap>
    <Button type="primary">Primary Button</Button>
    <Button>Default Button</Button>
    ...
  </Flex>
);

为什么 demo 要坚持绝对路径?从源码结构看,站点侧构建(dumi)与包构建(antd-tools run dist)对 demo 的解析方式不同,统一走公开入口 antd 能确保 demo 与真实用户的引用路径一致,尽早暴露出口缺失或循环依赖问题;相对路径则会把站点内部结构与用户 API 混在一起。

五、Test 导入规范:与 Demo 相反的相对路径策略

components/**/__tests__/ 下的测试文件,文档规定了一套与 demo 完全相反的导入策略:

  • 引入 Ant Design 组件或组件内部模块、工具方法、变量、类型定义时,一律使用相对路径导入,不使用绝对路径导入
  • 应优先从当前组件目录、相邻内部模块或共享测试工具目录引用,例如 ..../index../xxx../../_util/*../../../tests/shared/*
  • 禁止__tests__ 目录下使用 antdantd/es/*antd/lib/*antd/locale/*.dumi/*@@/* 这类绝对路径或别名路径去引用仓库内代码;
  • 引用仓库外第三方依赖(react@testing-library/reactdayjs 等)仍按包名正常导入。

components/button/tests/index.test.tsx 的导入部分是这条规范的直接实证:

import Button, { _ButtonVariantTypes } from '..';
import type { GetProp, GetRef } from '../../_util/type';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { act, fireEvent, render, waitFakeTimer } from '../../../tests/utils';
import ConfigProvider from '../../config-provider';
import theme from '../../theme';

测试坚持相对路径的动机在于:测试要覆盖组件内部实现(如 _ButtonVariantTypes、内部 context),而别名 antd 只暴露公开 API;同时,相对导入保证测试始终解析到源码文件而非已构建产物,避免"测的不是正在改的代码"。

测试的运行入口在 package.json 的 scripts 中:npm test(Jest)、npm run test:vitest(Vitest)、npm run test:node(SSR 场景)、npm run test:site(站点测试)、npm run test:all(串联脚本 scripts/test-all.sh)等,可以按改动范围选择对应验证命令。

六、文档规范:API 表格格式与锚点规则

文档给出了组件文档 API 表格的标准模板。英文版:

Property Description Type Default Version Global Config
disabled Whether the component is disabled boolean false - ×
loadingIcon (Only supports global configuration) Custom loading icon ReactNode - - 6.2.0
type Button type primary | default default -

中文版:

参数 说明 类型 默认值 版本 全局配置
disabled 是否禁用 boolean false - ×
loadingIcon (仅支持全局配置) 自定义加载图标 ReactNode - × 6.2.0
type 按钮类型 primary | default default -

各列的填写规则(这是贡献者最容易写错的部分):

  • 参数列:按字母顺序排列;忽略 classNamestyleonClickonKeyDown 等通用属性;onChangeonClick 等事件回调放在最后;
  • 说明列:简洁描述参数作用,仅支持全局配置时需用括号注明;
  • 类型列:使用 TypeScript 定义的类型;
  • 默认值列:字符串用反引号,布尔/数字直接写,无默认值用 -
  • 版本列:新增属性需声明引入的版本号;上个大版本已存在的属性标注 -;仅支持全局配置的属性标注 ×
  • 全局配置列:支持全局配置的属性需标注版本号;上个大版本已支持的标注 ;不支持的标注 ×

文档锚点 ID 规范

  • 中文标题必须手动指定英文锚点:## 中文标题 {#english-anchor-id}
  • 锚点 ID 需符合 ^[a-zA-Z][\w-:\.]*$,长度不超过 32 字符;
  • FAQ 章节下的锚点必须以 faq- 为前缀;
  • 同一问题的中英文锚点保持一致。

国际化规范

七、PR 规范:标题、模板、分支策略与改动类型

标题与内容

  • PR 标题始终使用英文,格式为 类型: 简短描述,示例:fix: fix button style issues in Safari browser
  • PR 内容默认使用英文,可根据用户语言习惯决定。

PR 模板(必须使用)

仓库内置两套模板文件:.github/PULL_REQUEST_TEMPLATE.md(英文)与 .github/PULL_REQUEST_TEMPLATE_CN.md(中文)。文档特别强调:使用 gh pr create 创建 PR 时必须手动填充模板内容(命令行方式不会自动套用模板)。英文模板包含一张改动类型 checklist(🆕 New feature🐞 Bug fix 等),并要求说明对现有功能的影响、浏览器验证清单与截图。

分支策略

  • 新特性开发需基于 feature 分支,PR 目标分支也需为 feature;其余(修复、文档等)提交至 master 分支;
  • 分支命名规范:
    • 功能开发:feat/description-of-feature
    • 问题修复:fix/issue-number-or-description
    • 文档更新:docs/what-is-changed
    • 代码重构:refactor/what-is-changed

PR 改动类型

标记 含义
🆕 新特性提交
🐞 Bug 修复
📝 文档改进
📽️ 演示代码改进
💄 样式/交互改进
🤖 TypeScript 更新
📦 包体积优化
⚡️ 性能优化
🌐 国际化改进

八、Changelog 规范:最容易被误读的贡献环节

文档用了一整节约束 CHANGELOG.zh-CN.mdCHANGELOG.en-US.md 的写法,核心要点如下。

适用范围(普通 PR 不要动 Changelog)

  • 本节仅适用于用户明确要求收集/生成 changelog、准备 release PR、版本发布,或正在编辑两个 CHANGELOG 文件的场景;
  • 普通功能、修复、文档、demo PR 不要求直接修改 CHANGELOG;代码 CR 时也不应仅因缺少 CHANGELOG 改动而提出 finding;
  • 普通 PR 在模板的 Change Log 一栏仅描述本 PR 对用户或开发者的影响,或填写 N/A / No changelog required / 无需更新日志;正式 CHANGELOG 由 release owner 在发布流程中统一整理。

核心原则

  • 必须同时提供中英文两个版本
  • 忽略用户无感知的改动(内部重构、纯测试更新、工具链优化等);
  • 描述"用户或开发者能感知到的变化",而非实现细节;不要写"传递某配置"、"修改 runtime"、"增加 metadata"等内部实现视角;
  • 涉及 Design Token 时,必须列出具体新增、修复或变更的 token 名称;
  • rc-component 等运行时依赖升级默认不作为 changelog;但若带来用户可感知能力、行为变化、类型定义改进或包体积收益,必须单独核查并描述影响;
  • 尽量给出 PR 链接,并统一添加贡献者链接。

条目格式

  • Emoji 置顶:每条以 Emoji 开头,且每条只选一个 Emoji,不叠加;
  • 不加冒号:组件名后不使用英文冒号;
  • 每条必含组件名,组件名不用反引号(Modal、Button 等),属性名/API 用反引号;
  • 中英空格:中文与英文、数字、链接之间保留一个空格。

句式要求:

语言 格式 示例
中文 Emoji 动词 组件名 描述(动词在前) 🐞 修复 Button 在暗色主题下 \color` 的问题。`
英文 Emoji 动词 组件名 描述(动词在前) 🐞 Fix Button reversed \hover` colors in dark theme.`

分组逻辑

  • 同一组件最终有 2 条以上有效改动时,使用 - 组件名 作为分类标题;
  • 单项改动直接写单行条目;
  • 严格禁止只有 1 条有效 changelog 的组件单独分组:过滤、合并、移动分类后必须重新统计各组件有效条目数,最终只有 1 条的组件必须拆回顶层单行条目;
  • 发布版本 changelog 按影响排序:最重要的跨版本变化放最前;同为组件分组时,条目多的组件优先。

Emoji 规范

Emoji 用途
🐞 修复 Bug
💄 样式更新或 token 更新
🆕 新增特性 / 新增属性
🔥 极其值得关注的新增特性
🇺🇸🇨🇳🇬🇧 国际化改动
📖 📝 文档或网站改进
新增或更新测试用例
🛎 更新警告/提示信息
⌨️ ♿ 可访问性增强
🗑 废弃或移除
🛠 重构或工具链优化
⚡️ 性能提升

当前 CHANGELOG.zh-CN.md 中的真实条目完全符合上述所有规则,例如 6.6.2 版本中的:

  • 🐞 修复 Alert、Empty、Card、Tag、Breadcrumb、Segmented、FloatButton、Form.Item 和 Statistic 无法正确渲染数值 0 内容的问题。

单行顶层条目、Emoji 置顶、组件名不带反引号、Form.Item 这类属性用反引号、中英文与链接之间有空格——都可以作为写作时的对照范本。仓库还提供 npm run changelog(串联 lint:changelogscripts/print-changelog.ts)用于校验和输出,npm run lint:changelog 可作为提交前的格式自检。

九、编码行为准则:面向 LLM 协作的工程纪律

文档最后一部分定义了四条行为准则,旨在减少 LLM 编码中常见错误,倾向"谨慎优于速度"(简单任务可自行判断):

  1. 先思考再编码——"不要假设。不要隐藏困惑。呈现权衡。" 实现之前明确陈述假设,存在多种理解时逐一列出而非默默选择,有不懂的地方停下来提问;
  2. 简洁优先——"用最少的代码解决问题。不做臆测性编码。" 不实现超出需求的特性、不为仅使用一次的代码做抽象、不添加未经请求的"灵活性";检验标准是"资深工程师会觉得这过于复杂吗?";
  3. 精准改动——"只改必须改的。只清理自己制造的遗留。" 不"改善"相邻代码、不重构没有问题的代码、与现有风格保持一致;只移除因自己改动而变得未使用的 import/变量/函数;检验标准是"每一行改动都应该能追溯到用户的请求";
  4. 目标驱动执行——"定义成功标准。循环验证直到通过。" 将任务转化为可验证目标("添加校验"→ 为无效输入编写测试并使其通过;"修复 Bug"→ 编写复现测试并使其通过;"重构"→ 确保前后测试都通过),多步骤任务列出 步骤 → 验证方式 的计划。

准则的生效标志:diff 中不必要的改动减少、因过度复杂导致的重写减少、澄清问题发生在实现之前而非犯错之后。

十、落地路径:规范如何与仓库命令衔接

把上述规范落到日常操作,可以按以下流程走:

  1. 改代码前:先读对应组件目录(如 components/button),按第二条规范复用 components/_util/is.ts 等工具方法,按第三条规范处理样式合并优先级;
  2. 写 demo:从 antd 公开入口绝对路径导入(对照 tsconfig.jsonpaths 别名),避免任何相对路径;
  3. 写测试:在 __tests__/ 内使用相对路径引用组件与内部模块,共享测试工具从 tests/shared/* 引入(如 mountTestrtlTest),运行 npm testnpm run test:vitest 验证;
  4. 补文档:按第六节的列规则维护 index.en-US.md / index.zh-CN.md 的 API 表格,中文标题手动指定英文锚点,涉及文案则同步修改 components/locale所有语言文件;
  5. 提 PR:英文标题 类型: 简短描述,按改动类型选择 feat/fix/docs/refactor/ 分支前缀,特性类 PR 目标为 feature 分支,手动填充 .github/PULL_REQUEST_TEMPLATE.md 或中文模板;
  6. 发布时:由 release owner 按第八节规则整理双语 CHANGELOG,用 npm run lint:changelog 校验。

这份文档的价值在于:它把 ant-design 这个 84+ 组件、80 万行级代码库中的隐性约定(样式优先级、导入方向、文档列含义、changelog 分组统计)显式化了。无论是人工贡献还是借助 AI 助手开发,只要逐条对照本文覆盖的规范执行,产出的 diff 就能满足"每一行改动都可追溯、每一个 demo 都与真实引用路径一致、每一条 changelog 都用户可感知"的验收标准。

登录后查看全文
热门项目推荐
相关项目推荐