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

Formily Reactive 的 observable 响应式对象创建 API 全解析

发布时间:2026/9/24 0:05:44 来源:云帆数科 栏目:资讯中心
Formily Reactive 的 observable 响应式对象创建 API 全解析
前端UI组件【免费下载链接】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点击查看免费下载导读observable是 Formily 响应式核心库 formily/reactive 中最基础、使用频率最高的 API它用于创建具有不同响应式行为的对象同时也可以作为注解annotation标记类的响应式属性。本文将以 packages/reactive/docs/api/observable.md 为骨架逐一讲解observable/observable.deep、observable.shallow、observable.computed、observable.ref、observable.box五种形态的签名、适用场景与示例并结合 packages/reactive/src/observable.ts 与 packages/reactive/src/annotations 下的源码实现剖析它们各自的劫持策略与底层原理帮助你根据业务需求选择最合适的响应式容器。observable 家族概览五种形态与选择依据observable实际上是一个多态工厂它本身是函数同时又挂载了box、ref、deep、shallow、computed五个子方法。从 源码 可以看到它们之间的挂载关系export function observableT extends object(target: T): T { return createObservable(null, null, target) } observable.box annotations.box observable.ref annotations.ref observable.deep annotations.observable observable.shallow annotations.shallow observable.computed annotations.computed observable[MakeObModelSymbol] annotations.observable其中形态响应范围读写方式典型用途observable(obj)/observable.deep(obj)深劫持递归所有层级直接属性访问常规的嵌套对象 / 数组状态observable.shallow(obj)浅劫持仅第一层属性直接属性访问大数据对象只关心顶层变化observable.computed(fn)依赖追踪 缓存通过.value读取由其他响应式状态派生的计算值observable.ref(value)引用容器值整体替换通过.value读写包装基础类型number / string / booleanobservable.box(value)引用容器值整体替换通过.get()/.set()需要方法式读写的基础类型包装注意函数形式observable(obj)与observable.deep(obj)是等价的二者都指向深层劫持的注解实现见 annotations/observable.ts。deep只是语义更明确的一个别名。observable / observable.deep深层劫持响应式对象功能描述observable用于创建深层劫持的响应式对象。所谓深劫持指的是不仅目标对象自身的第一层属性是响应式的其嵌套的子对象、子数组在首次访问时也会被递归包装为响应式对象任何层级的读写操作都会被追踪和触发。签名interface observableT extends object { (target: T): T } interface deepT extends object { (target: T): T }入参为任意对象普通对象或数组返回值仍为原类型T实际返回的是原对象的响应式代理。使用示例import { observable, autorun } from formily/reactive const obs observable({ aa: { bb: 123, }, }) autorun(() { console.log(obs.aa.bb) }) obs.aa.bb 321运行上述代码时autorun中的副作用函数会立即执行一次并打印123当obs.aa.bb被修改为321时由于bb属于深层属性副作用会被重新触发并打印321。源码原理createObservable 的递归代理深层劫持的入口是 internals.ts 中的createObservable函数其核心逻辑可以归纳为三步原始值短路如果目标value不是对象typeof value ! object直接原样返回不对基础类型做包装重复包装保护通过ProxyRaw弱映射检查目标是否已被代理过若已代理则复用已有代理并维护数据树节点避免重复包装导致响应式失效构建数据树并创建代理调用buildDataTree将目标挂入全局数据树随后根据类型分别走createNormalProxy普通对象/数组走baseHandlers或createCollectionProxyMap/Set 等集合走collectionHandlers。深劫持的深体现在baseHandlers的get陷阱中当访问嵌套属性如obs.aa时如果该属性值是对象且尚未被代理处理器会递归调用createObservable将其包装后再返回。因此嵌套对象是按需惰性深度代理的——只有真正访问到的层级才会被包装这与 Vue 3 的 Proxy 实现思路一致。在tests/observable.spec.ts 中还有一组contains用例验证了深劫持下嵌套元素如arr[0].aa、element的子对象都能被数据树正确包含与追踪。observable.shallow浅层劫持响应式对象功能描述observable.shallow创建浅层劫持的响应式对象只响应目标对象第一层属性的读写操作。对于嵌套的子对象浅代理只保证替换整个子对象这一操作可被侦测子对象内部的属性变化不会被追踪。签名interface shallowT extends object { (target: T): T }使用示例import { observable, autorun } from formily/reactive const obs observable.shallow({ aa: { bb: 111, }, }) autorun(() { console.log(obs.aa.bb) }) obs.aa.bb 222 // 不会触发响应 obs.aa { bb: 333 } // 会触发响应示例中有意对比了两种写法的差异obs.aa.bb 222修改的是第二层属性浅劫持不会拦截副作用不会重新执行obs.aa { bb: 333 }替换第一层属性aa的整个引用浅劫持能够侦测到副作用重新执行。源码原理浅代理与父浅则子不代理规则浅劫持的实现位于 annotations/shallow.ts与深层实现几乎同构唯一区别是在调用createObservable(target, key, value, true)时传入第四个参数shallow true。createObservable内部由此分支见 internals.tsif (target) { const parentRaw ProxyRaw.get(target) || target const isShallowParent RawShallowProxy.get(parentRaw) if (isShallowParent) return value // 父级为浅代理时子对象不再代理原样返回 }这里有两个关键机制浅代理会通过RawShallowProxy弱映射登记见 internals.ts 中createNormalProxy/createCollectionProxy的shallow分支当某个浅代理对象的子属性被读取时baseHandlers在尝试递归代理前会先检查父级是否为浅代理——如果是则原样返回子对象不做包装。这正是obs.aa.bb 222无法触发响应的根本原因。适用建议当你的状态对象层级很深、但业务上只关心顶层结构变化时例如配置对象整体替换、列表整体更新使用shallow可以显著减少代理包装带来的开销。observable.computed带缓存的派生计算值功能描述observable.computed创建计算缓冲区calculation buffer它接受一个计算函数或包含get/set的对象返回一个带.value的响应式容器。计算值会自动追踪其内部读取的所有响应式依赖并具备惰性缓存能力依赖未变化时重复读取.value不会重复执行计算函数。签名interface computed { T extends () any(target: T): { value: ReturnTypeT } T extends { get?: () any; set?: (value: any) void }(target: T): { value: ReturnTypeT[get] } }签名支持两种重载传入纯函数{ value: ReturnTypeT }传入{ get?, set? }对象可同时定义读取逻辑与写入逻辑写入时set内部可以继续修改其他响应式状态实现可写计算属性。使用示例import { observable, autorun } from formily/reactive const obs observable({ aa: 11, bb: 22, }) const computed observable.computed(() obs.aa obs.bb) autorun(() { console.log(computed.value) }) obs.aa 33第一次读取computed.value时计算结果为3311 22并缓存当obs.aa变为33后computed 的依赖被标记为脏autorun再次读取时重新计算得到55控制台依次打印33与55。源码原理ComputedReaction 与脏标记computed 的实现是五个注解中最复杂的见 annotations/computed.ts核心可以拆解为几个部分计算反应compute()负责调用descriptor.get?.call(context)执行用户计算函数结果暂存于store.value依赖收集reaction()将自身压入ReactionStack后执行compute()这样计算函数内部读取的响应式状态就会把该反应登记为依赖reaction._name固定为ComputedReaction便于调试定位脏标记调度reaction._scheduler在依赖变化时仅置reaction._dirty true并广播计算值已过期不立即重算惰性求值get()中只有当reaction._dirty为true时才真正执行reaction()重算否则直接返回缓存的store.value读取时还会通过bindTargetKeyWithCurrentReaction把自己注册到当前正在运行的追踪者如autorun的依赖列表中批处理写入set()将descriptor.set的调用包裹在batchStart()/batchEnd()之间保证一次写入多个依赖时只触发一轮更新详见 packages/reactive/src/batch.ts。此外computed 注解在属性定义上做了性能优化getPropertyDescriptorCache会将类原型上的属性描述符按构造器缓存ClassDescriptorSymbol避免每次读取都递归遍历原型链。注意observable.computed必须与追踪上下文autorun、tracker、observe等配合使用才有意义。脱离追踪直接读取.value时由于没有消费者脏标记会在下次读取时才被重新计算即仍然正确只是不会主动求值。observable.ref引用劫持的响应式容器功能描述observable.ref创建引用劫持的响应式容器将任意值尤其是基础类型包装进一个带.value属性的容器中通过对ref.value的读写来驱动响应式更新。它解决了基础类型无法被直接代理的问题——基础类型不是对象无法挂载 getter/setter因此必须先包一层容器。签名interface refT extends object { (target: T): { value: T } }源码中的实际类型为IRefT(target: T): { value: T }见 annotations/ref.ts入参不限于objectobservable.ref(1)即传入了数字。使用示例import { observable, autorun } from formily/reactive const ref observable.ref(1) autorun(() { console.log(ref.value) }) ref.value 2控制台依次打印1与2。当ref.value被整体替换时绑定在容器上的副作用重新执行。源码原理数据树节点与响应式 value 属性ref 的实现位于 annotations/ref.ts。它创建了一个store内部存储对象并在返回的proxy上通过Object.defineProperty定义value属性的 getter/setterget()调用bindTargetKeyWithCurrentReaction把当前追踪者绑定到store.value上set(value)写入新值并在oldValue ! value时调用runReactionsFromTargetKey触发绑定在该键上的所有反应。同时它调用buildDataTree(target, key, store)将容器接入全局数据树并把proxy[ObModelSymbol] store挂上模型符号使容器与内部存储的映射关系可被框架识别。当 ref 作为类的注解使用时target/key存在会在原对象属性上直接定义 getter/setter 并返回原对象见 annotations/ref.ts。ref 与直接使用对象的区别对observable.ref(1)而言追踪的粒度是整个值而不是值的内部属性——这也正是引用劫持的含义。observable.boxget/set 方法式读写的响应式容器功能描述observable.box与observable.ref功能类似都是对基础类型等值做响应式包装区别在于box 通过get()/set()方法读写数据而不是访问.value属性。签名interface boxT extends object { (target: T): { get: () T; set: (value: T) void } }使用示例import { observable, autorun } from formily/reactive const box observable.box(1) autorun(() { console.log(box.get()) }) box.set(2)控制台依次打印1与2。源码原理显式代理映射box 的实现位于 annotations/box.ts与 ref 的关键差异在于代理的登记方式const proxy { set, get } ProxyRaw.set(proxy, store) RawProxy.set(store, proxy)ref 返回的是一个通过Object.defineProperty定义了value属性的普通proxy对象而 box 直接返回一个包含get/set方法的字面量proxy并通过ProxyRaw/RawProxy两个弱映射显式建立代理 ↔ 内部存储的双向关联。get()/set()内部同样执行依赖绑定bindTargetKeyWithCurrentReaction与变更广播runReactionsFromTargetKey并且set在值未变化时不会触发更新if (oldValue ! value)判断。当 box 作为注解使用时target存在它会用Object.defineProperty将代理对象作为不可写、不可配置的属性挂到目标上见 annotations/box.ts。作为注解使用给类属性标记响应式observable的各个形态除了直接调用还都实现了注解能力——即挂载MakeObModelSymbol标记见 internals.ts 的createAnnotation与getObservableMaker。在 TypeScript 装饰器场景下可以用它们标记类属性为响应式例如class Store { observable.ref count 0 observable.computed get double() { return this.count * 2 } }从源码看注解调用时createAnnotation会以{ target, key, value }访问器对象调用各实现target为类实例、key为属性名实现内部据此分支有target时通过Object.defineProperty定义属性并返回原对象无target时直接函数调用则创建独立容器返回详见 annotations/observable.ts 等。这就是一个 API 两种用法函数式创建 / 装饰器注解的统一入口设计。场景选型速查需求推荐 API理由管理嵌套的表单/业务对象状态observable/observable.deep深劫持任意层级读写都能触发更新对象层级深但只关心顶层替换observable.shallow减少代理开销避免不必要的深层包装由已有状态派生的值总和、过滤结果等observable.computed自动依赖追踪 惰性缓存性能最优包装数字、字符串等基础类型observable.ref属性式读写语法简洁偏好方法式读写、或需显式区分 APIobservable.boxget/set 语义明确便于封装小结observable是 formily/reactive 响应式体系的基石deep/shallow控制劫持深度computed提供依赖缓存与派生计算ref/box解决基础类型包装问题。理解它们各自的劫持粒度与底层代理/注解机制是正确使用 Formily 双向绑定、联动、校验等高级特性的前提。若想深入实践可以继续阅读 observable 的官方 API 文档并通过 observable.spec.ts 中的测试用例验证各种边界行为。赞分享前端UI组件【免费下载链接】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点击查看免费下载相关推荐formily/reactive observe API 全解析深度/浅度监听 Observable 对象的所有写操作formily/reactive observe API 全解析深度/浅度监听 Observable 对象的所有写操作 observe 是 formily前端UI组件Formily Reactive 的 raw API 详解如何从 observable 对象中取回源数据Formily Reactive 的 raw API 详解如何从 observable 对象中取回源数据 导读 raw 是 formily/reactive前端UI组件Formily Reactive 的 markRaw 详解让对象彻底脱离响应式劫持Formily Reactive 的 markRaw 详解让对象彻底脱离响应式劫持 导读 markRaw 是 formily/reactive https:前端UI组件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

