Ant Design v6:zeroRuntime 使用与踩坑总结
在升级到 React 19 + Ant Design v6 之后,我明显感觉到页面的 CLS(Cumulative Layout Shift)性能变差。
最直观的表现是:
- 首屏渲染时样式会闪一下
- 组件尺寸在 JS 执行完成后才稳定
这在静态站点(如 Docusaurus)中尤为明显。
一开始我采用的是最“原始”的缓解方案: 通过 CSS 给部分组件写死 min-width / min-height,虽然能减少 CLS,但显然不是一个优雅或可维护的解决方案。
因此,我开始尝试 Ant Design 官方提供的 zeroRuntime 方案。
本文主要记录我在使用 Ant Design zeroRuntime 过程中遇到的核心问题、踩过的坑,以及最终为什么选择了 「只使用 CSS + 单一主题」 这一方案。
问题根因:Ant Design 的 CSS 并不是完整样式
在排查过程中,我发现了一个关键事实:
antd/dist/antd.css 中大量使用 var(--ant-*)
但这些 CSS 变量并不存在于任何静态 CSS 文件中原因在于 Ant Design v6 的样式机制:
- Ant Design v6 默认使用 CSS-in-JS
--ant-*这类 CSS 变量是:- 由 JS 在运行时动态注入
- 而非静态 CSS 中提前定义
对于 Docusaurus 这类预渲染的静态 HTML 来说:
- 首屏只包含 HTML 结构
- CSS 变量尚未注入
- 等 JS 执行完成后样式才完整
- 因此不可避免地产生 CLS 和样式闪烁
即使已经引入了 antd/dist/antd.css, 只要 CSS 变量依赖运行时注入,首屏 HTML 本身仍然“感知不到完整样式”。
zeroRuntime 的设计原理
zeroRuntime 的核心目标非常明确:
在构建阶段生成完整样式,而不是依赖运行时 CSS-in-JS 注入
官方文档对此的描述是: zeroRuntime 会在构建时,根据配置生成一份完整、可直接使用的 CSS 文件。
具体表现为:
构建阶段
- 通过
genAntdCss.mjs(或类似脚本) - 基于指定的
theme、token 配置 - 生成一份完整、静态 CSS
运行阶段
不再生成或注入样式
组件默认假设:
所有样式已经存在于页面中
这直接带来一个非常重要的结论:
使用 zeroRuntime 后,所有样式相关配置必须在构建期确定
zeroRuntime 下的能力边界(能做 / 不能做)
这是使用 zeroRuntime 必须接受的现实边界。
✅ 可以做的事情
- 在生成脚本中配置全局
theme - 构建阶段生成一套固定主题的 CSS
- 使用
ConfigProvider设置locale(不影响样式)
❌ 不能做的事情
- 在组件中通过
ConfigProvider动态设置theme - 使用
theme.useToken() - 运行时切换主题色、
componentSize等样式相关 token
一句话总结:
zeroRuntime = 样式在构建期“完全写死”,JS 不再参与样式生成
这并不是限制,而是 zeroRuntime 的设计前提。
cssVar.key 必须完全一致
在启用 zeroRuntime 时,有一个非常容易忽略、但影响极大的配置点:
cssVar: {
key: "aishort";
}这个 key 的作用是: 作为 Ant Design CSS 变量的命名空间标识。
⚠️ 必须保证以下两处完全一致:
- 生成 CSS 的脚本配置中
- 应用主题时使用的配置中
如果不一致:
- 构建出来的 CSS 变量无法被组件正确命中
- zeroRuntime 实际上是“未生效”的
- 页面刷新时会出现:
- 字体大小闪烁
- 组件样式先错后对
一个非常简单的判断方法:
刷新页面是否出现样式闪动?
- 有 → zeroRuntime 没有真正生效
- 没有 → 样式已经完全静态化
多主题:理论可行,工程上不值得
我曾尝试过生成 浅色 + 深色两套 CSS,并在运行时进行切换。
结论非常明确:
复杂度极高,不值得投入。
主要问题包括:
多套 CSS 同时存在时:
- 后加载的主题会覆盖前者
即使切换主题:
- 实际生效的仍然是权重更高的那套变量
为了解决变量覆盖问题:
- 需要大量人为拆分、加权、隔离
可维护性和可预测性急剧下降
最终结论是:
zeroRuntime 并不适合运行时多主题切换
因此目前站点仅保留 单一深色主题,不再提供主题切换能力。
ConfigProvider 的现实限制
Ant Design v6 提供了大量基于 ConfigProvider 和 theme.useToken 的定制能力。
但在 zeroRuntime 模式下:
- 这些能力几乎全部不可用
- 官方文档中也明确指出:
- zeroRuntime 仅适用于 构建期确定样式的场景
这意味着:
- 原本基于 Ant Design 统一主题系统的设计
- 需要回退到:
- 写死样式
- 预编码 token
- 构建期生成 CSS
本质上,这是一次明确的取舍:
用灵活性,换取首屏性能和样式稳定性
总结
zeroRuntime 并不是一个“无脑开启就能提升体验”的方案,它更像是一个 为静态站点和性能极致优化而设计的工程取舍。
它确实解决了:
- CLS 问题
- 首屏样式闪烁
- 运行时样式注入带来的不确定性
但代价同样明确:
- 主题必须在构建期确定
- 动态主题能力几乎全部失效
- 多主题的工程复杂度呈指数级上升
如果你的项目:
- 是静态站点(如 Docusaurus)
- 主题风格长期稳定
- 更看重首屏性能和视觉一致性
那么 zeroRuntime 是值得使用的方案。
但如果你需要:
- 运行时动态主题
- 高度可配置的设计系统
那么就必须接受 CSS-in-JS 带来的运行时成本, 或者等待 Ant Design 在未来提供更成熟的解决路径。