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),仅供参考

相关新闻

弗洛伊德梦的解析保姆级教程:3分钟搞定Stack Trace报错

弗洛伊德梦的解析保姆级教程:3分钟搞定Stack Trace报错

弗洛伊德梦的解析保姆级教程:3分钟搞定Stack Trace报错 盯着屏幕满屏红色的报错信息,你是不是感觉脑瓜子嗡嗡的?特别是那个长得像天书一样的 Stack…

2026/9/23 13:04:45 阅读更多 →
OPA 2022 年 10 月社区月报解读:v0.45.0 新特性与政策即代码生态进展

OPA 2022 年 10 月社区月报解读:v0.45.0 新特性与政策即代码生态进展

后端认证鉴权云原生 【免费下载链接】opa Open Policy Agent (OPA) is an open source, general-purpose policy engine. 项目地址: https://gitcode.com/gh_mirrors/op/opa 点击查看 免费下载 本篇文章基于 Open Policy Agent(OPA)官方 202…

2026/9/23 13:03:45 阅读更多 →
3个坑教你用Python生成好听的qq网名女生速查手册

3个坑教你用Python生成好听的qq网名女生速查手册

3个坑教你用Python生成好听的qq网名女生速查手册 别再对着屏幕发呆,看了一堆教程还是不会写项目,那是你没抓住核心。今天不聊虚的,直接给你一份基于Python的【好听的qq网名女生】生成器,附带一份实战速查手册。这不是简单的字符拼接,而…

2026/9/23 13:03:45 阅读更多 →

最新新闻

PHPStan 错误标识符 paramOut.nestedUnusedType 详解:@param-out 嵌套类型过宽的精修与收窄指南

PHPStan 错误标识符 paramOut.nestedUnusedType 详解:@param-out 嵌套类型过宽的精修与收窄指南

开发工具代码质量静态分析 【免费下载链接】phpstan PHP Static Analysis Tool - discover bugs in your code without running it! 项目地址: https://gitcode.com/gh_mirrors/ph/phpstan 点击查看 免费下载 导读 本文围绕 PHPStan 错误标识符 paramOut.nestedUn…

2026/9/23 13:40:25 阅读更多 →
慧耕思的博客源码解析:3个实战技巧解决环境配置卡顿

慧耕思的博客源码解析:3个实战技巧解决环境配置卡顿

慧耕思的博客源码解析:3个实战技巧解决环境配置卡顿 刚接手新项目,或者从别的岗位转过来,最怕什么?不是写不出逻辑,而是 配置环境就卡半天 。 明明照着教程敲了半小时,报错信息像天书一样滚过屏幕。你盯着那个红色的…

2026/9/23 13:40:25 阅读更多 →
nuqs 包体积优化实战:用子代理并行“Bake-Off“竞赛把 Client Bundle 压到 6 kB 以内

nuqs 包体积优化实战:用子代理并行“Bake-Off“竞赛把 Client Bundle 压到 6 kB 以内

nuqs 包体积优化实战:用子代理并行"Bake-Off"竞赛把 Client Bundle 压到 6 kB 以内 【免费下载链接】next-usequerystate Type-safe search params state manager for React frameworks - Like useState, but stored in the URL query string. 项目地址…

2026/9/23 13:40:25 阅读更多 →
Claude Code Haha v0.2.6 更新解读:H5 安全访问恢复、会话批量管理与桌面体验打磨

Claude Code Haha v0.2.6 更新解读:H5 安全访问恢复、会话批量管理与桌面体验打磨

Claude Code Haha v0.2.6 更新解读:H5 安全访问恢复、会话批量管理与桌面体验打磨 【免费下载链接】cc-haha Local-first cross-platform desktop workspace for Claude Code / agents: multi-agent, Git worktrees, code diffs, skill marketplace, multi-model, C…

2026/9/23 13:40:25 阅读更多 →
Ceph CPU 性能剖析实战:使用 OProfile 与 perf 定位守护进程热点

Ceph CPU 性能剖析实战:使用 OProfile 与 perf 定位守护进程热点

存储分布式文件系统对象存储后端高可用 【免费下载链接】ceph Ceph is a distributed object, block, and file storage platform 项目地址: https://gitcode.com/gh_mirrors/ce/ceph 点击查看 免费下载 本指南面向 Ceph 开发者与集群运维人员,介绍如何…

2026/9/23 13:40:25 阅读更多 →
Claude会话数据泄漏再敲警钟:用TaoToken统一Key给AI助手加一道门禁

Claude会话数据泄漏再敲警钟:用TaoToken统一Key给AI助手加一道门禁

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/23 13:39:24 阅读更多 →

日新闻

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

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

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

2026/9/23 0:00:23 阅读更多 →
2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我 刚把开发环境的显示器从1080P换到2K,跑老项目直接报错,版本升级后 API…

2026/9/23 0:01:25 阅读更多 →
3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点 官方文档翻了三遍还是云里雾里?别急,美眉图在实战项目中常被用来做数据可视化,但它的原理比你想的简单。今天咱们直接上手,用一个完整的小项目把美眉图跑通,不再死磕那些冗长的理论说明。…

2026/9/23 0:01:25 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/23 4:55:02 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/23 4:49:06 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/23 9:53:41 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/23 9:53:40 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/23 9:53:40 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/23 9:53:40 阅读更多 →