PX4 飞行模式开发指南:内部模式、外部(ROS 2)模式与模式约束机制详解
PX4 飞行模式开发指南:内部模式、外部(ROS 2)模式与模式约束机制详解

嵌入式物联网机器人自动驾驶智能硬件 【免费下载链接】PX4-Autopilot PX4 Autopilot Software 项目地址: https://gitcode.com/gh_mirrors/px/PX4-Autopilot 点击查看 免费下载 飞行模式(Flight Mode)是 PX4 自动驾驶仪的核心概念——它定义… · 2026/9/24 0:05:25

Python深度学习回归实战:从Keras基线到物理约束网络
Python深度学习回归实战:从Keras基线到物理约束网络

简介:这份资源面向具备一定Python基础、希望系统实践深度学习回归与序列建模的学习者,围绕神经网络在连续变量预测中的应用展开,涵盖全连接网络、循环神经网络及LSTM等模型在时间序列预测、股票与汇率走势预测、气候变化预测等场景下的实现思… · 2026/9/24 0:04:48

虚假新闻检测多模态融合实战:文本+结构化+统计特征联合建模
虚假新闻检测多模态融合实战:文本+结构化+统计特征联合建模

简介:本资源是一套基于Python实现的虚假新闻多模态检测高分课程设计项目,面向计算机专业本科生及AI初学者,解决社交媒体中图文混合内容的真实性判别问题,适用于期末大作业、课程设计与入门级科研实践。压缩包共39个文件&#xff0… · 2026/9/24 0:04:16

