KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
03 · antd:运行时生成与一张变量表 — keel 龙骨
这一章回答:同一个页面里三套主题为什么能共存、改一个 token 到底改了什么——以及 antd 的按钮明明和 Tailwind 的类名一样重,凭什么压得住它。
这一章回答:同一个页面里三套主题为什么能共存、改一个 token 到底改了什么——以及 antd 的按钮明明和 Tailwind 的类名一样重,凭什么压得住它。
现场:三块面板、三套主题、一模一样的类名
一个页面里并排放三块面板,每块里都有一个主按钮:
- 第一块用默认主题;
- 第二块套
ConfigProvider,改了token.colorPrimary; - 第三块套
ConfigProvider,用了darkAlgorithm。
三套主题在同一页里共存,互不影响:第一块是蓝的,第二块按你改的那个色走,第三块是暗色的。看起来天经地义。现在把三个按钮的 class 字符串并排抄下来:
ant-btn css-oc1rc0 css-var-root ant-btn-primary ant-btn-color-primary ant-btn-variant-solid
ant-btn css-oc1rc0 css-var-_r_1_ ant-btn-primary ant-btn-color-primary ant-btn-variant-solid
ant-btn css-oc1rc0 css-var-_r_2_ ant-btn-primary ant-btn-color-primary ant-btn-variant-solid
除了中间那一个 css-var-* 类名,三行一模一样。
先别往下翻,给出你的预测: 类名结构相同,意味着它们命中同一批规则;那"三套主题不打架"是靠什么实现的?是本块面板被套了某种作用域隔离(比如 iframe、Shadow DOM)?还是每个按钮被单独写了一套选择器?还是……它们其实共用同一套规则,只有变量值不同?
答案是第三种。这一章就把这条"共用规则、只换变量"的机制拆开——它同时也是这门课里所有"类名加对了却没生效"的前提。
一、样式在组件渲染时被注入 <style>
先看"什么时候变成 CSS"。Tailwind 那条线你已经在 02 章 见过:构建期编译出一份 .css,页面用一份 <link> 引进来。antd 不是。它没有静态的样式文件,样式是在组件渲染的那一刻被注入 <style> 的。
在一个只有一个默认按钮的极简页面上(plain-only.html,没有任何 StyleProvider),head 的子元素顺序是这样的:
STYLE ×18 META TITLE LINK[./tailwind.css]
十八个 <style>,全部 prepend 到 head 开头,位置在 <link> 之前。这不是"顺手插进去",而是刻意的顺序选择——先来的在文档更前,层叠里更早;至于它对这个顺序的依赖到底有多深,是 04 层序 那一章的主线。
这十八个 <style> 不是"一个组件一份",也不完全对得上组件个数。把每个的开头抄下来,能看出它的组织方式:
| # | rules | 首条规则 |
|---|---|---|
| 0 | 1 | .css-var-root { --ant-blue: #1677FF; … } |
| 1 | 21 | a:where(.css-oc1rc0) { color: var(--ant-color-link); … } |
| 2 | 1 | @keyframes css-oc1rc0-loadingCircle |
| 3 | 8 | :where(.css-oc1rc0).ant-wave { … } |
| 4 | 99 | :where(.css-oc1rc0).ant-btn { … } |
| 5 | 1 | .css-var-root.ant-btn { --ant-button-blue-shadow-color: … } |
| 6 | 103 | :where(.css-oc1rc0).ant-input { box-sizing: border-box; } |
| 7 | 1 | .css-var-root.ant-input { … } |
| 8 | 84 | :where(.css-oc1rc0).ant-input-css-var { … } |
| 9 | 1 | .css-var-root.ant-input-css-var { … } |
| 10 | 34 | :where(.css-oc1rc0).ant-spin { … } |
| 11 | 1 | @keyframes css-oc1rc0-antRotate |
| 12 | 1 | @keyframes css-oc1rc0-antSpinMove |
| 13 | 1 | .css-var-root.ant-spin { … } |
| 14 | 198 | :where(.css-oc1rc0).ant-table-css-var { … } |
| 15 | 1 | .css-var-root.ant-table-css-var { … } |
| 16 | 7 | .anticon { … } |
| 17 | 1 | @keyframes loadingCircle |
读三条出来:
- "每个组件一张表"是真的。
.ant-btn那一条里有 99 条规则,.ant-input有 103 条,.ant-table-css-var有 198 条。你多用几个组件,<style>数量就跟着长——这块页面上"注入 18 个",另一个五面板的页面(theme.html)注入 30 个、文本总量 177543 B,三面板的index.html注入 31 个。它的成本不是"一个 antd 的体积",是"你这一页渲染了哪些组件"。 - 有两种
<style>长得不一样。 一种是组件规则(.ant-btn { … }、.ant-input { … }这种,选择器里带:where(.css-oc1rc0));另一种是变量表(.css-var-root开头,里面清一色--ant-*: 值)。这两类东西承担的责任完全不同,第二节专门讲后者。 @keyframes也是逐条注入的(css-oc1rc0-loadingCircle、css-oc1rc0-antRotate、css-oc1rc0-antSpinMove)。所以"注入多少"这件事,连动画都不放过。
再看类名那一侧。同一主题、同一组件,那个 css-oc1rc0 的 hash 在任何上下文里都一样;index.html 上"无 Provider 的 plain 面板"与"StyleProvider layer 的 layered 面板"两个按钮的类名字符串完全相同。这是一件必须提前知道的事:类名相同,就意味着它们的那批规则选择器也一样——两个面板互为对方的候选规则。 所以"两套配置能不能互不影响",从来不是靠"类名不同"保证的。
二、改 token 不是覆盖变量,是多生成一张表
现在看那类 .css-var-* 的 <style>。先说它的规模:默认主题下,.css-var-root 里有 381 个 --ant-* 变量(头几个是 --ant-blue: #1677FF、--ant-purple: #722ED1、--ant-cyan: #13C2C2)。这 381 个就是 antd 一整套设计令牌的默认值。
真正反直觉的是"改一个 token 之后发生什么"。把三块面板的变量表列出来:
| 变量表选择器 | 变量条数 | 什么时候出现 |
|---|---|---|
.css-var-root |
381 | 默认主题 |
.css-var-root.ant-btn |
58 | 用到按钮时 |
.css-var-root.ant-input |
18 | 用到输入框时 |
.css-var-root.ant-input-css-var |
18 | 同上 |
.css-var-root.ant-spin |
4 | 用到 Spin 时 |
.css-var-root.ant-table-css-var |
37 | 用到表格时 |
.css-var-_r_1_(token 面板) |
381 | ConfigProvider theme.token 生效后 |
.css-var-_r_1_.ant-btn |
58 | 同上 |
.css-var-_r_2_(暗色算法面板) |
381 | darkAlgorithm 生效后 |
看 .css-var-root 是 381,.css-var-_r_1_ 也是 381。这就是这一章最该记住的一句话:
改 token 不是"覆盖那 381 个变量",而是"多生成一整张 381 个变量的表",并且给它换一个类名。
对应到类名上就顺理成章了:改了 token 的按钮多一个类 css-var-_r_1_,用了 darkAlgorithm 的多一个类 css-var-_r_2_。两块面板的按钮吃到的是两张不同的变量表,规则却共用同一批。 于是三套主题能在同一页里共存、互不影响——它们不是三份 CSS,是同一批规则 + 三张变量表。
把这条对照回第一节的"每个组件一张表",可以得到一个可以直接用的判断:
- 组件规则表(
.ant-btn { … }):跟你渲染了哪些组件有关,随组件个数增长; - 变量表(
.css-var-*):跟你建了几套主题配置有关,每多一套主题配置就多一整张(默认那张是 381 项打底)。
所以"我页面上 antd 注入的样式特别大"有两种完全不同的成因,查法也不同:是组件多(组件表多),还是主题配置多(变量表多)。 数一下 <style> 里 .css-var- 开头的有几张,就能分开。
补一个容易踩的点:新生成的变量表并不覆盖旧表,旧表也还在页面上。 也就是说,同一个页面上同时存在 .css-var-root、.css-var-_r_1_、.css-var-_r_2_ 三张 381 项的表——这也是为什么"多主题"在那个页面上会明显变重。它不是"借用",是"并存"。
三、颜色真正住在变量里:抢 background-color 是在跟影子打架
第二节说的是变量表本身。这一节说这些变量怎么作用到按钮颜色上。
第三块面板(darkAlgorithm)的主按钮,元素上量出来的值是:
computed background-color = rgb(22, 104, 220)
元素上 --ant-color-primary = #1668dc
元素上 --ant-color-bg-container = #141414
元素上 --ant-color-text = rgba(255, 255, 255, 0.85)
第一块(默认)是 rgb(22, 119, 255)、--ant-color-primary = #1677ff。也就是说:"按钮变暗色了"在数据上是"这些 --ant-* 变量的值换了",不是"多了一条 background-color 规则"。
再往里一层。一份只想知道"按钮背景是哪条规则给的"的记录里,默认主按钮命中背景声明的规则只有两条:
[(未分层)] :where spec=0,1,0 order=161 :where(.css-oc1rc0).ant-btn { var(--ant-btn-bg-color) }
[base] spec=0,1,6 order=1195 button, input, select, … { transparent }
看第一条:它写的不是颜色,是 var(--ant-btn-bg-color)。而 --ant-btn-bg-color 这个变量自己的来源,又是元素自身上的另一条规则:
[(未分层)] spec=0,2,0 order=82 :where(.css-oc1rc0).ant-btn.ant-btn-variant-solid
{ --ant-btn-bg-color: var(--ant-btn-solid-bg-color) }
[(未分层)] spec=0,1,0 order=79 :where(.css-oc1rc0).ant-btn { --ant-btn-bg-color: #ddd }
一层套一层。 你在 DevTools 里"抓按钮的 background-color",抓到的是一个指向变量的声明;那个变量自己又指向另一个变量。所以这条经验值得记死:
antd 的颜色真正存在 CSS 变量里。你去抢 background-color,是在跟一个影子打架——就算你赢了那条 background-color 声明,也只能改这一次,改不动那 381 个变量背后的整套语义(hover、active、disabled、暗色……都是从同一批变量推出来的)。
这也解释了第四节的判断顺序为什么是"先改 token":改 token 改的是源头,抢选择器改的是末端。
四、:where() 把权重压到 0,1,0——那 antd 靠什么赢
上一节那条 :where(.css-oc1rc0).ant-btn,它的权重是 0,1,0。这不是笔误::where() 的语义就是"里面的选择器不贡献权重",所以整条只剩 .ant-btn 那一个类撑着的 0,1,0。
而 Tailwind 的 .bg-red-600 是 0,1,0,:where(.css-oc1rc0).ant-btn 也是 0,1,0——完全一样。
那次专门量过的对照里,一个既带 antd 按钮类、又加了 bg-red-600 的元素,命中的两条规则是:
1. ★ [(未分层)] antd-cssinjs :where(.css-oc1rc0).ant-btn { var(--ant-btn-bg-color) }
2. [utilities] spec=0,1,0 order=641 <tailwind.css> .bg-red-600 { var(--color-red-600) }
computed 仍是 rgb(22, 119, 255) —— Tailwind 输了
两条规则权重一样,order 一个是 79 一个是 641。赢了的是 order 更小的那条——也就是排在文档更前面的那条。而 04 章 会给出更根本的那条规则:未分层的规则优先级高于任何层,所以住在 utilities 层里的 .bg-red-600 天然输给未分层的 antd 规则。
所以这道题的答案不是"antd 选择器更狠",而是:antd 主动把自己的权重归零了,赢是靠"未分层 + 位置在前"这两件事。(第一块面板那条 [base] 的 button, input, select, … { transparent } 权重反而是 0,1,6,但它住在层里、又输给未分层——同样是"层与顺序"在起作用。)
那 antd 为什么要主动把自己的权重压到 0,1,0?因为这是它给使用者的一个承诺:"我不靠权重压你,你可以用层和顺序来覆盖我。" 如果 antd 用 button.ant-btn 这种 0,2,1 的选择器,你想覆盖它就得写一条更狠的——可当所有人都被迫往上加权重时,样式表最终会变成"比谁 !important 更多"的军备竞赛。:where() 把这把钥匙交了出来:想覆盖,去排层,别去加权重。 这也是 04 章 那个"排层修法"能成立的前提。
五、五种改样式的手段,五种落点
把"改一个按钮颜色"这件事能用的手段全试一遍,落在页面上的位置完全不同:
| 手段 | 落在了哪里 | computed background-color |
元素上的 --ant-color-primary |
|---|---|---|---|
| 默认 | — | rgb(22, 119, 255) |
#1677ff |
ConfigProvider theme={{token:{colorPrimary:"#dc2626"}}} |
新变量表 css-var-_r_1_ |
rgb(220, 38, 38) |
#dc2626 |
theme={{algorithm: theme.darkAlgorithm}} |
新变量表 css-var-_r_2_ |
rgb(22, 104, 220) |
#1668dc |
button.ant-btn[data-fight] { background-color: #dc2626 }(未分层,权重 0,2,1) |
未分层,order=1385 | rgb(220, 38, 38) ✅ 赢过 antd 的 0,1,0 |
不变(#1677ff) |
styles={{ root: { backgroundColor: "#dc2626" } }} |
变成元素内联 style | rgb(220, 38, 38) ✅ |
不变 |
classNames={{ root: "bg-red-600" }} |
类名加上 bg-red-600,落在 @layer utilities order=1323 |
rgb(22, 119, 255) ❌ 被 antd 压掉 |
不变 |
六行里最该分别记的是后三行。
第四行(提权重):硬写一条 button.ant-btn[data-fight],权重 0,2,1、又未分层——它赢了。但注意它改的只是这一个属性:--ant-color-primary 没变(还是 #1677ff)。也就是说,颜色是变红了,可 hover、active、disabled 那一整套仍然从旧变量推——你只赢了表面上那一下。它命中的三条候选原文是:
[(未分层)] :where spec=0,1,0 order=161 :where(.css-oc1rc0).ant-btn { var(--ant-btn-bg-color) }
[base] spec=0,1,6 order=1195 button, input, select, … { transparent }
[(未分层)] spec=0,2,1 order=1385 button.ant-btn[data-fight] { rgb(220, 38, 38) }
第五行(styles 插槽):styles={{ root: … }} 的结果是元素上多了一句内联 style background-color: rgb(220, 38, 38);,于是它赢了(内联样式高于任何选择器)。同样,--ant-color-primary 不变——内联是"这一处生效",不是"这套语义改掉了"。
第六行(classNames 插槽):classNames={{ root: "bg-red-600" }} 的结果是把 bg-red-600 加进了类名列表,那条规则落在 @layer utilities、order=1323,然后输了——computed 还是 rgb(22, 119, 255)。为什么?因为 classNames 传的是一批 Tailwind 类名,而 Tailwind 的原子类住在 utilities 层里,层打不过"未分层"。这条特别值得记住:一样是"给按钮传个东西",styles 落成内联(能赢),classNames 落成类名(要受层序管辖,可能输)。 这也直接呼应了 05 组件边界 里"四种传参各自什么时候生效"。
于是有一条可执行的判断顺序:
- 改 token(正道)——
ConfigProvider theme.token或theme.algorithm。你改的是那 381 个变量里的源头,hover / active / disabled / 暗色一起跟着变。要"整体换一套观感",只有这条路是对的。 - 局部覆盖用
styles插槽——只针对某个组件的某个部位做一次性调整,落成内联样式,不受层序影响。代价是它只覆盖你写的那一条声明,改不到语义。 classNames传 Tailwind 类名是最后才考虑的——因为它要受层序管辖,赢不赢取决于你排了层没有(04 章 的四页对照就是量这件事的)。在你没排层之前,别指望它。
顺带把传参的键名记下来。button.d.ts 里给的语义化插槽是:
export type ButtonSemanticType = {
classNames?: { root?: string; icon?: string; content?: string };
styles?: { root?: React.CSSProperties; icon?: React.CSSProperties; content?: React.CSSProperties };
};
也就是 root / icon / content 三个部位,classNames 与 styles 各有一套。另外它还有 color / variant / shape / iconPlacement / rootClassName 这些一等公民 props,以及 [key: `data-${string}`]: string——自定义 data-* 属性可以直接透传(上面那行 data-fight 就是靠这条挂上去的)。知道这一条,你就能给组件挂稳定的钩子、又不必去赌 css-oc1rc0 这种会随环境变的 hash。
本章脉络
flowchart TD
A["antd 组件渲染"] --> B["cssinjs 注入 style"]
B --> C["prepend 到 head 开头<br/>位置在 link 之前"]
C --> D["每个组件一张表<br/>.ant-btn 99 条规则<br/>.ant-input 103 条"]
B --> E["变量表 .css-var-root<br/>381 个 --ant-*"]
A --> F["ConfigProvider"]
F -->|"改 token"| G["多生成一张 381 项的表<br/>css-var-_r_1_"]
F -->|"darkAlgorithm"| H["新表 css-var-_r_2_"]
G -.-> I["三套主题共存<br/>互不影响"]
H -.-> I
E -.-> I
B --> J[":where 把权重压到 0,1,0"]
J --> K["和 .bg-red-600 一样重"]
K -.-> L["antd 赢不靠权重<br/>靠未分层与位置"]
A --> M["改样式的手段"]
M -->|"token"| N["正道,整套语义都变"]
M -->|"styles 插槽"| O["落成内联,赢"]
M -->|"classNames 插槽"| P["落成类名<br/>在 utilities 层,输"]
图里在说什么。 顶上那个方框是这一章的起点——antd 的样式不是构建期生成的,是组件渲染时注入的。往下一路分两支:左边是"注入了什么"(组件规则表 + 变量表),右边是"这些规则凭什么赢"。关键在于中间那三条虚线都汇到同一个结论:三套主题能共存,靠的是"同一批规则 + 多张变量表";而它们对 Tailwind 能赢,靠的是未分层与位置,不是权重。最下面那支是三种改样式手段的落点——同一句"给按钮传个东西",落成内联的能赢,落成类名的要排队。
生产边界
- 本课的替身是"最小页面 + 探针",不是真实应用。 实验页面里只有六七种组件、三五块面板。真实应用会渲染几十种组件、可能在弹窗/抽屉里再叠一层,注入的
<style>数量与顺序都会与你这里量的不同。判据(组件表随组件数增长、变量表随主题配置数增长、:where权重 0,1,0)不变,具体条数请自己数。 - hash 不要写死。 本课里到处出现的
css-oc1rc0是生产态的类名;开发态会带css-dev-only-do-not-override-前缀,两者不同。任何把.css-oc1rc0写进选择器的覆盖,都会在另一个环境里静默失效——这是 05 章 专门展开的一条。要用稳定钩子,就用组件自己的 props(rootClassName)或data-*透传。 - 381 / 99 / 103 / 198 / 177543 B 这些数字只读形状。 它们随 antd 版本(本课是 6.6.5)与渲染的组件集合变化。要带走的是"变量表整张生成"和"每个组件一张表"这两个形状,别把条数搬到自己项目当预期。
styles/classNames/token的优先级顺序,是在"你没排层"的前提下量的。 一旦你在 CSS 里按 04 章 排了层,classNames那一行的结果会变(它会赢)。所以本节结论的正确读法是"给定层序下的落点",不是"绝对谁赢"。- 本课没有测 CSS-in-JS 的缓存与"动态主题切换"。 上面的三张变量表都是首屏一次性生成的;真实应用里"用户在设置里切主题"会触发再生成一批新的
css-var-*,那部分会持续留在页面上。形状一致,长尾要自己量。
动手:可观察结果
挑一个你手上真实的 antd 页面,把下面五件事各做一遍。每一步的答案都要来自浏览器里的量测,不能来自推断。
- 数一数
<style>与变量表。 打开 DevTools,在<head>的子元素里数STYLE的个数,并确认它们是不是都在<link>之前。再数一下其中.css-var-开头的有几张——组件表几张、变量表几张,分开报数。 - 抄两个按钮的
class。 找一个默认主题的按钮、一个套了ConfigProvider的按钮,把两串 class 并排抄下来。除了css-var-*那一个类,还有别的差别吗? - 看颜色到底存在哪。 对着一个按钮量
getComputedStyle(el).getPropertyValue('--ant-color-primary'),再改一次token.colorPrimary重新量。变的到底是background-color那条规则,还是这个变量? - 比一次"权重相同、结果不同"。 给一个按钮加上
className="bg-red-600",在 DevTools 里看它命中了哪两条规则、各自的权重与来源。再加同板块的对比:给它改成内联style,结果一样吗? - 按判断顺序走一遍。 想把某个按钮改成红色,依次用(a)
token.colorPrimary、(b)styles.root、(c)classNames.root三种做法各改一次,各自量computed background-color与元素上的--ant-color-primary,把三组结果记下来。
完成标志:拿到"按钮颜色改不动"的反馈时,你能先问出那个正确的问题——我要改的是"这一处的颜色",还是"这套语义里的主色"? 前者用 styles,后者只能改 token;而"我加了个 Tailwind 类名却没生效"这句话,能立刻接上"它进的是 utilities 层,得先看层序"。
故障注入
三种注入方式,每一种都有明确的观察点。先写预测,再跑。
| 注入 | 怎么做 | 观察什么 | 期望行为 |
|---|---|---|---|
把 token 改掉 |
给一块面板套 ConfigProvider theme={{token:{colorPrimary:"#dc2626"}}} |
注入的 <style> 数量、按钮的 class |
多出一整张 381 项的变量表(新类名 css-var-_r_1_),旧表还在——不是覆盖,是并存 |
用 classNames 传 Tailwind 类名 |
给按钮加 classNames={{ root: "bg-red-600" }} |
computed background-color |
不变(仍是 antd 的颜色)——类名落进 utilities 层,被未分层的 antd 规则压住 |
同样的 bg-red-600 换成立即生效的写法 |
改成 styles={{ root: { backgroundColor: "#dc2626" } }} |
元素上有没有内联 style、computed |
元素上出现 background-color: rgb(220, 38, 38);,computed 变成 rgb(220, 38, 38)——内联能赢 |
| 提权重硬盖 | 在 CSS 里写 button.ant-btn[data-fight] { background-color: #dc2626 }(未分层) |
谁赢、--ant-color-primary 变没变 |
赢了,但**--ant-color-primary 不变**——你只改了表面那一条声明 |
| 改一套主题配置 | 再加一块面板用 theme.darkAlgorithm |
变量表张数、三块面板是否互相影响 | 再多一张表(css-var-_r_2_),三块互不影响——这就是"三套共存"的机制 |
最后一种注入之后别急着收。让三块面板同时留在页面上,然后问自己一个成本问题:如果换成"每个用户可切换的四套主题",页面上会有几张变量表?"共存"的代价是可以数出来的。
自测题
- 同一页里三套主题的按钮,
class只有中间那一个css-var-*不同。请用一句话说明"三套主题互不影响"是靠什么实现的。 plain-only.html的<head>顺序是STYLE ×18 META TITLE LINK[./tailwind.css]。为什么这些<style>出现在<link>之前,而不是之后?.ant-btn那张表里有多少条规则?这个数字跟"你渲染了几个按钮"有关,还是跟"你渲染了几种组件"有关?同理,.css-var-root是 381 项——它跟什么有关?- 改一次
token.colorPrimary,页面多了一张.css-var-_r_1_(也是 381 项)。请说明"覆盖"与"多生成一张"这两件事在观测上的区别,以及各自会带来什么后果。 :where(.css-oc1rc0).ant-btn的权重和.bg-red-600一样都是 0,1,0。那 antd 到底凭什么赢?请再补一句——"提权重去盖它"为什么是个坏主意。- 同为"给按钮传个东西",
styles={{ root: … }}赢了、classNames={{ root: "bg-red-600" }}输了。请说明两者的落点差异,以及各自受不受"层序"影响。 - 一个同事写了一条
.css-oc1rc0.ant-btn { background-color: red }的覆盖样式。请说出它至少两处会出问题的地方(提示:环境差异 + 变量语义)。 - 想在按钮上挂一个自己的选择器钩子,又不想依赖会变的 hash。
button.d.ts给了哪两条路?
现在能解释什么
- 为什么同一个页面里三套主题能共存、类名结构还一模一样——同一批规则 + 多张变量表,不是三份 CSS;
- 为什么"改一个 token"会让页面上多出一整张 381 项的变量表——它在生成,不是在覆盖,所以多主题会明显变重;
- 为什么"按钮颜色改不动"——它不是一个
background-color声明决定的,是变量推出来的,抢末端等于跟影子打架; - 为什么 antd 和
.bg-red-600一样重、却压得住它——:where()主动归零权重,赢靠未分层与位置,所以覆盖要动的是层而不是权重; - 为什么改了
token之后连 hover、disabled、暗色都跟着变,而硬盖一条规则的只有一个瞬间——源头与末端的区别; - 为什么
styles传的生效、classNames传的失效——一个落成内联,一个落成类名,后者要排队; - 为什么不能把
css-oc1rc0写死进选择器——开发态与生产态类名不同,写死必然在另一个环境里静默失效。
下一步:04 章 · 层序:谁赢是有先后规则的——这一章反复提到"未分层与位置",但没给出完整的规则;下一章用四页对照把"谁赢"这件事变成一套可以逆推的判据,也顺手回答本章留下的那个悬念:classNames 传的类名,到底要满足什么条件才能赢。