这本专栏有 134 篇深读 + 15 篇导览(本文件加 14 篇目录导览)。导览不讲机制,只负责把你从症状带到那一篇。如果你只有一分钟:症状明确就读下面 §一 的第二条路线,然后跳进对应目录导览的 §三;要动架构就读
01-architecture/导览.md。
路线 A:我撞见一个坏行为(最常见)。 顺序是「类导览 §三 坑索引 → 深读篇 §七 → 该篇 §四 定位到行」。类导览的坑索引按症状分组,每条都写着「哪一篇的坑 N」,跳过去就是完整的触发/症状/原因/定位/改动边界五要素。不要从用法手册(apps/docs 那 98 篇)开始查——那批文档回答「怎么用」,不记录「为什么坏」。
路线 B:我要改一处,先确认改动会波及谁。 顺序是「01-architecture/导览.md 的选型表(改动能落在哪一层)→ 对应 A 篇或 I 篇 → 该类的横写篇 Cxx-00」。架构层的六条硬约束(三个生成物禁止手改、pnpm gen:entries 与 check:entries 的分工、ownership pin 只锁已发布归属、Token 谁派生谁、hashId 与 wrapSSR 的落点、SSR 注水是恒等函数)都在 A 层,任何跨组件改动都会撞上其中至少两条。
路线 C:我要建立心智模型。 按 A01 → A02 → A03 → A04 → A05 → A06 → A07 → A08 的顺序读完(这一层是后面所有篇的词汇表),再挑 3-4 篇 I 层(推荐 I02 弹随时序、I06 表格状态机、I08 虚拟化双实现、I10 层级编排),最后读你要动的那个类的 Cxx-00 横写篇。深读篇的 §三 与 §四 是为这条路线写的,只想解决具体问题的人可以整段跳过。
| 范围 | 导览 | 篇数 |
|---|---|---|
| 架构与工程 | 01-architecture/导览.md | 8 |
| 基座模块 | 02-infrastructure/导览.md | 16 |
| 通用基础 | 03-components/01-general/导览.md | 7 |
| 布局与容器 | 03-components/02-layout/导览.md | 14 |
| 导航 | 03-components/03-navigation/导览.md | 14 |
| 基础表单控件 | 03-components/04-form-controls/导览.md | 10 |
| 选择与日期 | 03-components/05-picker/导览.md | 9 |
| 表单编排与编辑 | 03-components/06-form-editor/导览.md | 9 |
| 反馈与浮层 | 03-components/07-feedback/导览.md | 10 |
| 表格与虚拟化 | 03-components/08-table/导览.md | 7 |
| 树与结构可视化 | 03-components/09-tree/导览.md | 3 |
| 媒体与内容展示 | 03-components/10-media/导览.md | 9 |
| 状态与信息呈现 | 03-components/11-status/导览.md | 9 |
| 全局与应用级 | 03-components/12-global/导览.md | 9 |
机器门一共五道,逐层收紧:
node docs/column/tools/verify-refs.mjs --excerpt # 每条 file:line 真实存在、摘录吻合
node docs/column/tools/check-doc.mjs # 七段骨架 + 篇幅/引用密度 + 禁止项 + 占位段落
node docs/column/tools/check-xref.mjs # 跨篇编号引用与「坑 N」落点
node docs/column/tools/board.mjs # 134 篇队列一览
node docs/column/tools/check-guide.mjs # 15 篇导览(选型表 + 坑索引 + 编号真实)这一节是全书唯一「按缺陷族而非按目录」组织的索引。族的判据都来自 tools/DISPATCH.md §3,条目后括号里是该族在本仓的取证实例。族名相同不代表机制相同,跳进去看各自的行号。
C12-06 论点 6(position/group 且用法手册把它们写成可用特性)与 C02-13 论点 2(inheritAttrs: false 且 attrs 全程零落点,连 class 与 style 都丢)。inheritAttrs」,而是「渲染函数里有没有落点」。按严重度排:C02-13 论点 2 → C02-14 论点 1 → C02-08 论点 6 → C02-06 坑 2 → C01-04 论点 6 → C12-06 论点 5 → C02-02 论点 1。useConfigInject 用 ?? 与 || 两条合取,任何带真默认值的 prop 都让右侧永不生效。实例:C01-04 论点 3(disabled)、C04-08(direction: 'ltr')、C11-01 的 size 那条(同一坑的 || 版本)——前三例已于 2026-09-29 修复、注入接通,判据保留;C12-02 论点 1(25 个 props 里只有 2 个带真默认值,恰好让父链回落恒不触发)。hook 版判据另见 _util/hooks/useComponentProps.ts:32(useConfigInject 的 hook 化版本,判据同源)。X !== undefined ? X : Y 与 ??/|| 型兜底恒不可达:X 有非 undefined 默认值时兜底分支永远走不到。活例与反例都写在 C04-10(defaultValue 默认 null)与 C04-07(vc-slider 那条没有默认值、回落可达);C04-04、C05-08、C07-08 各有独立实例。grep -n "const <propName> = computed" <主文件>。实例分布:C11-02(progressStatus,2026-09-29 已修:computed 现优先返回用户 prop)、C08-02(columns 的遮蔽仍在,但 computed 已直读 prop,见该篇论点 3)、C05-04。C01-05 论点 7(409 行 badge 样式修前被 Badge 与 Ribbon 各注册一次,2026-09-29 已拆成 badge 段与 useRibbonStyle 两段)、C04-03(Checkbox 的 Group 传 groupPrefixCls,对照正确写法 radio/Group.tsx:54,同日已改回自己的前缀)、C02-04 论点 6、C12-02 的注入面。C01-01 论点 6(-two-chinese-chars 无 CSS 消费者)、C01-05 论点 4(状态类名无条件写进 sup)、C02-08 论点 4、C02-03 论点 6、C03-08(Dropdown 族的箭头居中规则整条是死 CSS)、C08-02 论点 2 与 坑 3(全库面积最大的一处:155 处 &__ + 40 处根级 &--,2026-09-30 已整体对齐到 DOM 的单横线命名;判形要分清「出现次数 155」与「去重元素名 55」,且注意元素内修饰符 cell--frozen 的 -- 本来就是对的,别一并改掉;验收读注入 CSS 而不是 grep 源文件,见 data-table/__tests__/style-selectors.test.tsx),C08-05 论点 2 与 坑 8(四条选择器错位的死规则,2026-09-29 已修:.xy-list-spin/.xy-list-loading .xy-list-spin-nested-loading 改指 Spin 真实容器类名、窄屏块改指 -item-action、根节点补生成 -empty 让 footer 边框复活,并删掉与 -empty-text 值冲突的 .xy-list-empty。这条的教训是反向的:不是"生成了没人用",而是"用了却没人生成"——判别方法同样是两边集合相减)。emit('x') 与 callEvent(props.onX)——Vue 的 emit 本来就去看 vnode 上的 onXxx,两条指向同一个函数;这个 helper 在本仓有两个名字(callEvent 与 callPropEvent),只 grep 前者会漏掉 input-number。形态二:emit('x') 之后紧跟一句 props.onX?.(...) 直调,形状更隐蔽(mentions/switch/radio/dark-mode-toggle/vc-select 各若干处)。形态三:外层把整包 {...props} 或 {...attrs} 摊给内层、又在同一元素显式绑 onChange={arrow},mergeProps 把两者并成数组,于是每穿一层翻一倍(input-number 三层相乘最狠:一次 focus 把外部 handler 打 6 次、一次输入把 @change 打 8 次;Tabs 四层是 16 次)。修法只有一个方向:每层只留 emit 一个出口,且外层透传时把已桥接的 handler 名从 spread 里剔掉(omit(props, [...]) 或 delete p[k])。实例分布:C03-05 论点 6 与 坑 2(Tabs)、C02-07 坑 9(Card)、A06 §五(vc-dialog)、C07-05、C12-04、C06-03、C05-01/C05-03/C05-04/C05-06/C05-07、C04-02(InputNumber,本族最重)、C04-07(Slider)、C03-13(Tour)。计数回归在 packages/xiaoye-ui/src/_util/__tests__/event-dispatch-count.test.tsx(23 条)与 tabs/event-dedupe.test.tsx。判别方法只有一个:数调用次数,不是看参数是否匹配——仓内原有断言全是 toHaveBeenCalledWith,一条都不数次数,所以这族缺陷在测试面上完全隐形。反向的教训同样记在这里:结构启发式扫描(docs/column/tools/scan-jsx-tag-double.mjs 报出 92 个候选标签位)绝大多数是假阳性,只有逐个真跑一遍交互量次数才能定性,本轮 20 余个候选里最终只有 7 个组件是真的(InputNumber、Slider、Switch 的 attrs 面、TreeSelect、TimePicker、Tour、Steps)。反向的假阴性同样咬过我一次:给 Steps 取证时探针点的是 .xy-steps-item-container 的第一个,而 vc-steps/Steps.tsx:39 有 if (current !== next) 的同值短路,量到 0 次便写下「@change 根本没有投递路径」——链其实是通的(emit('stepClick') 拼出的键名正是容器传下来的 onStepClick prop)。量到 0 次先怀疑这次交互有没有真的改变状态,再谈「无路径」;详见 C03-06 坑 10 的撤回记录。最后两个还补出一条判别手段上的经验:当 jsdom 里根本触发不到那次交互时(点时间格不提交、Tour 翻不到页),计数判据就换成交付物本身的形状——mergeProps 并出的数组必然让 Vue 打 Expected Function, got Array,收集 console.warn 断言为空即可,基线复跑分别得到 4 条与 1 条告警。另有一条纪律级的教训:git diff -U0 | grep -cE "^- *(callEvent|callPropEvent)\(props" 数出来是 100 行,而按「本轮删了几处」口径写进台账的是 50——跨多轮的修复必须用「相对基线」而不是「本轮」来计数,否则数字会随会话边界漂移。C12-05 论点 3(涟漪共享一个 timeout)、C12-02 论点 5(多实例并存时 RTL 副作用「最后挂载者胜」且三条出射边无卸载清理)、C02-14 论点 6(模块级自增计数器生成 id,绕开已有的 SSR 安全 useId)、I13 论点 3(三份 getUUID,防护程度不同,跨请求泄漏计数)。I08(虚拟化是 vc-virtual-list 与 virtual-scroller 两份并存)、C12-00 论点 1(传送门三套互不复用)、C02-07 论点 6 与 C02-08 论点 1(CardLike 是 Card 的平行副本、唯一客户是 Panel)。it( 的数量不等于可执行用例数。三种形态——整块被注释掉的 it(C03-10 的 anchor:名义 8 个实跑 2 个)、注释指向不存在的测试、共享工厂展开造成的反向假阴性(C04-08 的 Rate:字面 0 条、经 tests/shared/ 展开后 4 条)。数之前先确定后缀口径(本仓 .js/.ts/.tsx 三种都有),并防 $emit( 这类子串假阳性(C09-02 就为此换过判据)。getHTML 出口零净化,接入 UGC 必须外部消毒。两组件已于 2026-10-02 移除,教训保留:今后凡引入富内容内核,出口净化是接入方的责任。C10-06 水印是 Canvas 2D 自绘加 toDataURL)、vendored 源码副本(C10-07 二维码是 src/qrcode/qrcodegen.ts 那份 974 行的 Project Nayuki 拷贝,dependencies 里 /qr/i 零命中)、npm 依赖(C10-08 图表,结论见该篇 §六 结案)。判「某个能力是不是第三方」必须三处都查:包 dependencies、目录内 import 来源、是否有 SPDX/版权头。locale.aria 有 7 处以上读取点,而 65 个内置 locale 没有一个提供该块(I11 登记),于是这些文案全部走兜底或空串。查「某个 i18n 键是否真有值」要同时数读取点与供给点。data-* 前先确认走的是哪个 getAttribute:本仓自研的那个做两次折叠('true'/'false' 变布尔、再对数字做 +),后果是 === 'true' 恒假、属性不存在时返回 0 而不是 undefined。正确写法对照 C03-03 的 === true;原画廊组件曾因该语义出现整段不可达的比较分支(组件已于 2026-10-02 移除)。C02-07 把 Tabs 记成「C03-12 队列内」(实际是 C03-05)、C03-00 与 C03-01 关于 vc-overflow 覆盖 Tabs 的说法(实际 Tabs 只经内嵌 Menu 间接影响)、C06-05 与 C06-06 的「第 n/共 m 篇」口径不一致。引用前先 grep -n "^### 论点" <对方文件> 确认落点。pnpm gen:entries)→ 该目录导览的 §四 → 同类 Cxx-00 横写篇。别忘了一条本仓教训:新组件的公开名不要与既有裸名重名(Group/Item 这类),重名不报错但会改变包根导出面。C01-03(Tag 的两条颜色出口是 Token 体系内外的分界线)→ C02-05 论点 7(哪些几何根本不走 token)。C07-00。层级问题走 I10,不要在各组件里手加 zIndex(C12-04 论点 6 是一个反例)。C12-06 论点 6)、被默认值吃掉(C01-04 论点 3)、被同名 computed 遮蔽(C11-02)、还是被后面的无条件赋值覆盖(C02-02 论点 3)。四条路的修法互不相同。C08-02、C08-06。注意 realVirtual 那类「宽度绑定关虚拟化」的隐含条件只在个别入口生效,判据见 C05-01 坑 5 与其 §四 的分叉点清单。C12-07(陷阱本体)→ C12-03 论点 5(role 与 aria-checked 的键盘收益)→ C02-06 坑 9(aria-valuenow 对所有分隔条报同一个数)→ I11(aria 文案的供给缺口)。tools/GUIDE.md,导览不写机制、只做路由;新断言一律来自深读篇,深读篇没有就先补深读篇,不要在导览里立未经取证的结论。