大数据入门学习顺序:Hadoop、Hive、Spark、Flink等九大组件最小链路搭建指南
大数据入门学习顺序:Hadoop、Hive、Spark、Flink等九大组件最小链路搭建指南

简介:这是一份面向大数据初学者与转行开发者的系统入门资料包,围绕Hadoop、Hive、Spark、Storm、Flink、HBase、Kafka、Zookeeper、Flume等主流组件展开,覆盖学习路线、技术栈思维导图、常用软件安装指南,以及环境搭建、命令实操、… · 2026/9/24 0:40:04

CodeBurn 中 OpenCode 用量追踪:数据目录解析、双代存储格式与计费路由实战指南
CodeBurn 中 OpenCode 用量追踪:数据目录解析、双代存储格式与计费路由实战指南

【免费下载链接】codeburn Free, local tool to track AI coding token usage and cost across 37 tools and agents (Claude Code, Cursor, Codex, Gemini and more), by model, project, and task. npx codeburn 项目地址: https://gitcode.com/gh_mirrors/co/cod… · 2026/9/24 0:40:04

Java服务端发丝级抠图:ONNX Runtime部署matting模型实战
Java服务端发丝级抠图:ONNX Runtime部署matting模型实战

简介:该资源是一套基于ONNX模型的发丝级人像抠图与背景替换Java实现源码,面向希望将深度学习模型集成到Java应用中的开发者,以及研究图像分割与高精度抠图的技术人员。项目以Java为核心语言,借助ONNX实现跨框架模型加载与推理&… · 2026/9/24 0:39:58

