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

Formily Reactive Tracker 详解:手动追踪依赖的响应式核心工具

发布时间:2026/9/23 13:04:14 来源:云帆数科 栏目:资讯中心
Formily Reactive Tracker 详解:手动追踪依赖的响应式核心工具
前端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点击查看免费下载导读Tracker是 Formily 响应式核心库formily/reactive提供的一个手动依赖追踪工具它的设计目标是在依赖发生变化时不重复执行 tracker 函数只触发 scheduler是否需要重新执行由使用者自行决定。这一特性使它成为 React/Vue 等框架适配层连接响应式内核与组件渲染的桥梁——例如formily/reactive-react的useObserver正是基于Tracker实现组件级依赖追踪与按需重渲染。读完本文你将掌握Tracker的完整 API 签名、构造函数参数含义、与autorun的本质区别以及它在真实框架适配中的落地方式。一、Tracker 是什么在 packages/reactive/docs/api/tracker.zh-CN.md 中官方对Tracker的描述非常精炼主要用于接入 React/Vue 的手动追踪依赖工具在依赖发生变化时不会重复执行 tracker 函数需要用户手动重复执行只会触发 scheduler。拆解这句话可以得到三个关键结论手动追踪与autorun自动运行不同Tracker不会自动重复执行被追踪的函数首次执行需要你手动调用track。变化只触发 scheduler当被追踪的响应式依赖发生变化时Tracker只调用构造函数传入的scheduler回调而不会自动重新执行被追踪的视图函数。面向框架适配正因为它把依赖变化和重新执行解耦上层框架可以在 scheduler 中决定何时、以何种方式刷新比如 React 的setState/forceUpdateVue 的依赖更新调度。Tracker定义在 packages/reactive/src/tracker.ts并从 packages/reactive/src/index.ts 通过export * from ./tracker对外导出使用时直接import { Tracker } from formily/reactive即可。二、API 签名与参数说明官方文档给出的类签名如下class Tracker { constructor(scheduler?: (reaction: this[track]) void, name?: string) track: T(tracker?: () T) T dispose: () void }对照源码 packages/reactive/src/tracker.ts 的实现可以对每个成员做更精确的解读1.constructor(scheduler?, name?)参数类型默认值说明scheduler(reaction: Reaction) void无依赖变化时触发的调度函数收到一个reaction参数即track方法本身通常在里面再次调用tracker.track(view)完成重执行namestringTrackerReaction该 Tracker 反应的名字主要用于调试与标识注意签名中reaction: this[track]的写法——scheduler 收到的参数就是当前 Tracker 实例的track方法本身。这一点在源码中体现为constructor( scheduler?: (reaction: Reaction) void, name TrackerReaction ) { this.track._scheduler (callback) { if (this.track._boundary 0) this.dispose() if (isFn(callback)) scheduler(callback) } this.track._name name this.track._boundary 0 }这里有一个容易被忽略的重要行为当_boundary 0即没有嵌套调用时scheduler 触发会先自动dispose()当前 Tracker。也就是说每次依赖变化触发调度时旧的依赖绑定会被清理为接下来重新track收集新依赖做准备。这正是Tracker能实现依赖动态更新见下文测试用例的底层机制。2.track(tracker?)track是被追踪函数执行入口也是源码中的核心逻辑track: Reaction (tracker: Reaction) { if (!isFn(tracker)) return this.results if (this.track._boundary 0) return if (ReactionStack.indexOf(this.track) -1) { releaseBindingReactions(this.track) try { batchStart() ReactionStack.push(this.track) this.results tracker() } finally { ReactionStack.pop() this.track._boundary batchEnd() this.track._boundary 0 } } return this.results }关键行为逐条说明非函数直接返回上次结果if (!isFn(tracker)) return this.resultsisFn定义在 packages/reactive/src/checkers.ts。重入保护_boundary 0时直接返回避免递归重复执行同时检查ReactionStack中是否已有自身防止重复入栈。先释放再收集执行前调用releaseBindingReactions(this.track)清空上一次的依赖绑定然后ReactionStack.push(this.track)将自身压入全局反应栈定义在 packages/reactive/src/environment.ts随后执行tracker()。执行期间任何被读取的响应式属性都会通过bindTargetKeyWithCurrentReaction见 packages/reactive/src/reaction.ts把当前 Tracker 绑定到该属性上。批处理包裹整个执行过程用batchStart()/batchEnd()同样来自 packages/reactive/src/reaction.ts包裹保证执行过程中触发的多次依赖变更被合并到PendingReactions在批处理结束后统一调度。3.dispose()dispose () { disposeBindingReactions(this.track) }调用disposeBindingReactionspackages/reactive/src/reaction.ts 中定义将 Tracker 标记为_disposed true并释放所有已收集的依赖绑定同时挂起相关的 computed 反应。销毁后该 Tracker 不再响应任何依赖变化组件卸载时必须调用它来防止内存泄漏。三、官方用例逐步解读原文档给出了一个完整用例import { observable, Tracker } from formily/reactive const obs observable({ aa: 11, }) const view () { console.log(obs.aa) } const tracker new Tracker(() { tracker.track(view) }) tracker.track(view) obs.aa 22 tracker.dispose()逐步分析执行过程observable({ aa: 11 })创建响应式对象。view读取obs.aa是待追踪的视图函数。new Tracker(() { tracker.track(view) })创建 Trackerscheduler 内部通过闭包再次执行track。注意这里用到了先声明变量、后引用的闭包技巧——scheduler 只在依赖变化时才执行此时tracker变量已完成初始化。tracker.track(view)手动执行第一次追踪收集obs.aa到当前 Tracker 的依赖集合view执行打印11。obs.aa 22修改依赖属性触发runReactionspackages/reactive/src/reaction.ts找到绑定在该属性上的 Tracker 并调用其_scheduler于是tracker.track(view)再次执行view打印22。关键在于view的第二次执行是由 scheduler 手动触发的而不是 Tracker 自动完成的。tracker.dispose()释放所有依赖绑定此后obs.aa再变化也不会触发任何调度。这个流程直观展示了手动追踪 手动重执行的核心模型。四、结合源码与测试的深入理解1. Tracker 与 autorun 的本质区别autorun定义在 packages/reactive/src/autorun.ts它的实现与Tracker非常相似同样的ReactionStack压栈、batchStart/batchEnd包裹但有一个根本差异autorun在创建时立即执行一次reaction()并且把reaction自身作为_scheduler的兜底——依赖变化时 reaction 会被重新调度执行packages/reactive/src/reaction.ts 的runReactions中没有_scheduler或不在批处理范围内时直接调用reaction()。Tracker创建时不执行任何函数首次执行必须手动track且它强制要求传入 scheduler 来决定变化后做什么。一句话总结autorun是自动循环Tracker是手动开关。2. 测试用例验证的关键行为packages/reactive/src/tests/tracker.spec.ts 中有 4 个测试覆盖了 Tracker 最重要的行为base tracker验证基本流程——track首次执行、obs.value 123后 scheduler 触发重新track、dispose后不再响应。nested tracker验证view内部同时存在读与写obs.value obs.value || 321时首次track执行一次、依赖变化后经 scheduler 再执行一次且能拿到正确值。tracker recollect dependencies这是最有价值的一个用例验证依赖动态重收集const view () { fn() if (obs.aa aaa) { return obs.bb } return obs.cc }首次track时obs.aa aaa只收集了obs.aa和obs.bb随后obs.aa 111触发重执行后view分支改变重新收集obs.aa和obs.cc此时再改obs.bb因为obs.bb已不在依赖集合中不会触发调度。这就是track执行前releaseBindingReactions先清空旧依赖的意义所在——依赖集合永远是最近一次执行实际读取的属性。shared scheduler with multi tracker模拟 React StrictMode 场景——两个 Tracker 共享一个 render 流程obs.value变化后只有scheduler1被调用、scheduler2被调用 0 次。这验证了批处理与去重机制同一轮变化中已在调度队列里的反应不会重复入队。3. Tracker 在 React 适配层的真实落地Tracker并非一个孤立概念它是 packages/reactive-react/src/hooks/useObserver.ts 中useObserver的底层实现export const useObserver T extends () any( view: T, options?: IObserverOptions ): ReturnTypeT { const forceUpdate useForceUpdate() const tracker useCompatFactory( () new Tracker(() { if (typeof options?.scheduler function) { options.scheduler(forceUpdate) } else { forceUpdate() } }, options?.displayName) ) return tracker.track(view) }可以看到用useCompatFactory保证组件生命周期内 Tracker 实例只创建一次scheduler 中调用forceUpdate()触发 React 重渲染——这正是依赖变化只触发 scheduler由 scheduler 决定重执行这一设计在框架层的直接体现options.displayName被透传为 Tracker 的 name便于调试。也就是说你在 React 中使用formily/react时每个组件的响应式依赖收集与重渲染调度底层都是通过Tracker完成的。类似地formily/reactive-vue等 Vue 适配层也采用了同一套 Tracker 机制接入 Vue 的渲染调度。五、最佳实践与注意事项基于源码与测试使用Tracker时有几点值得注意首次执行必须手动调用trackTracker 构造函数不会执行任何追踪函数忘记调用track会导致依赖从未被收集。scheduler 中务必重新track如果 scheduler 只做通知而不重新执行track那么视图永远不会用新值更新官方用例中的tracker.track(view)模式是最标准的写法。及时dispose组件卸载或 Tracker 不再需要时调用dispose()释放依赖绑定避免无效调度与内存泄漏。从源码看每次 scheduler 触发前也会先dispose旧绑定但显式调用仍是必须的收尾动作。循环依赖需自控由于 scheduler 由用户编写务必保证 scheduler 内部的track不会造成无限递归_boundary重入保护只能防住同步重入无法替代业务层面的终止条件。嵌套 Tracker 安全ReactionStack的存在保证嵌套track时依赖绑定到正确的反应上测试用例nested tracker已验证该场景。结语Tracker是 Formily 响应式体系中最贴近框架适配层面的基础工具它通过手动追踪、变化只触发 scheduler的设计把依赖收集与执行调度彻底解耦从而让 React/Vue 等不同渲染模型都能以统一方式接入formily/reactive。理解它就理解了 Formily 响应式内核如何与前端框架握手。进一步探索时可以对照阅读 packages/reactive/src/tracker.ts 源码、packages/reactive/src/tests/tracker.spec.ts 测试以及 packages/reactive/docs/api/autorun.zh-CN.md 中 autorun 的对比实现。赞分享前端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 响应式核心autorun 依赖追踪 API 全面解析与源码级实战指南Formily Reactive 响应式核心autorun 依赖追踪 API 全面解析与源码级实战指南 导读 formily/reactive 是 Form前端UI组件Formily Reactive 之 action掌握批量更新与依赖追踪隔离的响应式编程利器Formily Reactive 之 action掌握批量更新与依赖追踪隔离的响应式编程利器 导读 action 是 Formily 响应式核心 formi前端UI组件Formily 核心架构解析基于 formily/reactive 响应式领域模型的设计原理Formily 核心架构解析基于 formily/reactive 响应式领域模型的设计原理 导读 本文围绕 packages/core/docs/guid前端UI组件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

