成员顺序契约(棘轮)
状态:2026-09-17 立的家族级约定。跨仓铁律以引擎仓
AGENTS.md为准;本页是应用层代码层面的补充约定。
家族的应用层(各仓的页面 / 示例页 / demo)按下面这条顺序排类成员:
static 常量/字段 → static 方法 → 实例字段 → 构造函数 → 访问器 / 实例方法
用正则表达就是 S*T*F*C*(A|M)*(每个字母出现 0 次或多次,且按这个相对先后,不允许跳序回潮):
Sstatic 常量 / 字段Tstatic 方法F实例字段C构造函数A访问器(getter / setter)M实例方法
为什么立这条
写新类时,「这段逻辑该挂在哪里」不该每次重新纠结。约定把答案固定下来:
- 静态的都在最前:常量、工厂、纯函数工具一眼能找到,不穿插在实例方法之间;
- 字段集中在构造之前:看一个类先看完它"有什么",再看"怎么造",最后看"能做什么";
- 构造是分水岭:
C之前是状态声明,之后是行为,审计与 code review 都好扫。
这是一条棘轮:新代码照契约写,偏离会被对应仓的约定测试守住(例如 ice-web-components 的 tests/layoutConvention.test.ts、tests/resizeFollowsOwnSize.test.ts 这类"成员顺序"守护测试)。
什么强制、什么不强制
- 强制:应用层的页面 / 示例页 / demo 类。这些类形态千差万别,最需要从顺序上获得一致性。
- 不强制:各组件库仓库的
src/(引擎仓、ice-web-components等)。2026-09-17 体检里这类仓库有少量组件类偏离——典型是private static __countText()/__iconOf()/__defaultPalette()这类静态小助手紧挨着它唯一的调用者。这恰好是 Google Java Style §3.4.2 说的"每种顺序都讲得通、维护者能解释"那类顺序(该指南同时明确说成员顺序"没有唯一正确的配方",Google 的 TypeScript 指南对顺序完全沉默)。所 以组件库存量不搬迁。
为什么存量不迁移
代价不对称,而且 TypeScript 里字段的声明顺序是有语义的:
- 初始化按声明顺序执行——字段初始化器依赖前面字段的值时,挪顺序会改变求值;
- 影响 V8 的 class shape——为排版挪字段可能改变隐藏类,反而伤性能。
为"看起来整齐"去挪字段,既不划算也有真实风险。所以约定是:新代码照写,存量保持原状,由棘轮测试保证不再新增偏离即可。
速查
| 要加的东西 | 放哪 |
|---|---|
| 常量 / 枚举 / 配置表 | S 静态区最前 |
| 纯函数工具 / 工厂 | T 静态方法区 |
| 实例状态字段 | F 构造之前的实例字段区 |
| 建对象、接依赖 | C 构造函数 |
getX / setX | A 访问器区(在实例方法之前) |
| 业务方法 | M 实例方法区(最后) |
跨仓铁律(渲染 / 序列化 / 事件 / i18n 边界 / 动画等)见引擎仓
AGENTS.md;本页只管"类内部成员怎么排"这一件小事。