管的是「通用基础」这一类 7 篇之间的路由:Button / Icon / Tag / Chips / Badge / Empty 这六个东西加一篇类横写,谁负责什么、近义的怎么选、坑落在哪一篇的第几条。 不管:单个组件的内部机制(各深读篇的 §三、§四)、样式引擎与 Token 派生(A03、A04)、注入链的默认值合取(A05)。本类里凡是牵到那三条的,这里只给编号不复述。
这一类看着最「浅」——按钮、图标、标签、徽标,都是单文件就能讲完的小组件。但 C01-00 的六个论点合起来给了一个不舒服的结论:本类没有共享基座,只有五个各自成活的实现。C01-00 论点 1 说「可点击容器」有两套互不调用的实现(XYButton 与 _util/transButton.tsx),Button 是终点而不是基座;C01-00 论点 2 说点击反馈(Wave)的挂载判据是三套互不相干的布尔式;C01-00 论点 3 说「给组件配一个图标」在本类对应四种互不兼容的 API 形态;C01-00 论点 5 说「调用方给点东西进来」也是三种不同构写法。也就是说,改这一个组件的教训不能直接搬到隔壁那个组件——这是本类最大的读法注意。
第二件事:「声明了但没读」在本类是有实体的缺陷族,不是措辞问题。C01-02 论点 1 给出本类的极端样本——src/icon/ 只有 11 行、渲染 null,它作为「组件」的身份全靠 withInstall 与 displayName 撑着。判「某个 prop 有没有用」时,本类的正确姿势是看 setup 里有没有读它,而不是看类型面与文档面。
第三件事:默认值会吃掉注入。C01-04 论点 3 取证 Chips 的 disabled 注入链在组件自己的默认值处断掉(booleanType() 给的 undefined 被 initDefaultProps 覆写),于是常规的合并表达式成了不可达死代码,全局禁用对 Chips 零效果。同一族在 C04-08(direction: 'ltr')与 C11-01(size 的 || 版)各自成立,机制归 A05,本类只贡献实例。
第四件事:类名与样式之间有两条静默断点。一条是「生成了没人用」:C01-01 论点 6 的 -two-chinese-chars 在本仓没有任何 CSS 消费者,C01-05 论点 4 的 -status-<值> 类名会被无条件写进而样式端只在状态根类下才有对端规则。另一条是「一份样式注册成两张表」:C01-05 论点 7 数出 378 行的 badge 样式被 Badge.tsx:52 与 Ribbon.tsx:30 各注册一次,因为注册缓存键含 prefixCls(2026-09-29 已修:badge/style/index.ts 拆成 badge 段与 ribbon 段,Ribbon 改用 useRibbonStyle;同族的 checkbox/Group.tsx 传 groupPrefixCls 也在同日改回自己的前缀)。
最后,C01-02 论点 4 划出本类与 I 层(图标 SFC 代际、图标前缀注入)的接缝:注入链四段都能跑通,断点全部集中在最后一米——组件层直接 import 常量贴类名,唯一把注入值往下传的调用在接收端被丢弃。所以「改了 iconPrefixCls 却没生效」这类问题,在本类读 C01-02 坑 2,在 I 层读 I15 的论点 3/4。
| 要解决的问题 | 选它 | 而不是另一个 | 判据 |
|---|---|---|---|
| 点一下触发一次操作,要外观与涟漪 | XYButton(C01-01) | _util/transButton.tsx | 两套渲染元素、禁用方式、键盘合同完全不同且彼此零引用,Button 不基于 TransButton(C01-00 论点 1) |
| 只要一个可点的外观容器(菜单项、卡片壳) | transButton | XYButton | Button 侧的涟漪挂载条件是「非无边框类型」(button.tsx:233-239),无边框档位刻意不包 Wave(C01-01 论点 5) |
| 一组可多选的值、能输入新增 | Chips(C01-04) | CheckableTag 拼自管数组 | Chips 内部不持有值镜像、渲染直读 props.value,三条写路径共用一条出口(C01-04 论点 1) |
| 一组固定的可切换标签 | CheckableTag(C01-03) | Chips | 它是与 Tag 并列的第二套渲染出口、共用 xy-tag 前缀但事件与状态各自实现,一次发三个事件且不做本地状态(C01-03 论点 4) |
| 标记一条数据的性质(成功/警告) | Tag 的 preset 色 | 自定义 color 字符串 | preset 命中走 CSS-in-JS 类名、没命中走内联 style 且只覆盖 backgroundColor 一个属性(C01-03 论点 1) |
| 给已有元素挂一个数字角标 | Badge(C01-05) | 自写定位样式 | Badge 的可见性由四层派生加三个快照 ref 组成,有完整的进隐/出隐语义(C01-05 论点 1);自写定位补不出离场动画期间的数字稳定与 overflowCount 截断(C01-05 §六、论点 2) |
| 需要一个「随值滚动」的数字徽标 | Badge 的 ScrollNumber | 自写 transition | 数位不是换文本而是每列铺 11 个 <p> 再 translateY 位移,收尾靠 transitionend 加一个 1 秒定时器(C01-05 论点 3) |
| 页面里没有数据时的占位图形 | Empty(C01-07) | 自写 img | 空态文案与图形供给归 ConfigProvider 的 renderEmpty,本类只有它接了这条通道(C01-00 论点 6) |
<xy-icon>,页面上什么都没有,生产环境连报错都没有 → C01-02 坑 1。告警被 NODE_ENV 与消息去重表两层门控消掉(C01-02 论点 2),空壳与不存在的标签在可观测性上等价。iconPrefixCls 把主库自己贴的图标类名一起废掉 → C01-02 坑 2;根因是 C01-02 论点 4 的「最后一米断链」。suffixIcon/removeIcon 传 null 关不掉,但 clearIcon 能关 → C01-02 坑 6。同一个「传 null 表示不要」的意图在本类没有统一协议。icon 传字符串在两个组件里结果完全不同 → C01-02 坑 5:把 MegaMenu 的 icon: 'my-iconfont-class' 搬到 Tree 的 switcherIcon 或 Select 的 suffixIcon 会坏;四套互不兼容的分派实现见 C01-02 论点 5,另有三个同名不同物的 IconType。spin 传进两个 loading 图标,一个变成 DOM 属性、一个被静默吞掉 → C01-02 坑 4(C01-02 论点 6):主库还有 4 处在给两代形状相反的图标 SFC 传 spin。v-model 时 Chips 会「先清空输入框、后由调用方决定加不加」 → C01-04 论点 1:清空动作发生在调用方确认之前。max 限了个寂寞 → C01-04 论点 2:Enter 路径查它,分隔符路径、粘贴路径与 addItem 本体都不查,且等值判定让闸门在越界后自动失效。separator 传两个竖线连写(正则里的「或」)时每次击键都命中加分支 → C01-04 论点 5:分隔符被「只转义首字符」的拼接法升格成正则,两处构建点都不缓存、不校验语义。用户提供的含分隔符字符串会走进同一条路。class/style/id/data-* 全部丢失 → C01-04 论点 6:inheritAttrs: false 声明了却没有任何一处展开 attrs。C01-04 论点 3(Chips 的 disabled);同族另有 C04-08(direction: 'ltr' 默认值)与 C11-01 坑 2(size 的 || 版),机制归 A05。count 又传了 status 时状态形态静默降级为计数形态,DOM 上还留着一组没有对端选择器的类名 → C01-05 论点 4。死类名同族另见 C01-01 论点 6(-two-chinese-chars 无 CSS 消费者)。C01-05 论点 6:getTransitionProps 对空名不是换名字而是把 CSS 过渡整个关掉(css: false)。C01-05 论点 7(badge/style/index.ts:401 被 Badge.tsx:52 与 Ribbon.tsx:30 各调一次);groupPrefixCls 式的同族见 C04-03。color="default" 之类的取值失配,既不生成类名也没有内联样式 → C01-03 论点 2:判定名单与规则名单是两份手工清单,源码里没有任何一致性校验。theme.components.Tag 注入了同名键却没有效果 → C01-03 论点 6:样式函数里那次二次 mergeToken 会把外部注入覆盖掉,且 Tag 的 Component Token 接口是空对象。src/icon/ 补真实现后按需样式少一个入口,且没有任何一处报错 → C01-02 坑 8;C01-02 论点 3 数出 icon 是 102 个聚合条目里仅有的 6 个无样式副作用导入条目之一,生成物口径见 A02。C11-02 的 progressStatus。C01-01 论点 1(五维在 TSX 侧刻意拆成单维后缀、交叉全部下沉到 CSS 复合选择器)→ C01-01 论点 5(涟漪挂载判据会跟着漏)→ A04(类名与 hashId 的落点)。size 的取值优先级:C01-01 论点 2(四级来源链,两个 Context 在无提供者时返回空对象而非 undefined,源码里的可选链从不生效)+ A05 + C11-01 坑 2。loading 的延迟/防抖:C01-01 论点 4——只有「变亮」被推迟、「变灭」永远同步,且外部类型与内部类型是两套(buttonTypes.ts:29-32 对内部归一逻辑)。C01-00 论点 3(四种形态)与 C01-02 论点 5(四套分派 + 三个同名 IconType),再决定跟随哪一族;不要新造第五种。count 语义:C01-05 论点 1(快照只在可见时刷新)、论点 2(截断与是否滚动两套判定用不同强制转换)、论点 5(自定义 VNode 时 ScrollNumber 退化成克隆器,自己装配的四样全部丢弃)。position: relative 上(badge/style/index.ts:95),-count/-dot 的绝对定位(:150-155)与它要一起动;Ribbon 的缎带定位在 -ribbon 段(:286-332),锚点是 position: relative 的 -ribbon-wrapper(:285);层级编排归 I10。C01-00 论点 6(装配层四件事没有统一写法)→ A02(三个生成物与 pnpm gen:entries)→ C01-02 坑 7(Icon、IconMap、defaultIconPrefixCls 三个名字被快照冻住)。C01-01 论点 6、C01-05 论点 4)还是双张表(C01-05 论点 7),再进 A04;两者表现相同、成因相反。