logoAnt Design

⌘ K
  • 设计
  • 研发
  • 组件
  • 博客
  • 资源
  • 国内镜像
5.25.0
  • 👀 视觉回归测试
  • 为什么禁用日期这么难?
  • 封装 Form.Item 实现数组转对象
  • 行省略计算
  • 📢 v4 维护周期截止
  • antd 里常用的 TypeScript 工具方法
  • 一个构建的幽灵
  • 当 Ant Design 遇上 CSS 变量
  • API 的历史债务
  • 灵动的 Notification
  • 色彩模型与颜色选择器
  • 主题拓展
  • 虚拟表格来了!
  • 快乐工作主题(一)
  • 动态样式去哪儿了?
  • Suspense 引发的样式丢失问题
  • 打包体积优化
  • 你好,GitHub Actions
  • 所见即所得
  • 静态方法之痛
  • SSR 静态样式导出
  • 依赖排查
  • 贡献者开发维护指南
  • 转载-如何提交无法解答的问题
  • 新的 Tooltip 对齐方式
  • 非必要的渲染
  • 如何成长为 Collaborator
  • Modal hook 的有趣 BUG
  • antd 测试库迁移的那些事儿
  • Tree 的勾选传导
  • getContainer 的一些变化
  • 组件级别的 CSS-in-JS

API 的历史债务

相关资源

Ant Design X
Ant Design Charts
Ant Design Pro
Pro Components
Ant Design Mobile
Ant Design Mini
Ant Design Web3
Ant Design Landing-首页模板集
Scaffolds-脚手架市场
Umi-React 应用开发框架
dumi-组件/文档研发工具
qiankun-微前端框架
Ant Motion-设计动效
国内镜像站点 🇨🇳

社区

Awesome Ant Design
Medium
Twitter
yuque logoAnt Design 语雀专栏
Ant Design 知乎专栏
体验科技专栏
seeconf logoSEE Conf-蚂蚁体验科技大会
加入我们

帮助

GitHub
更新日志
常见问题
报告 Bug
议题
讨论区
StackOverflow
SegmentFault

Ant XTech logo更多产品

yuque logo语雀-构建你的数字花园
AntV logoAntV-数据可视化解决方案
Egg logoEgg-企业级 Node.js 框架
Kitchen logoKitchen-Sketch 工具集
Galacean logoGalacean-互动图形解决方案
xtech logo蚂蚁体验科技
主题编辑器
Made with ❤ by
蚂蚁集团和 Ant Design 开源社区
loading

在升级 Ant Design 的过程中,你或许收到过这种警告:

text
Warning: [antd: XXX] `old prop` is deprecated. Please use `new prop` instead.

这是因为 antd 在开发过程中,有一些 API 设计的不合理导致的历史债务。举个例子,在 antd v3 及以前,TreeSelect 的代码是从 Select 中直接复制出来并在此基础上做的拓展。他们的搜索样式存在差异:

SelectTreeSelect

而后的维护过程中,开发者希望可以受控控制搜索框的内容。而不巧的是,这个需求是不同的机缘巧合被提出并由不同的开发者贡献了代码。于是两者添加了不同的属性,一个叫做 inputValue 而另一个叫做 searchValue:

tsx
// Select 在 combobox 模式下,搜索框就是输入框,`inputValue` 看起来很合理
<Select inputValue="search" />
// TreeSelect 的搜索框在弹出层,`searchValue` 也很合理
<TreeSelect searchValue="search" />

在多选模式下,类 Select 组件在选择内容后会清除搜索框内容。但是有些场景下,开发者希望能够保留。因而 TreeSelect 和 Select 又添加了 autoClearSearchValue 属性。