基于VMD排列熵与ELM的滚动轴承故障诊断Python实现
基于VMD排列熵与ELM的滚动轴承故障诊断Python实现

简介:这份资源面向机械故障诊断方向的研究人员、工程师及学生,提供基于VMD排列熵与ELM的滚动轴承故障诊断完整Python实现。项目将变分模态分解用于非平稳振动信号处理,分离故障特征频率,再以排列熵量化各模态分量的复杂度&#xf… · 2026/9/23 13:04:07

情商低的9种表现:新手避坑指南,别让沟通成为你的技术瓶颈
情商低的9种表现:新手避坑指南,别让沟通成为你的技术瓶颈

情商低的9种表现:新手避坑指南,别让沟通成为你的技术瓶颈 官方文档动辄几百页,新手往往在浩如烟海的文字中迷失,抓不住核心痛点,导致“新手避坑”变成了一句空话。很多技术人以为只要代码写得漂亮就能晋升,却忽略了职场中那些看不见的“软技能”陷阱。… · 2026/9/23 13:04:07

Apache Arrow Java 开发指南:日志、测试、基准与代码风格全解析
Apache Arrow Java 开发指南:日志、测试、基准与代码风格全解析

数据工程大数据序列化数据分析 【免费下载链接】arrow Apache Arrow is a multi-language toolbox for accelerated data interchange and in-memory processing 项目地址: https://gitcode.com/gh_mirrors/arrow13/arrow 点击查看 免费下载 Apache Arrow 的 Java … · 2026/9/23 13:04:07

