尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

@formily/reactive-vue observer:将 Vue 组件渲染变为 Reaction 响应式追踪的完整指南

发布时间:2026/9/24 4:48:43

资讯中心
01
ARTICLE

@formily/reactive-vue observer:将 Vue 组件渲染变为 Reaction 响应式追踪的完整指南

@formily/reactive-vue observer:将 Vue 组件渲染变为 Reaction 响应式追踪的完整指南
前端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 体系中formily/reactive提供了独立于框架的响应式内核而formily/reactive-vue则负责把这一内核接入 Vue 的组件生命周期。本文聚焦formily/reactive-vue的核心 API ——observer它能把一个 Vue 组件的渲染方法改造成一个Reaction使得组件每次重新渲染时自动收集所消费的响应式依赖并在依赖变化时精准触发重渲染。读完本文你将掌握observer的签名与参数语义、Vue 2 / Vue 3 双版本下的底层实现原理、scheduler调度器的正确用法以及它与 Vue 原生响应系统的共存方式。本文基于当前仓库中 packages/reactive/docs/api/vue/observer.md 编写并以 packages/reactive-vue 包的真实源码与测试用例作为佐证。一、核心概念把渲染函数变成 Reactionobserver的官方描述非常凝练In Vue, the component rendering method is changed to Reaction, and dependencies are collected every time the view is re-rendered, and dependencies are updated automatically to re-render.翻译过来即在 Vue 中将组件渲染方法变成 Reaction每次视图重新渲染时收集依赖依赖更新时自动重渲染。要理解这句话需要先了解formily/reactive中两个核心原语详见 packages/reactive/src/index.ts 及文档 packages/reactive/docs/api/reaction.mdobservable把普通对象包装成可被追踪的响应式数据Tracker / reaction执行一段函数effect在函数执行期间读取到的 observable 属性会被自动登记为依赖此后任何依赖变化都会触发 effect 重新执行。observer的工作就是把 Vue 组件的render逻辑放进这样一个 Tracker 中执行渲染期间读到的observable属性全部成为该组件的依赖此后只要任一依赖变化组件就会自动进入重渲染流程。这正是 Formily 表单元件能以高性能、细粒度方式响应表单状态变化的基础设施。二、API 签名与参数说明2.1 完整签名原文档给出了如下 TypeScript 签名interface IObserverOptions { scheduler?: (updater: () void) void //调度器可以手动控制更新时机 name?: string //包装后的组件的name } interface observerT extends VueComponent { (component: T, options?: IObserverOptions): T }observer接收一个 Vue 组件选项对象或类组件和可选配置返回一个包装后的等价组件返回值的类型保持T不变因此对调用方而言 API 形状完全透明。该签名与仓库中 packages/reactive-vue/src/types.ts 的定义一一对应export interface IObserverOptions { name?: string scheduler?: (updater: () void) void }2.2 参数语义参数类型说明默认行为componentT extends VueComponent待包装的 Vue 组件选项对象或 Vue 类必填options.namestring包装后组件的名称优先取显式传入的name其次回退到组件自身name/_componentTag/ 构造函数名最终回退到componentoptions.scheduler(updater: () void) void调度器在依赖变化时接管重渲染的时机控制不传则依赖变化后立即同步重渲染关于name的回退优先级源码 packages/reactive-vue/src/observer/observerInVue2.ts 给出了精确顺序const name observerOptions?.name || (Component as any).name || (Component as any)._componentTag || (Component.constructor Component.constructor.name) || component对应的测试 packages/reactive-vue/src/tests/observer.spec.ts 验证了两种边界无任何名称时最终得到component显式传入{ name: abc }时得到abc。2.3 scheduler 的用途scheduler是IObserverOptions中最有实战价值的参数当依赖更新发生时observer不会直接执行重渲染而是把执行重渲染这个动作即updater回调交给你手动编排。典型场景包括合并高频更新在一帧内多次修改响应式数据时只调度一次渲染延迟渲染配合setTimeout/requestAnimationFrame把渲染推迟到合适时机异步批量刷新把更新任务推入自己的批处理队列。测试 packages/reactive-vue/src/tests/observer.spec.ts 给出了一个完整的节流式 scheduler 示例100ms 内的多次更新被合并为一次渲染。三、完整用例与 Vue 响应系统共存原文档提供了一段开箱即用的示例。一个值得强调的设计点是observer包装后的组件不排斥 Vue 原生响应系统——data()中既可以是普通 Vue 响应式数据也可以混入formily/reactive的observable对象两者可以在同一组件中并存。template div div input :style{ height: 28, padding: 0 8px, border: 2px solid #888, borderRadius: 3, } :valueobs.value input(e) { obs.value e.target.value } / /div div{{obs.value}}/div /div /template script import { observable } from formily/reactive import { observer } from formily/reactive-vue export default observer({ data() { // 能与 vue 的响应系统共存 const obs observable({ value: Hello world, }) return { obs, } }, }) /script运行逻辑拆解observable({ value: Hello world })创建一个formily/reactive响应式对象模板中:valueobs.value与{{obs.value}}在渲染阶段被observer追踪obs.value成为组件依赖输入框input事件改写obs.value依赖变化触发 Tracker 回调组件自动重渲染视图同步更新。3.1 共存机制collectData 的过滤逻辑能与 Vue 响应系统共存不是一句口号其底层实现位于 packages/reactive-vue/src/observer/collectData.ts。observer在 Vue 2 分支中会重写data选项用collectData对data()的返回值做分流处理export default function collectData(vm: any, data?: any) { const dataDefinition typeof data function ? data.call(vm, vm) : data || {} const filteredData Object.keys(dataDefinition).reduce( (result: any, field) { const value dataDefinition[field] if (isObservable(value)) { Object.defineProperty(vm, field, { configurable: true, get() { return value }, }) } else { result[field] value } return result }, {} ) return filteredData }关键行为对data()返回的每个字段调用isObservable来自formily/reactive做判定若是observable对象则不交给 Vue 的响应式系统而是直接在vm上定义 getter 透传引用避免 Vue 对已代理对象再次递归代理带来的性能损耗与行为冲突若是普通数据则保持原样交给 Vue 处理与 Vue 原生响应系统无缝衔接。对应的测试 packages/reactive-vue/src/tests/observer.spec.ts 验证了这一点经过collectData过滤后modelobservable仍由formily/reactive追踪并触发 autorun而普通字段target完全不受formily/reactive影响。四、双版本实现Vue 2 与 Vue 3 的分流formily/reactive-vue借助vue-demi同时支持 Vue 2 与 Vue 3。入口 packages/reactive-vue/src/observer/index.ts 根据isVue2标志分发到两套实现export function observerC(baseComponent: C, options?: IObserverOptions): C { if (isVue2) { return observerV2(baseComponent, options) } else { return observerV3(baseComponent, options) } }依赖关系见 packages/reactive-vue/package.json运行时依赖formily/reactive2.3.7与vue-demi0.13.6同时以vue^2.6.0 || 3.0.0-rc.0和vue/composition-apiVue 2 下使用 Composition API 时需要作为 peer 依赖。4.1 Vue 2重写 $mount/$destroy接管原生渲染Vue 2 分支位于 packages/reactive-vue/src/observer/observerInVue2.ts其核心思路是复制原始组件选项避免修改原组件包装data为collectData处理后的版本通过Super.extend(options)生成扩展组件其中Super取原组件的原型链构造器规避Vue.extend时生命周期被重复调用的问题重写原型上的$mount首次挂载时在batch包裹下用Tracker.track执行原生渲染并把 Vue 的_watcher.getter替换为reactiveRender使组件后续每次渲染都重新进入 Tracker 收集依赖重写$destroy组件销毁前先disposeTracker杜绝销毁后的无效追踪。const reactiveRender () { batch(() { tracker.track(() { if (!mounted) { $mount.apply(this, args) mounted true nativeRenderOfVue this._watcher.getter // rewrite the native render method of vue with our reactive tracker render // thus if component updated by vue watcher, we could re track and collect dependencies by formily/reactive this._watcher.getter reactiveRender } else { nativeRenderOfVue.call(this, this) } }) }) return this }而 Tracker 的回调则负责在依赖变化时决定何时、以何种方式重渲染const tracker new Tracker(() { if ( reactiveRender.$vm._isBeingDestroyed || reactiveRender.$vm._isDestroyed ) { return tracker.dispose() } if ( observerOptions?.scheduler typeof observerOptions.scheduler function ) { observerOptions.scheduler(reactiveRender) } else { reactiveRender() } })这里有两处值得注意的实现细节销毁保护检测到组件处于_isBeingDestroyed/_isDestroyed状态时直接tracker.dispose()。测试 packages/reactive-vue/src/tests/observer.spec.ts 专门验证了watcher 销毁后不再触发渲染这一行为scheduler 接管依赖变化时优先调用用户传入的scheduler否则立即reactiveRender()。4.2 Vue 3setup 内注入 useObserverVue 3 分支位于 packages/reactive-vue/src/observer/observerInVue3.ts实现更加轻量包装返回一个带setup的新组件在用户setup执行前先调用useObserver建立追踪export const observer function (opts: any, options?: IObserverOptions): any { const name options?.name || opts.name || ObservableComponent return { name, ...opts, setup(props: Recordstring, any, context: any) { useObserver(options) return opts?.setup?.(props, context) }, } }useObserver定义在 packages/reactive-vue/src/hooks/useObserver.ts在组件实例上拦截effect属性把组件更新函数包装进Tracker.track并支持onBeforeUnmount时disposeTracker防止内存泄漏依赖变化时优先走options.scheduler否则调用vm.proxy.$forceUpdate()强制重渲染。需要说明的是这段实现中有一个对 Formily 官方 issue#2655的兼容性处理其细节随 Vue 版本迭代可能有差异本文仅陈述源码当前状态。五、在 Formily 生态中的定位与典型用法5.1 使用入口formily/reactive-vue的公共导出见 packages/reactive-vue/src/index.ts包含三部分export * from ./observer export * from ./hooks export * from ./types即observer含collectData、useObserverhook 以及IObserverOptions类型。安装后在项目中即可直接使用# 以 npm 为例 npm install formily/reactive-vue使用时注意 Vue 2 场景需自行安装vue^2.6.0并视情况引入vue/composition-apiVue 3 则直接使用vue3。5.2 何时使用 observer从源码与测试可以归纳出observer的典型适用场景组件内消费了formily/reactive的 observable 数据例如 Formily 表单模型Form/Field的value、errors、loading等状态希望渲染粒度精确到组件级只有真正读取到的依赖变化才触发本组件重渲染而不是整棵组件树重渲染需要手动控制更新时机配合scheduler做渲染合并或延迟渲染。5.3 与 React 版本 observer 的差异当前仓库同时提供了 React 版本文档 packages/reactive/docs/api/react/observer.md两者理念相同把渲染变成 Reaction但存在明显差异维度formily/reactive-vueformily/reactive-react支持组件形态Vue 组件选项对象 / 类 / 带 setup 的组件仅函数组件Function Component名称配置项namedisplayName透传 ref 配置无forwardRef附加能力collectData与 Vue 响应系统共存Observer组件Render Props 局部精确渲染这一对照可以帮助你在跨框架复用 Formily 响应式状态时快速对齐 API 语义。六、常见问题与最佳实践6.1 依赖收集每次渲染都发生是特性而非缺陷observer的设计是在每次渲染时重新收集依赖。这带来两个好处条件渲染下的依赖是动态的if (obs.a) return obs.b这种写法下只有当obs.a为真时obs.b才成为依赖之后obs.b的变化不会在obs.a为假时误触发渲染依赖集合始终精确收敛到当前视图真正消费的数据。代价是每次渲染都需要一次追踪开销但batch与细粒度收集保证了整体成本可控。6.2 scheduler 的正确姿势scheduler的updater就是执行重渲染的函数务必在合适时机调用且不要重复调用。参考测试中的节流写法const Component observer( { data() { return { model } }, render(h) { return h(button, { on: { click: this.model.setAge }, domProps: { textContent: this.model.age }, }) }, }, { scheduler: (update) { clearTimeout(timer) timer setTimeout(update, 100) }, } )要点连续触发时先clearTimeout再重新计时实现最后一次生效的合并效果组件销毁后 Tracker 已 dispose不应再调度渲染。6.3 组件销毁后的安全性observer两套实现都内置了销毁保护Vue 2Tracker 回调检测_isBeingDestroyed/_isDestroyed后自动dispose$destroy也会显式执行 disposer见 observerInVue2.tsVue 3useObserver在onBeforeUnmount中 dispose Tracker见 useObserver.ts。因此无需在业务代码里手动清理这也是测试 observer.spec.ts 覆盖的核心保障之一。6.4 不要混用两套响应式代理同一对象尽管collectData已自动把observable对象从 Vue 响应式系统中摘出但建议在业务层保持清晰边界同一份状态要么归formily/reactive管要么归 Vue 管避免对同一对象同时施加两套代理导致的行为不确定性。七、小结observer是formily/reactive-vue的基石 API它把 Vue 组件的渲染生命周期与formily/reactive的响应式追踪机制打通每次渲染收集依赖、依赖更新自动重渲染实现组件级精确更新通过collectData与 Vue 原生响应系统和平共存Vue 2 分支重写$mount/$destroy并接管原生渲染Vue 3 分支通过setupuseObserver注入追踪scheduler提供手动编排渲染时机的自由name帮助调试与组件树识别。深入阅读建议API 文档packages/reactive/docs/api/vue/observer.md、packages/reactive/docs/api/vue/observer.zh-CN.md实现源码packages/reactive-vue/src/observer/index.ts、observerInVue2.ts、observerInVue3.ts、collectData.ts、useObserver.ts测试用例packages/reactive-vue/src/tests/observer.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 响应式渲染formily/reactive-react 的 observer 与 Observer 使用指南Formily 响应式渲染formily/reactive react 的 observer 与 Observer 使用指南 本文是 Formily 官方文前端UI组件Formily 响应式渲染指南深入理解 observer HOC 与 Observer 组件Formily 响应式渲染指南深入理解 observer HOC 与 Observer 组件 导读 本文以 Formily 官方文档 observer.md前端UI组件formily Vue 响应式组件observer API 用法与原理解读formily Vue 响应式组件observer API 用法与原理解读 导读 在 Formily 的 Vue 体系中 observer 是让普通 Vue前端UI组件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。