等等,Select 明明叫 inputValue,为什么要叫 autoClearSearchValue?明显应该叫做 autoClearInputValue 呐。如果我们在现有的 API 上继续生长其他的同类 API 风格。你会发现组件的 prop 变得越来越分裂。这也会导致代码维护出现坏味道。例如上面这个例子,在之后我们对类 Select 组件抽成了统一的 UI 层并将其合并到 rc-select 组件中。rc-tree-select 只需要实现弹出层的内容,而输入框的结构和样式可以和 Select 完全复用。但是由于两者的 API 不一致,导致我们需要额外的处理,所以我们在迭代过程中需要对这些 API 债务进行重构并将其统一起来。(在 v4 中,我们将其合并为了 searchValue 并且对设计也进行了统一)

然而世上没有银弹,我们无法在一开始就设计出完美的 API。有一些 API 在设计之初显得非常合理,而随着迭代又会发现或多或少不合时宜。比如说弹出层早期起名为 dropdown,这对应了 Dropdown 以及类 Select 组件的弹出内容。但是对于 Tooltip 而言,dropdown 显然是不适合的。从统一的角度看,popup 会更适合。

废弃警告

在维护过程中,我们逐渐统一了 API 命名规范(API Naming rules)。在添加新的 feature 时,优先从现存的 API 中寻找接近。对于现存的 API,逐步添加废弃警告。为了保持兼容,我们的策略是每个版本提供的废弃警告会继续兼容一个大版本,而在下下个大版本中移除它。例如在 v4 中添加了废弃警告,那么在 v5 中仍然可以使用,但是在 v6 中将会被移除。以此确保开发者有足够的时间进行迁移。

但是从开发者角度看,这也并不合理。开发者本身只是对 antd 进行了升级,却要因为组件库 API 设计的失误而遭受 console 的侵扰。如果在废弃警告中混入几个使用警告,开发者往往很难发现它们。这种情况在大版本升级中尤为显著,业务可能并没有给你足够的时间去做升级迁移,因而不得不使用兼容包以及其他的一些技术手段让它先跑起来。而对于冗长的废弃警告,开发者不得不选择暂时(或者永远)无视它们。针对这种情况,使用警告会更为重要,因而我们提出了 Warning Filter RFC。

警告过滤

通过 ConfigProvider 的 warning 属性,可以将废弃信息进行聚合:

tsx
<ConfigProvider warning={{ strict: false }} />

聚合后,原本打平的废弃信息会合并为一个数组在 console 中展示。而对于使用警告则不会影响:

Merged Message

拓展问题

如上所述,API 设计不存在银弹。为了防止 breaking change,我们一般不会改动现有的 API 实现。但是对于一些约定的内容,这就会造成麻烦。比如说 ref 组件是很典型的约定,只要是 React 的开发者就能明白,通过 ref 可以获取 DOM 节点以及做一些诸如 focus 的基本操作。但是对于复合组件而言,调用方法和 DOM 不一定能够统一。比如说 Table 组件的 ref 显然应该是最外层的 div,但是对于 scrollTo 方法则应该对应到滚动容器上(如果是 VirtualTable 则应该交由内部的 rc-virtual-list 进行处理)。在 antd mobile 中 ref 被设计为复合结构,DOM 节点总是通过 nativeElement 返回:

tsx
export interface SampleRef {
nativeElement: HTMLElement;
focus(): void;
blur(): void;
}

而在 antd 中,由于我们早期没有对 ref 进行约定,导致在实现方法是就遇到了难题。不过好在有 Proxy 支持,我们可以通过 Proxy 对 ref 进行拦截并返回我们想要的结果:

tsx
useImperativeHandle(
ref,
() =>
new Proxy(divRef.current, {
get(target, key) {
// ...
},
}),
);

通过这种方式,我们可以继续兼容之前的使用。它仍然是一个 DOM 节点,但是同样也支持了 SampleRef 的定义调用。

总结

API 设计是个难题,随着技术栈以及组件本身的迭代。一些设计会逐渐腐朽,而 API 升级本身对于开发者也是痛苦的。我们希望通过这篇文章,让开发者能够理解我们的设计思路以及在升级过程中的一些问题。如果你有任何的建议或者想法,欢迎在 GitHub 中讨论。