细胞图像分割实战:UNet与UNet++选型及Python实现
细胞图像分割实战:UNet与UNet++选型及Python实现

简介:这份资源面向计算机相关专业正在做毕业设计、课程设计或期末大作业的学生,以及需要医学图像分割实战练习的学习者,提供基于UNet与UNet两种经典网络对细胞图像进行分割的完整Python实现。压缩包共48个文件,以44个py源码为主&a… · 2026/9/23 13:46:32

3个真实项目教你一文搞懂开启bridge功能的底层逻辑
3个真实项目教你一文搞懂开启bridge功能的底层逻辑

3个真实项目教你一文搞懂开启bridge功能的底层逻辑 刚入行写代码,是不是总卡在“语法都会,项目跑不通”的坑里?看着文档里的 bridge… · 2026/9/23 13:46:32

基于 Docker Compose 搭建 MySQL→Flink CDC→Doris 实时同步链路实战指南
基于 Docker Compose 搭建 MySQL→Flink CDC→Doris 实时同步链路实战指南

基于 Docker Compose 搭建 MySQL→Flink CDC→Doris 实时同步链路实战指南 【免费下载链接】doris Apache Doris is an easy-to-use, high performance and unified analytics database. 项目地址: https://gitcode.com/gh_mirrors/dori/doris 导读 本文基于 docker/ru… · 2026/9/23 13:46:32

