ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Formily 响应式核心 @formily/reactive 深度指南:安装、快速开始与源码级原理

Formily 响应式核心 @formily/reactive 深度指南:安装、快速开始与源码级原理 前端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 是 Formily 体系中与 UI 框架无关的响应式状态管理内核面向 DDD领域驱动设计建模提供 Observable 数据劫持、依赖追踪、按需渲染与批量更新能力。本篇指南以官方文档为主体结合本仓库源码展开帮助你在 React/Vue 项目中完成安装、快速上手并深入理解 observable、reaction、computed、batch 等核心 API 的底层工作机制。安装formily/reactive 是一个独立的 npm 包不依赖任何 UI 框架可与 React、React Native、Vue 2、Vue 3 组合使用。在项目根目录执行$ npm install --save formily/reactive如需在 React 中使用响应式订阅组件还需要安装配套的桥接包$ npm install --save formily/reactive-react若在 Vue 2 / Vue 3 中使用则安装$ npm install --save formily/reactive-vue从仓库源码看包的所有对外能力统一从入口文件 packages/reactive/src/index.ts 导出包括batch、action、untracked、observable、model、autorun、tracker、observe以及externals中的各类辅助工具安装后即可按需引入。快速开始官方文档给出了一个最精简的响应式示例创建一个observable对象用observer包裹 React 组件实现输入框与文本的实时联动。import React from react import { observable } from formily/reactive import { observer } from formily/reactive-react const obs observable({ value: Hello world, }) export default observer(() { return ( div div input style{{ height: 28, padding: 0 8px, border: 2px solid #888, borderRadius: 3, }} value{obs.value} onChange{(e) { obs.value e.target.value }} / /div div{obs.value}/div /div ) })这个示例演示了完整的响应式闭环observable({ value: Hello world })通过 ES Proxy 创建可订阅对象组件内读取obs.value时observer会收集该依赖并绑定订阅关系输入框onChange中对obs.value赋值写操作自动触发组件重新渲染。即使不使用任何框架也可以用纯 JavaScript 体验同样的响应式效果import { observable, autorun } from formily/reactive const obs observable({ value: Hello world }) autorun(() { console.log(obs.value) }) obs.value Formily Reactive // 控制台依次输出Hello world、Formily Reactive核心概念formily/reactive 的响应式模型包含四个核心概念Observable、Reaction、Computed 与 Batch。下面逐一结合文档与源码进行说明。Observable可订阅对象observable是整个响应式模型的基础。一个 observable 对象字面意思是可订阅对象——每当对该对象的属性进行数据操作时会自动通知订阅者。formily/reactive 创建 observable 对象主要通过 ES Proxy 实现可以完美劫持数据操作读取、赋值、删除、遍历等。创建 observable 对象主要使用以下 APIobservable函数创建深度 observable 对象observable.deep函数创建深劫持 observable 对象observable.shallow函数创建浅劫持 observable 对象observable.computed函数创建缓存计算器observable.box函数创建带get/set方法的 observable 对象observable.ref函数创建引用级 observable 对象define函数定义 observable 领域模型可组合observable与其静态属性函数完成领域模型定义model函数定义自动 observable 领域模型它会将 getter/setter 包装为 computed 计算属性、将函数包装为 action、将普通数据属性用 observable 深度包装。由于强依赖 ES Proxyformily/reactive不支持 IE 浏览器这一点在使用前需要评估目标环境的兼容性。Reaction订阅者Reaction 相当于可订阅对象的订阅者。它接收一个 tracker 函数执行时若函数内部对 observable 对象的某个属性进行了读操作依赖收集当前 reaction 就会与该属性绑定依赖追踪直到该属性在其他地方发生写操作便会触发 tracker 函数重复执行。从订阅到派发订阅是一个封闭的循环状态机每次 tracker 函数执行时都会重新收集依赖依赖变化时又会重新触发 tracker 执行。因此一旦不再需要某个订阅必须手动调用dispose释放否则会造成内存泄漏。创建 reaction 主要使用以下 APIautorun创建一个自动执行的响应器reaction创建一个可以实现脏检查的响应器Tracker创建一个依赖追踪器需要用户手动执行追踪。Computed可缓存的 Reaction一句话概括computed 是一个可以缓存计算结果的 Reaction。它的缓存策略是只要 computed 函数内部依赖的 observable 数据发生变化函数才会重新执行计算否则永远读取缓存结果。因此 computed 函数必须是纯函数——内部依赖的数据要么是 observable 数据要么是外部常量数据。如果依赖了外部变量非 observable外部变量变化时 computed 不会重新计算。Batch合并更新由于 formily/reactive 基于 Proxy 劫持实现任何一个原子操作都会触发 Reaction 执行这在多属性连续赋值时会造成计算资源浪费。例如import { observable, autorun } from formily/reactive const obs observable({}) const handler () { obs.aa 123 obs.bb 321 } autorun(() { console.log(obs.aa, obs.bb) }) handler() // 会打印 3 次autorun 默认执行一次obs.aa 赋值一次obs.bb 赋值一次原子操作越多执行次数越多。推荐使用batch模式将更新合并import { observable, autorun, batch } from formily/reactive const obs observable({}) const handler () { obs.aa 123 obs.bb 321 } autorun(() { console.log(obs.aa, obs.bb) }) batch(() { handler() })也可以使用action进行高阶包装效果等价import { observable, autorun, action } from formily/reactive const obs observable({}) const handler action.bound(() { obs.aa 123 obs.bb 321 }) autorun(() { console.log(obs.aa, obs.bb) }) handler()两种方式下最终执行次数都是 2 次且无论 handler 内部操作再多也始终保持 2 次。batch还支持scope原地局部批量、bound高阶绑定和endpoint注册批量执行结束回调详见 batch 文档 与 action 文档。创建 Observable 的六种姿势observable / observable.deep深劫持创建深度劫持响应式对象嵌套属性也会被递归代理import { observable, autorun } from formily/reactive const obs observable({ aa: { bb: 123, }, }) autorun(() { console.log(obs.aa.bb) }) obs.aa.bb 321 // 触发响应observable.shallow浅劫持只对目标对象的第一级属性操作响应嵌套对象不会被代理import { observable, autorun } from formily/reactive const obs observable.shallow({ aa: { bb: 111, }, }) autorun(() { console.log(obs.aa.bb) }) obs.aa.bb 222 // 不会响应 obs.aa { bb: 333 } // 可以响应一级属性替换observable.computed缓存计算器import { observable, autorun } from formily/reactive const obs observable({ aa: 11, bb: 22, }) const computed observable.computed(() obs.aa obs.bb) autorun(() { console.log(computed.value) }) obs.aa 33computed内部依赖obs.aa、obs.bb只有它们变化时才重新计算否则读取缓存。observable.ref引用劫持ref将目标值包装为{ value: T }容器替换整个 value 才触发响应import { observable, autorun } from formily/reactive const ref observable.ref(1) autorun(() { console.log(ref.value) }) ref.value 2observable.boxget/set 容器与 ref 相似只是读写通过get()/set()方法import { observable, autorun } from formily/reactive const box observable.box(1) autorun(() { console.log(box.get()) }) box.set(2)订阅 Observable 的 APIautorun自动执行接收 tracker 函数内部消费的 observable 数据变化时重复执行返回dispose函数用于取消订阅import { observable, autorun } from formily/reactive const obs observable({}) const dispose autorun(() { console.log(obs.aa) }) obs.aa 123 dispose()autorun还提供了两个进阶 APIautorun.memo(callback, dependencies [])在 autorun 中创建持久引用数据只受依赖变化影响而重新执行 memo 内部函数。依赖默认为[]即不传依赖代表永远不执行第二次。注意不要在 If/For 语句中使用因为它内部依赖执行顺序来绑定当前 autorun。autorun.effect(callback, dependencies [{}])响应 autorun 第一次执行后的下一个微任务时机以及 autorun 的dispose。依赖默认为[{}]不传依赖代表持续执行内部脏检查是浅比较。同样不要在 If/For 语句中使用。具体用例可参考 autorun 文档。reaction脏检查响应器与 autorun 不同reaction(tracker, subscriber, options)要求tracker 的返回值发生变化时才执行 subscriber 回调适合只看结果的场景import { observable, reaction, batch } from formily/reactive const obs observable({ aa: 1, bb: 2, }) const dispose reaction(() { return obs.aa obs.bb }, console.log) batch(() { // 不会触发因为 obs.aa obs.bb 的值没变 obs.aa 2 obs.bb 1 }) obs.aa 4 // 值变为 6触发回调 dispose()reaction的完整选项包括name?: string命名该响应器便于调试equals?: (oldValue, newValue) boolean自定义脏检查函数fireImmediately?: boolean是否第一次默认触发绕过脏检查。详见 reaction 文档。Tracker手动依赖追踪器Tracker需要用户手动执行追踪适合在渲染函数等场景中精确控制依赖收集时机配合框架的 observer 实现按需渲染。面向 DDD 的领域模型define 与 modeldefine手动定义领域模型define(target, annotations)允许为类实例指定每个属性的响应式行为也支持将某个方法标记为 batch 模式。目前支持的全部 annotation 有observable/observable.deep定义深度劫持响应式属性observable.box定义 get/set 容器observable.computed定义计算属性observable.ref定义引用劫持响应式属性observable.shallow定义浅劫持响应式属性action/batch定义批处理方法。import { define, observable, action, autorun } from formily/reactive class DomainModel { deep { aa: 1 } shallow {} box 0 ref constructor() { define(this, { deep: observable, shallow: observable.shallow, box: observable.box, ref: observable.ref, computed: observable.computed, action, }) } get computed() { return this.deep.aa this.box.get() } action(aa, box) { this.deep.aa aa this.box.set(box) } } const model new DomainModel() autorun(() { console.log(model.computed) }) model.action(1, 2) model.action(1, 2) // 重复调用不会重复响应 model.action(3, 4)model自动领域模型model(target)对普通对象做自动声明无需手动书写 annotationsgetter/setter 属性自动声明为 computed函数自动声明为 action普通属性自动声明为 observable深劫持。import { model, autorun } from formily/reactive const obs model({ aa: 1, bb: 2, get cc() { return this.aa this.bb }, update(aa, bb) { this.aa aa this.bb bb }, }) autorun(() { console.log(obs.cc) }) obs.aa 3 obs.update(4, 6)两种方式都能将业务模型与响应式能力解耦让领域逻辑业务方法天然获得批量更新与缓存计算能力这正是文档所称面向 DDD 的响应式状态管理方案的落地形态。为什么是自研而非 Mobxformily/reactive 的核心思想参考自 Mobx但重新造轮子主要有 4 点原因mobx 不支持 action 内部进行依赖收集mobx 的 observable 函数不支持过滤 react node、moment、immutable 之类的特殊对象mobx 的 observable 函数会自动将函数变成 actionmobx-react-lite 的 observer 不支持 React 并发渲染。自研的好处同样明确把控性更强可以为 formily 场景做更深的优化定制不用考虑 Mobx 的历史包袱代码更干净若 Mobx 版本发生 Break Change 或存在安全漏洞对 Formily 无影响。从仓库结构看formily/reactive 提供了完整的单元测试覆盖见 packages/reactive/src/tests并配套action、autorun、batch、tracker、observe、untracked、model、define等完整 API 家族以及 react、vue 两套框架桥接文档支撑 Formily 在 React / React Native / Vue 2 / Vue 3 全链路落地。最佳实践官方文档总结了使用 formily/reactive 时最值得注意的几点尽量少用observable/observable.deep做深度包装非不得已时优先使用observable.ref/observable.shallow性能更好领域模型中多用 computed 计算属性它可以智能缓存计算结果尽量多用 batch 模式虽然批量操作不是必须的但能显著减少 Reaction 执行次数使用autorun/reaction时一定要调用其返回的dispose函数释放订阅否则会内存泄漏。小结通过本文你可以看到formily/reactive 以 ES Proxy 为基石构建了Observable 数据劫持 → Reaction 依赖追踪 → Computed 缓存计算 → Batch 批量更新的完整响应式闭环。安装后在几行代码内即可体验响应式能力进阶用法中define/model提供了面向 DDD 的领域模型建模方式reaction的脏检查与autorun的自动执行分别覆盖了关注结果与关注过程两类订阅诉求。无论是直接使用还是深入 Formily 表单内核core、react、vue 等包时理解其运行原理掌握这套响应式模型都至关重要。赞分享前端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 核心架构解析基于 formily/reactive 响应式领域模型的设计原理 导读 本文围绕 packages/core/docs/guid前端UI组件Formily 核心架构深度解析基于 formily/reactive 领域模型的响应式表单内核Formily 核心架构深度解析基于 formily/reactive 领域模型的响应式表单内核 导读 本文围绕 packages/core/docs/gu前端UI组件formily/reactive 响应式引擎核心 APIautorun 与 autorun.memo / autorun.effect 全面指南formily/reactive 响应式引擎核心 APIautorun 与 autorun.memo / autorun.effect 全面指南 导读 au前端UI组件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表