首页/新闻资讯/正文详情

@formily/reactive model API 深度解析:用声明式语法自动构建响应式领域模型

发布时间:2026/9/23 12:53:26 来源:云帆数科 栏目:资讯中心
@formily/reactive model API 深度解析:用声明式语法自动构建响应式领域模型
formily/reactive model API 深度解析用声明式语法自动构建响应式领域模型【免费下载链接】formily Cross Device High Performance Normal Form/Dynamic(JSON Schema) Form/Form Builder -- Support React/React Native/Vue 2/Vue 3项目地址: https://gitcode.com/gh_mirrors/fo/formilymodel是 formily/reactive 提供的领域模型Domain Model快速定义入口传入一个普通对象字面量即可自动完成 getter 的计算属性化、方法的 action 批量化以及普通属性的 observable 化无需手写任何注解。本文以 model.md 为骨架结合 model.ts、computed.ts、action.ts 等源码与测试用例系统讲解其用法、自动声明规则、底层实现原理及与define的配合方式。读完你将能熟练运用model定义带计算属性和批处理方法的响应式数据模型并理解autorun等响应式副作用为何会按预期精确触发。model 是什么一行代码完成领域模型的响应式声明在响应式编程中定义一个领域模型通常需要手动区分三类属性并分别标注响应式行为普通数据属性、由其他属性推导而来的计算属性getter/setter、以及会修改多个状态的业务方法。model的作用正是快速定义领域模型并自动声明模型的属性它将这一过程收敛为一次函数调用。其自动声明规则对应 model.md 的 Description为getter/setter 属性自动声明为 computed例如get cc() { return this.aa this.bb }读取时惰性求值、依赖变更时自动失效重算函数自动声明为 action例如update(aa, bb)方法体内部的多次赋值会被批量收集副作用只触发一次普通属性自动声明为 observable例如aa: 1读取可被追踪赋值可触发更新。也就是说你只需要按照领域模型的直觉去书写对象数据 计算属性 方法model负责补齐全部响应式语义这正是它与直接调用observable()的关键区别observable()只做深劫持代理见 observable.ts而model会进一步按属性类型应用差异化注解。函数签名与返回值原文档给出的签名如下interface modelTarget extends object { (target: Target): Target }入参target一个普通对象字面量或类实例其中可以包含普通属性、getter/setter 与方法返回值传入的target本身类型保持不变。从源码 model.ts 看model内部构造注解表后调用define(target, annotations)并原样返回target——它原地修改并增强传入对象而非返回一个克隆体。这也意味着调用后原对象引用即具备响应式能力。快速上手完整示例与执行过程推演原文档给出了一个完整的可运行示例见 model.md 的 Example 部分import { model, autorun } from formily/reactive const obs model({ aa: 1, bb: 2, get cc() { return this.aa this.bb }, update(aa, bb) { this.aa aa this.bb bb }, }) autorun(() { console.log(obs.cc) }) obs.aa 3 obs.update(4, 6)其执行过程可以分步推演model({...})aa、bb是普通属性 → 自动声明为observable深劫持cc是 getter → 自动声明为computedupdate是函数 → 自动声明为actionautorun(() console.log(obs.cc))首次执行时读取obs.cccomputed 依赖aa、bb副作用被登记到三者之上控制台输出3obs.aa 3直接赋值触发aa的响应式更新computedcc被标记为脏并重算autorun被重新执行输出6obs.update(4, 6)update作为 action 执行内部连续修改aa与bb。由于 action 的批量语义这两次赋值被合并为一次依赖通知autorun再次执行输出10。该行为与 define.spec.ts 中define model测试用例的预期一致action()执行后obs.aa为2可以作为验证参考。自动声明规则是如何实现的model 源码解析model的全部逻辑集中在 model.tsexport function modelTarget extends object any(target: Target): Target { const annotations Object.keys(target || {}).reduce((buf, key) { const descriptor Object.getOwnPropertyDescriptor(target, key) if (descriptor descriptor.get) { buf[key] observable.computed } else if (isFn(target[key])) { buf[key] action } else { buf[key] observable } return buf }, {}) return define(target, annotations) }三个判定分支与文档描述一一对应getter 判定通过Object.getOwnPropertyDescriptor(target, key)检测descriptor.get是否存在。存在即说明该属性是访问器属性标记为observable.computed。注意这里同时覆盖了仅定义get和同时定义get/set的情况——setter 由 computed 注解内部处理见下文函数判定isFn(target[key])为真则标记为action兜底其余普通属性统一标记为observable。随后model将所有注解交给define落地。definemodel.ts的执行链路是先对已观察对象或不可观察对象做短路返回isObservable/isSupportObservable检查对应测试用例 define.spec.ts 中传入数字、字符串、函数、数组时原样返回的行为然后为对象打上模型标记ObModelSymbol、建立数据树节点buildDataTree最后遍历注解表通过getObservableMaker(annotation)取到注解工厂并逐个应用。深入三种自动注解observable、computed 与 actionmodel自动选择的三类注解各自承担不同的响应式职责值得分别深入observable深劫持的可观察属性普通属性被标记为observable对应注解实现在 annotations/observable.ts它通过createObservable(target, key, value)递归地把属性值包括嵌套对象转换为 Proxy 代理实现深度劫持——读取任意深度的子属性都能被追踪修改任意深度的值都能触发依赖更新。这也是define.spec.ts中observable annotation用例define.spec.ts能对target.aa.bb.cc这种嵌套路径精确响应的原因。computed惰性求值与依赖缓存getter 被标记为observable.computed其实现细节在 annotations/computed.ts 中非常典型惰性求值内部维护store.value缓存与reaction._dirty脏标记get()时仅在_dirty true时才真正执行计算函数reaction()→compute()否则直接返回缓存值依赖追踪计算执行期间会把自身reaction推入ReactionStack从而收集其依赖的 observable 属性自动失效依赖变更时由_scheduler将_dirty置为true并触发依赖该 computed 的副作用runReactionsFromTargetKey实现依赖变 → 计算脏 → 读取时重算的闭环setter 支持若 getter 对象同时定义了setcomputed 注解会通过descriptor.set?.call(context, value)在batchStart/batchEnd包裹下执行computed.ts。define.spec.ts 的computed annotation用例验证了这一点autorun中首次读取target.cc时计算函数只执行 1 次修改aa后再读取才执行第 2 次中间多次读取不会重复计算。action批量更新与依赖收集隔离方法被标记为action实现见 action.tsexport const action: IAction createBoundaryAnnotation( () { batchStart() untrackStart() }, () { untrackEnd() batchEnd() } )它借助createBoundaryAnnotationinternals.ts把原方法替换为绑定到目标对象上的边界函数batchStart/batchEnd方法体内的所有赋值被收集到同一批次结束时统一派发依赖通知。这正是 define.spec.ts 的action annotation用例中setData同时修改aa.bb与aa.cc两个字段、autorun却只多触发一次的原因untrackStart/untrackEnd执行期间暂时关闭依赖收集避免 action 内部读取操作污染外部追踪上下文this 绑定createBoundaryFunction的bound方法将原函数以target为context重新包装因此即使把方法从对象上解构出来如测试中的const { action } obs; action()也能正确读写目标对象的属性。从自动到手动model 与 define 的关系model本质是按类型约定自动生成注解表并调用define的语法糖。当自动推断不满足需求时可以直接使用define手动指定每个属性的响应式行为签名见 define.mdinterface defineTarget extends object { ( target: Target, annotations?: { [key: string]: (...args: any[]) any } ): Target }define目前支持的全部注解同样记录于 define.md 的 Annotations 部分为observable/observable.deep深劫持响应式属性model对普通属性默认选用此项observable.boxget/set 容器通过.get()/.set()读写observable.computed计算属性model对 getter 默认选用observable.ref引用劫持仅劫持属性本身的替换不深入内部observable.shallow浅劫持仅首层响应式action/batch批量处理方法model对方法默认选用action。这些注解均挂在 observable.ts 导出的observable命名空间下使用方式可参考 define.md 的DomainModel类示例。典型取舍是需要全部默认语义、追求简洁时用model需要精细控制例如浅劫持、box/ref 容器、对方法使用batch而非action时用define。适用场景与注意事项首选默认语义凡是数据 计算属性 修改方法结构的领域模型用model一行即可获得完整响应式能力配合autorun、observe、reaction等 API 使用见 packages/reactive/docs/api 目录下的对应文档原地增强model返回的是传入对象本身并改写其属性描述符computed 通过Object.defineProperty重定义 getter/setter不要期望它返回新对象对象类型限制model/define只处理可观察的普通对象对数字、字符串、函数等非目标类型会原样返回由 model.ts 的守卫逻辑保证action 的副作用语义由于 action 默认关闭依赖收集untrack并批量派发在 action 内读取其他 observable 不会建立依赖关系——这正是方法内多次赋值只触发一次更新的实现前提。综上model是 formily/reactive 中声明式定义响应式模型的入口理解它的三条自动规则与背后的 computed/action/observable 注解实现能帮助你在使用 Formily 生态表单模型、字段模型等大量依赖此模式的场景时更准确地预期响应式行为并能在需要时无缝切换到define做更细粒度的控制。【免费下载链接】formily Cross Device High Performance Normal Form/Dynamic(JSON Schema) Form/Form Builder -- Support React/React Native/Vue 2/Vue 3项目地址: https://gitcode.com/gh_mirrors/fo/formily创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

