联动系统:Effects / Reactions / x-reactions
1. 两类联动范式
Formily 2.x 同时支持:
- 主动联动:基于事件(onFieldValueChange 等)显式改状态。
- 被动联动:基于依赖(reaction/autorun)自动收敛。
在官方高级指南里,这两套都被完整展示(docs/guide/advanced/linkages.zh-CN.md)。
2. 主动联动(事件驱动)
2.1 机制
- 通过
onForm* / onField*hooks 监听生命周期事件。 - 在回调中调用
setFieldState/setFormState。
2.2 适用场景
- 一对多控制(单字段影响多个字段);
- 业务流程节点清晰、可读性优先。
2.3 风险
- 多对一/循环依赖场景下代码易膨胀;
- 需要手动组织依赖关系。
3. 被动联动(依赖驱动)
3.1 机制
createReactions内部把 reaction 函数接入autorun;- 函数内部读取到的 observable 字段会自动收集依赖;
- 依赖变更后自动重跑。
3.2 适用场景
- 计算型联动(价格、汇总、跨字段公式);
- 多依赖输入到单输出的场景。
3.3 风险
- 依赖隐式化,排查触发链路时需要响应式思维;
- 不当写法可能造成循环更新,需要 guard。
4. x-reactions 的运行时本质
@formily/json-schema 中:
transformFieldProps会把 schema 转为 field props;getUserReactions解析x-reactions;setSchemaFieldState执行fulfill/otherwise/target/run;dependencies通过field.query(...).getIn(...)提取值。
也就是说,x-reactions 不是“特殊语法糖”,而是 schema 到 core reaction 的编译结果。
5. 为什么联动支持字符串表达式
Formily 提供 {{...}} 表达式编译(compiler.ts),让 Schema 可以序列化携带动态逻辑。
这非常适合后端驱动与低代码平台,但也带来安全边界(详见第 6 章与第 9 章 CSP 讨论)。
6. 延伸追问:字段未挂载时怎么联动?
setFieldState 命中不到字段时,会进入更新订阅队列(subscribeUpdate)。
字段初始化 (initFieldUpdate) 后再回放更新,保证“声明先于渲染”也能成立。
这是动态 Schema 场景下非常实用的可靠性设计。
7. 结合 Issue/PR 的演进信号
联动系统相关的近期演进重点:
说明 Formily 团队持续在“联动表达力”与“运行时可控性”之间做平衡。