基于PCD小样本数据集的PCB元器件缺陷检测:YOLOv8训练与产线落地实践
基于PCD小样本数据集的PCB元器件缺陷检测:YOLOv8训练与产线落地实践

简介:PCD表面元器件缺陷检测数据集面向从事工业质检、电子制造与目标检测算法实践的开发者与研究者,用于训练和验证PCB表面元器件缺陷识别模型。数据集包含超过600张标注图像,已统一处理为YOLO格式并完成数据增强,可直接用于YOLO全… · 2026/9/24 0:39:52

GFPGAN人脸修复实战:从环境配置到视频美颜调参
GFPGAN人脸修复实战:从环境配置到视频美颜调参

简介:这是一套基于Python实现的GFPGAN人脸美颜与清晰度增强工具源码,面向图像/视频处理开发者、AI视觉初学者及内容创作者,解决人脸图像与短视频的自动化美化与画质提升需求。资源共60个文件,含29个核心Python脚本(如i… · 2026/9/24 0:39:52

免费小游戏平台实测:Poki、itch.io、7k7k哪个更好玩?
免费小游戏平台实测:Poki、itch.io、7k7k哪个更好玩?

很多人一到休息时间就不知道该玩点什么,正经大作玩不动,手机App又总觉得越做越重,光是安装包和注册流程就能劝退一半人。其实我一直觉得,真正适合大多数人消遣的,往往是那些打开就能玩、关掉也不心疼的免费小游戏平台。… · 2026/9/24 0:38:26

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13

1D-CNN时间序列建模实战:从Conv1d原理到工业落地
1D-CNN时间序列建模实战:从Conv1d原理到工业落地

简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26

柔软的L:汉语语流中被忽视的舌肌张力控制
柔软的L:汉语语流中被忽视的舌肌张力控制

1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44

了解更多?预约专属演示

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

企业微信二维码