C语言实战:手写英制公制长度换算器,覆盖英寸英尺厘米互转
C语言实战:手写英制公制长度换算器,覆盖英寸英尺厘米互转

1. 为什么要写一个英尺转换器学C语言的人,十有八九都写过“输入两个数,求和的程序”,再往后就是“输入厘米数,输出英尺英寸”。这个题目在很多教材里都有,当初我练习的时候也觉得挺无聊的——不就是除一下、取个余数嘛… · 2026/9/23 12:53:26

什么不同避坑指南
什么不同避坑指南

手写实现对比Python与Java的3个底层差异 配置环境就卡半天?别急着骂系统,多半是你没搞懂语言底层的“什么不同”。很多老手在面试中被问“Python和Java有什么本质区别”时,回答往往停留在“动态类型vs静态类型”这种表面层次。真正… · 2026/9/23 12:53:26

语帆术语宝实战:从零搭建电脑术语库,终结翻译术语不一致
语帆术语宝实战:从零搭建电脑术语库,终结翻译术语不一致

前阵子公司接了一个电脑硬件产品手册的翻译项目,三个译者分章节做,等到合成稿的时候客户QA直接打回来:同一份文档里“内存”“RAM”“随机存取存储器”轮流出现,“固态硬盘”和“固态驱动器”并存,光是术语不一致就列了… · 2026/9/23 12:53:13

