新闻详情

新闻详情

首页 / 资讯中心 / 详情

Formily Reactive toJS 详解:从 observable 到普通 JS 对象的深度递归转换

发布时间:2026/11/4 19:56:09来源:尧图网络
Formily Reactive toJS 详解:从 observable 到普通 JS 对象的深度递归转换
前端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点击查看免费下载导读toJS是 Formily 响应式核心 formily/reactive 提供的重要工具函数用于将 observable 响应式对象深度递归地转换回普通 JS 对象。在实际开发中当我们完成表单数据收集、需要将响应式状态提交给后端、序列化到本地存储或传递给非响应式库如 axios、echarts、富文本编辑器时toJS就是打通响应式世界与普通数据世界的标准出口。读完本文你将掌握toJS的完整签名、底层递归实现原理、与markRaw的联动行为以及循环引用场景下的安全用法。一、toJS 是什么toJS是 formily/reactive 对外导出的核心 API 之一其官方描述为深度递归将 observable 对象转换成普通 JS 对象。也就是说它会把通过observable()创建的响应式代理Proxy还原为无响应式能力的原生数据。转换后的结果与原始 observable不再共享依赖追踪关系后续对普通对象的修改不会触发任何 autorun 响应。一个需要特别注意的行为是如果对一个已经是 observable 的对象标记markRaw那么toJS不会将它转换成普通对象。这一行为在 toJS.zh-CN.md 与 markRaw.zh-CN.md 中均有明确说明其根源在源码实现中也有体现详见本文第三节。二、签名与基本用例toJS的类型签名非常简洁interface toJST { (target: T): T }即输入什么类型的值就返回什么类型的值泛型T保证类型在转换前后保持不变。注意它只接受一个参数深度转换是默认行为无需额外配置。官方文档给出的基础用例见 toJS.zh-CN.mdimport { observable, autorun, toJS } from formily/reactive const obs observable({ aa: { bb: { cc: 123, }, }, }) const js toJS(obs) autorun(() { console.log(js.aa.bb.cc) //变化时不会触发 }) js.aa.bb.cc 321在这个例子中obs是一个嵌套三层的 observable 对象obs.aa.bb.cc的读取会产生依赖收集toJS(obs)得到的是普通对象js其内部不再存在 Proxy 代理在autorun中读取js.aa.bb.cc不会建立任何响应式依赖因此注释说明变化时不会触发直接修改js.aa.bb.cc 321由于js是普通对象该赋值既不会触发任何反应也不会反向影响obs。这正是toJS的典型用途生成一份脱离响应式系统的数据快照。三、源码级原理toJS 的深度递归实现toJS的真实实现位于 packages/reactive/src/externals.ts完整逻辑如下export const toJS T(values: T): T { const visited new WeakSetany() const _toJS: typeof toJS (values: any) { if (visited.has(values)) { return values } if (values values[RAW_TYPE]) return values if (isArr(values)) { if (isObservable(values)) { visited.add(values) const res: any [] values.forEach((item: any) { res.push(_toJS(item)) }) visited.delete(values) return res } } else if (isPlainObj(values)) { if (isObservable(values)) { visited.add(values) const res: any {} for (const key in values) { if (hasOwnProperty.call(values, key)) { res[key] _toJS(values[key]) } } visited.delete(values) return res } } return values } return _toJS(values) }结合源码可以梳理出以下几个关键机制1. 深度递归 循环引用保护_toJS对数组isArr和普通对象isPlainObj分别递归处理每个元素/自有属性。为了应对对象自引用如obj.obj obj导致的无限递归实现使用WeakSet记录访问过的对象递归前visited.add(values)递归完成后visited.delete(values)。WeakSet不会阻止对象被垃圾回收且只在递归路径上生效适合作为访问栈标记。这一点在测试 packages/reactive/src/tests/externals.spec.ts 中有直接验证test(recursive references tojs, () { const obj: any { aa: 111 } obj.obj obj const obs observableany(obj) obs.obs obs expect(toJS(obs)).toBeTruthy() const arrObs observable([{ aa: 1 }, { bb: 2 }, { cc: 3 }]) expect(toJS(arrObs)).toEqual([{ aa: 1 }, { bb: 2 }, { cc: 3 }]) })2. 递归出口非 observable 原样返回只有被isObservable判定的对象才进入递归分支其余值原始类型、未被代理的函数、类实例等一律原样返回。isObservable的定义位于同一文件export const isObservable (target: any) { return ProxyRaw.has(target) || !!target?.[ObModelSymbol] }即对象要么是ProxyRawProxy → raw 映射见 environment.ts中登记过的 Proxy要么是带ObModelSymbol的模型对象二者都算 observable。3. markRaw 的短路RAW_TYPE 标记toJS内部有一行非常关键的判断if (values values[RAW_TYPE]) return valuesRAW_TYPE是一个模块级私有 Symbolexternals.ts由markRaw写入。这就是文档警告对已 observable 对象标记 markRaw 后toJS 不会转换它的实现根源一旦对象带有RAW_TYPE标记toJS会直接把它原样返回不做任何深拷贝。四、与 markRaw 的联动理解 RAW_TYPE 的语义markRaw的官方语义是标记任意一个对象或者类原型为永远不可被 observable 劫持优先级比 markObservable 高见 markRaw.zh-CN.md。其实现同样在 externals.tsexport const markRaw T(target: T): T { if (!target) return if (isFn(target)) { target.prototype[RAW_TYPE] true } else { target[RAW_TYPE] true } return target }要点如下函数类级标记对构造函数调用markRaw(Class)会在Class.prototype上写入RAW_TYPE从而影响该类的所有实例实例级标记对实例调用markRaw(instance)只影响当前实例与 toJS 的关系无论对象是否已被 observable 化只要带有RAW_TYPE标记toJS都将其视为应当原样透传的数据不做转换。这与markRaw 优先级最高的语义一致——被标记为 raw 的数据在toJS中同样保持原样。配套测试 externals.spec.ts 从多个角度验证了标记组合的优先级markRaw({ aa: 111 })后再observable()结果不可观察isObservable为 falsemarkRaw(Class)后new Class()的实例均不可观察markRaw(markObservable({...}))与markObservable(markRaw({...}))最终都不可观察证明markRaw 优先级高于 markObservable。五、isSupportObservable谁会被排除在 observable 之外toJS只转换 observable 数据而一个对象能否成为 observable由isSupportObservable同文件 externals.ts决定。该函数对普通对象有一系列排除规则if (target[RAW_TYPE]) return false if (target[OBSERVABLE_TYPE]) return true if ($$typeof in target _owner in target) return false // React 元素 if (target[_isAMomentObject]) return false // moment 对象 if (target[_isJSONSchemaObject]) return false // JSON Schema 对象 if (isFn(target[toJS])) return false // 自带 toJS if (isFn(target[toJSON])) return false // 自带 toJSON return true这些规则同样有测试覆盖externals.spec.tsReact 元素带$$typeof与_owner、moment 对象、JSON Schema 对象会被自动排除避免被代理破坏内部状态自带toJS/toJSON方法的对象不会被代理从而保留自定义序列化能力如 moment 的toJSONMap / WeakMap / Set / WeakSet 始终支持 observablenull、undefined等无效值不支持。理解这一点有助于避免为什么toJS对这个对象没生效的困惑如果对象本身根本没有被 observable 化toJS自然原样返回它。六、实用场景与注意事项1. 典型使用场景提交数据将表单响应式状态toJS化后交给 axios/fetch 发送避免 Proxy 对象在序列化时出现意外行为持久化把状态快照写入 localStorage/IndexedDB 或传给 postMessage保证存储的是纯数据对接外部库echarts、canvas、富文本等库通常不需要响应式传入普通对象更安全高效调试在控制台打印toJS(state)可以看到清晰可读的普通对象结构。2. 注意事项快照是一次性的toJS返回的是全新拷贝数组/对象均为新引用与源 observable 完全解耦修改互不影响markRaw 对象不会被转换被markRaw标记过的对象在toJS中原样透传这是文档明确声明并受源码保障的行为非 observable 原样返回如果传入的本来就是普通对象、函数或原始类型toJS不做任何处理类型保持签名(target: T): T保证转换前后 TypeScript 类型不变可直接用于类型安全的数据出口不推荐反向滥用频繁toJS会创建大量副本若需要拿到底层源数据而非拷贝官方提供rawAPI见 raw.zh-CN.md但官方同样注明通常情况下并不推荐使用。七、小结能力说明功能深度递归将 observable 转为普通 JS 对象签名toJST(target: T): T递归支持数组、普通对象深度遍历WeakSet防循环引用markRaw 联动带RAW_TYPE标记的对象原样返回不转换递归出口非 observable 值原样返回源码位置packages/reactive/src/externals.ts测试验证packages/reactive/src/tests/externals.spec.tstoJS虽小却是响应式数据走出 formily/reactive 世界的必经之路。理解它的递归策略、RAW_TYPE短路逻辑以及与isSupportObservable的配合能帮助你在表单提交、状态持久化和第三方库对接等场景中写出更稳妥的代码。赞分享前端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 响应式核心 API 深度解析toJS 如何把 observable 对象还原为普通 JS 对象Formily 响应式核心 API 深度解析toJS 如何把 observable 对象还原为普通 JS 对象 toJS 是 formily/reactiv前端UI组件Formily Reactive 的 raw API 详解如何从 observable 对象中取回源数据Formily Reactive 的 raw API 详解如何从 observable 对象中取回源数据 导读 raw 是 formily/reactive前端UI组件formily/reactive observe API 全解析深度/浅度监听 Observable 对象的所有写操作formily/reactive observe API 全解析深度/浅度监听 Observable 对象的所有写操作 observe 是 formily前端UI组件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

【外设】之大彩串口显示屏 2026/11/4 15:56:54

【外设】之大彩串口显示屏

大彩串口屏初步使用 1 .官网下载 STM32 屏幕 GUI 设计资料 http://www.gz-dc.com/category/typeid/4112 找到 STM32 Keil 工程,移植相关代码因项目而异进行移植,由于项目简单,本人只对用到的指令接口进行修改。 比如:注意事项&…

阅读更多 →
无法下载Windows系统iso文件 2026/11/3 12:09:35

无法下载Windows系统iso文件

当我遇到这个问题的时候,我打开了一个网站: 登录 然后我打算下载的时候: 突然那个官方的连接就可以下载了:

阅读更多 →
【清华代码熊】DeepSeek V4.1 Flash 后训练详解 2026/11/4 17:01:23

【清华代码熊】DeepSeek V4.1 Flash 后训练详解

📌 上期解析了 DeepSeek V4.1 Flash 模型架构改进,本期解析 DeepSeek V4.1 Flash 预训练/后训练技术: 🌟 预训练:45T 文本 多模态混合语料、直接训练 sparse attention(取消 DeepSeek V4 的 dense 冷启动&…

阅读更多 →
Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ... 2026/11/4 12:33:12

Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ...

文章主要内容和创新点 主要内容 本文聚焦于多模态大语言模型(MLLM)强化学习(RL)训练中的效率问题,提出了一个名为Shuffle-R1的框架。研究发现,当前RL训练存在两个关键缺陷: 优势值坍缩(Advantage Collapsing):批次中大多数优势值集中在零附近,导致有效梯度信号被淹…

阅读更多 →
PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction 2026/11/4 1:25:31

PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction

一、文章主要内容总结 本文聚焦于利用大型语言模型(LLMs)实现个人身份信息(PII)脱敏的研究,旨在解决传统脱敏方法(如基于规则的系统、领域特定命名实体识别(NER)模型)泛化能力差、跨格式/跨语境适应性弱的问题。 研究通过全面评估多种LLM架构(包括密集型LLM(D-LLM…

阅读更多 →
LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model 2026/11/4 3:51:11

LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model

文章主要内容和创新点 主要内容 本文聚焦于二进制图像-文本相关性评估任务(判断图像与文本“相关”或“不相关”),针对该任务中文本格式多样、相关性定义随场景变化等挑战,提出了基于多模态大语言模型(MLLM)的解决方案LLaVA-RE。 模型设计:LLaVA-RE基于LLaVA 1.5架构,…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