IVUS三维重建实战:从96张切片到可旋转血管模型
IVUS三维重建实战:从96张切片到可旋转血管模型

简介:本资源为IVUS血管内超声三维重建的Python实现源码包,面向医学图像处理方向的学生、研究人员及工程开发者,尤其适合计算机、生物医学工程、电子信息等专业用于课程设计、毕业设计或项目立项演示。包内共97个文件,以94张jpg图像… · 2026/9/23 13:46:32

libvips 基础类型体系全解:VipsArea 内存块、数组容器与 GValue 辅助函数实战指南
libvips 基础类型体系全解:VipsArea 内存块、数组容器与 GValue 辅助函数实战指南

图像处理 【免费下载链接】libvips A fast image processing library with low memory needs. 项目地址: https://gitcode.com/gh_mirrors/li/libvips 点击查看 免费下载 libvips 的 API 参考将一组贯穿整个库的基础数据类型与辅助工具归入 "Basic" 一节… · 2026/9/23 13:46:32

印制电路手册第6版中文高清附目录:从工艺基线到阻抗计算的PCB设计指南
印制电路手册第6版中文高清附目录:从工艺基线到阻抗计算的PCB设计指南

简介:《印制电路手册(第6版)》中文高清版是一部面向PCB设计与制造领域的权威工具书,适合电子工程师、PCB设计师、制造工艺与质量检测人员系统学习与查阅。全文附带详细目录,从PCB基础定义与设计流程讲起,涵… · 2026/9/23 13:46:26

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

了解更多?预约专属演示

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

企业微信二维码