资讯详情

Formily Reactive 源码解析:raw API 如何取回 Observable 源数据及为什么官方不推荐使用

📅 2026/9/24 10:04:49 | 华诺云谱 👁 阅读
Formily Reactive 源码解析:raw API 如何取回 Observable 源数据及为什么官方不推荐使用
前端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点击查看免费下载导读raw是formily/reactive响应式内核对外暴露的一个逃生舱式 API用于从 observable 代理对象中取回其背后的原始源数据。本文以 raw.zh-CN.md 文档为核心先给出它的签名、用例与使用注意点再深入到 externals.ts 的实现讲清它依赖的ProxyRaw弱引用表与ObModelSymbol两条取数路径并对照markRaw、toJS、isObservable等邻近 API 说明它不推荐使用的真正原因。读完本文你将理解 reactive 代理层与源数据层的映射关系并能在调试、序列化、与第三方库交互等场景中正确决策何时该用raw、何时该用toJS。raw 是什么从代理对象取回源数据在 Formily 的响应式体系中observable()返回的是一个 Proxy 包装后的代理对象详见 observable 文档。日常开发中读写代理对象即可触发依赖收集与响应但在少数必须拿到原始 JS 对象的场景例如与不感知 Proxy 的第三方库对接、序列化、对象身份比对中需要把代理打回原形。raw正是为此设计的最小工具函数其签名如下见 raw.zh-CN.mdinterface rawT extends object { (target: T): T }入参是被 observable 化的对象返回值是与之对应的源数据对象泛型T保证传入与返回类型一致。官方用例与关键注意点文档给出了最小可运行示例raw.zh-CN.mdimport { raw, observable } from formily/reactive const obs observable({}) obs.aa { bb: 123 } console.log(raw(obs)) console.log(raw(obs.aa))对raw(obs)而言返回的是obs背后未被代理的原始空对象而对raw(obs.aa)而言返回结果则取决于obs.aa这个对象是否被 observable 化——文档特别用Alert强调了这一点注意只能获取当前对象的源数据不包括深层对象属性也就是说raw只做一层逆向映射。obs.aa { bb: 123 }的赋值发生在代理对象上赋值完成后obs.aa是原始对象还是新的代理对象由 observable 创建时对嵌套对象的递归代理策略决定raw(obs)返回的原始对象上的aa属性依然是原始值并不会因为调用了raw(obs)就顺带把obs.aa也还原成源数据。源码实现两条取数路径raw的实际实现非常精炼位于 externals.tsexport const raw T(target: T): T { if (target?.[ObModelSymbol]) return target[ObModelSymbol] return ProxyRaw.get(target as any) || target }两条分支分别对应两类 observable 形态1. 基于 Proxy 的标准 observable走ProxyRaw分支当调用observable()创建普通对象、数组或 Map/Set 等集合时内部通过 internals.ts 的createNormalProxy/createCollectionProxy生成代理并同时维护两套双向映射const proxy new Proxy(target, baseHandlers) ProxyRaw.set(proxy, target) // proxy - 源数据 if (shallow) { RawShallowProxy.set(target, proxy) } else { RawProxy.set(target, proxy) // 源数据 - proxy }其中ProxyRaw与RawProxy都是定义在 environment.ts 中的WeakMapexport const ProxyRaw new WeakMap() export const RawProxy new WeakMap() export const RawShallowProxy new WeakMap()使用WeakMap而非普通 Map 是刻意的设计键为代理对象、值为源对象两者互不持有强引用从而保证代理对象与源对象都能被 GC 正常回收不会因取数映射造成内存泄漏。raw的ProxyRaw.get(target) || target语义也很清晰——命中映射则返回源数据未命中传入的本来就是普通对象则原样返回。2. 基于注解创建的模型走ObModelSymbol分支model()、ref()、computed()等注解生成的对象不走 Proxy 包装而是直接在生产对象上挂一个ObModelSymbol标记指向真正的数据容器。例如 model.tstarget[ObModelSymbol] target以及 annotations/ref.ts 与 annotations/computed.ts 中的proxy[ObModelSymbol] store。raw首行target?.[ObModelSymbol]的判空访问正是为了兼容这类对象而annotations/box.tsbox()注解则仍走 Proxy 路线在 box.ts 中执行ProxyRaw.set(proxy, store)。顺带一提isObservable的实现与raw共享了同一套判定基础设施externals.tsexport const isObservable (target: any) { return ProxyRaw.has(target) || !!target?.[ObModelSymbol] }即能被raw还原的对象与被判定为 observable 的对象在判定逻辑上是一一对应的。测试用例对语义的印证raw的行为在集合类型的测试中得到了直接验证。collections-map.spec.ts 开头即断言test(should be a proper JS Map, () { const map observable(new Map()) expect(map).toBeInstanceOf(Map) expect(raw(map)).toBeInstanceOf(Map) })同样的断言也出现在 collections-set.spec.ts、collections-weakmap.spec.ts、collections-weakset.spec.ts 中。这印证了两个事实代理后的 Map 依然是Map实例拦截了get/has等内部方法做依赖追踪raw(map)拿到的也是真正的Map实例源对象因此可以在其上直接执行raw(map).set(key, value)这样的原生操作——但要注意绕过代理直接操作源对象将不会触发依赖收集与响应派发。集合测试中还有一类高频用法值得注意在autorun追踪函数内部通过raw(weakMap).has(value)见 collections-weakmap.spec.ts、raw(map).get(key)见 collections-map.spec.ts读取值。这是在读取源数据的同时保持响应式——因为raw(map)本身是稳定引用每次返回同一个源对象读取的是源对象的原始值而依赖收集照常发生在代理层。这是一种合法的脱壳惯用法与文档不推荐使用的警示并不冲突警示针对的是拿 raw 结果去直接操作/绕过响应式而非在追踪函数里读原始值。为什么文档明确不推荐使用文档开篇即写明通常情况下并不推荐使用该 APIraw.zh-CN.md结合源码可以从四个层面理解这一警示破坏响应式链raw返回的源对象没有 Proxy 拦截直接对它读写不会触发依赖收集与副作用派发等于绕过整个响应式系统仅限单层还原文档强调不包括深层对象属性深层次对象各自持有独立代理需要逐层raw才能全部还原容易遗漏导致拿到半代理半原始的混合结构身份不一致风险同一源对象可能对应普通代理与浅代理RawShallowProxy两种代理形态混用raw与直接引用代理时容易产生同一数据、多个身份的困惑有更合适的替代品若目的是得到完整可序列化的纯 JS 数据应使用toJS()实现见 externals.ts它基于WeakSet防循环递归地把数组与普通对象逐层展开为普通值若目的是让某个对象完全脱离响应式应在源头使用markRaw()externals.ts内部以RAW_TYPE符号标记使isSupportObservable直接放行不再代理而不是事后用raw取回。适用场景与总结综合来看raw是一个低层工具箱里的函数最适合以下受限场景调试与断言确认某个对象是否被代理、验证代理与源对象的身份关系与不感知 Proxy 的第三方库对接需要传递真 JS 对象时临时脱壳响应式追踪内读取原始值如集合测试所示在autorun/tracker回调里用raw(col).has(key)读值既拿原始数据又不中断依赖收集。在绝大多数业务代码中优先考虑observable、markRaw与toJS的组合方案只有在你明确知道自己要的是当前对象这一层的源数据引用时才引入raw。理解它的两条取数路径ProxyRaw弱引用表与ObModelSymbol标记、单层还原语义以及WeakMap带来的无泄漏保证就能在 Formily 的响应式世界里准确驾驭这个逃生舱。赞分享前端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 的 raw API 详解如何从 observable 对象中取回源数据Formily Reactive 的 raw API 详解如何从 observable 对象中取回源数据 导读 raw 是 formily/reactive前端UI组件为什么 Docker 版 Odysseus 备份不包含 ChromaDB 数据以及如何单独备份为什么 Docker 版 Odysseus 备份不包含 ChromaDB 数据以及如何单独备份 如果你用 Docker Compose 运行 Odysseus人工智能AI 应用后端前端RAGAI AgentMCP 服务本地部署深度研究Rust 编译错误 E0533 详解为什么模式匹配里不能用方法名以及如何修复附 rustc 源码分析Rust 编译错误 E0533 详解为什么模式匹配里不能用方法名以及如何修复附 rustc 源码分析 E0533 是 rustc 在模式匹配阶段抛出的错编程语言编译器语言运行时标准库创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。