CNN图像分类系统实战:从环境搭建到训练避坑的完整路径
CNN图像分类系统实战:从环境搭建到训练避坑的完整路径

简介:基于Python实现卷积神经网络CNN的图像分类系统完整工程包,面向深度学习初学者、毕业设计及课设学生,解决从模型搭建、训练到部署应用的实践问题。压缩包共25个文件,以13个Python源码为核心,另含模型与数据集、编译… · 2026/9/23 13:39:37

Apache Druid Segment 大小优化实战指南:从 5 百万行/段到自动压缩的完整方案
Apache Druid Segment 大小优化实战指南:从 5 百万行/段到自动压缩的完整方案

数据库OLAP大数据后端 【免费下载链接】druid Apache Druid: a high performance real-time analytics database. 项目地址: https://gitcode.com/gh_mirrors/druid6/druid 点击查看 免费下载 Apache Druid 以高性能实时分析著称,而这一切性能的地基是大… · 2026/9/23 13:39:31

Akka Streams 与 Typed Actor 集成:ActorFlow.ask 操作符实战指南
Akka Streams 与 Typed Actor 集成:ActorFlow.ask 操作符实战指南

Akka Streams 与 Typed Actor 集成:ActorFlow.ask 操作符实战指南 【免费下载链接】akka-core A platform to build and run apps that are elastic, agile, and resilient. SDK, libraries, and hosted environments. 项目地址: https://gitcode.com/gh_mirrors/… · 2026/9/23 13:39:30

ATE测试避坑指南 5个高频考点帮你拿分
ATE测试避坑指南 5个高频考点帮你拿分

ATE测试避坑指南 5个高频考点帮你拿分 刚拿到ATE测试的面试题,或者正在准备这场硬仗的朋友,有没有这种感觉:网上搜到的答案看着都对,但一上手写代码或者回答细节,就卡壳?尤其是那些关于覆盖率、故障覆盖和时序约束的问题,稍微问深一点,很多人… · 2026/9/23 13:39:24

武汉门面转让避坑保姆级教程:3个致命报错与修复
武汉门面转让避坑保姆级教程:3个致命报错与修复

武汉门面转让避坑保姆级教程:3个致命报错与修复 盯着屏幕上一片红字的 StackTrace,是不是脑子瞬间炸了?刚接手武汉门面转让的项目,以为只是改改配置,结果一跑起来,异常堆栈像天书一样堆满了控制台,连哪行代码出问题都找不到。别慌,这种“… · 2026/9/23 13:39:17

DeepSeek V4.1 Flash 实测:MoE 架构越级挑战 Pro,多模态 Agent 部署指南
DeepSeek V4.1 Flash 实测:MoE 架构越级挑战 Pro,多模态 Agent 部署指南

1. 这次 Flash 版本到底动了谁的蛋糕DeepSeek 又发新模型了,这次是 V4.1 Flash。消息出来那天我正蹲在几个开发者群里摸鱼,结果不到半小时,群里全在刷同一句话——“Flash 把 Pro 给干掉了”。说实话,第一反应是不太信的。按常规套… · 2026/9/23 13:39:17

3招搞定手机怎么下载微信面试难题实战项目解析
3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧
Win7无线热点配置工具源码解析:解决API失效的3个实战技巧

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧 Win7无线热点配置工具在Win10/11上跑不动?不是你的问题,是版本升级后 API 全变了。很多老项目里的 netsh wlan… · 2026/9/23 0:00:36

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码