ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

mobx-state-tree 补丁录制器 IPatchRecorder 详解:实现撤销/重做与补丁回放的完整指南

mobx-state-tree 补丁录制器 IPatchRecorder 详解:实现撤销/重做与补丁回放的完整指南 状态管理前端【免费下载链接】mobx-state-treeFull-featured reactive state management without the boilerplate项目地址https://gitcode.com/gh_mirrors/mo/mobx-state-tree点击查看免费下载导读IPatchRecorder是 mobx-state-treeMST中与补丁patch录制功能直接对应的核心接口由recordPatches函数创建并返回用于把一棵状态树在一段时间内发生的所有变更以 JSON-Patch 序列的形式记录下来并支持撤销undo、重放replay、暂停与恢复录制。本文将基于 IPatchRecorder 接口文档结合recordPatches的源码实现与仓库测试用例讲解该接口的全部属性与方法语义、底层调用链以及如何用它实现撤销/重做、变更审计和状态回放等实战能力。IPatchRecorder一次录制的完整生命周期接口定义与产生方式IPatchRecorder定义在 src/core/mst-operations.ts:135其完整类型签名如下export interface IPatchRecorder { patches: ReadonlyArrayIJsonPatch inversePatches: ReadonlyArrayIJsonPatch reversedInversePatches: ReadonlyArrayIJsonPatch readonly recording: boolean stop(): void resume(): void replay(target?: IAnyStateTreeNode): void undo(target?: IAnyStateTreeNode): void }该接口对象由recordPatches函数创建。正如源码注释所述src/core/mst-operations.ts:146它是围绕onPatch与applyPatch的小型抽象recordPatches向状态树挂载一个补丁监听器把所有补丁及对应的逆补丁累积到内部数组中并对外暴露上述属性与方法。从类型结构看接口拥有4 个属性patches、inversePatches、reversedInversePatches、recording和4 个方法stop、resume、replay、undo。属性语义属性类型含义patchesReadonlyArrayIJsonPatch录制期间累积的全部正向补丁按发生顺序inversePatchesReadonlyArrayIJsonPatch每个正向补丁对应的逆补丁按原顺序排列reversedInversePatchesReadonlyArrayIJsonPatch逆补丁数组的反转副本可直接顺序应用以回滚变更recordingboolean当前是否处于录制状态只读 getter其中补丁类型 IJsonPatch 遵循 JSON-Patch 规范RFC 6902定义于 src/core/json-patch.ts:7export interface IJsonPatch { readonly op: replace | add | remove readonly path: string readonly value?: any }op只能是replace | add | remove三种操作path是形如/text、/children/0/text的 JSON 路径value仅在add与replace时携带。仓库还在 src/core/json-patch.ts:13 定义了扩展类型 IReversibleJsonPatch额外带oldValue字段用于生成逆补丁内部通过splitPatch/invertPatch实现见 src/core/json-patch.ts:21。方法语义与返回约定方法签名行为stop()(): void停止录制移除补丁监听器resume()(): void恢复录制重新挂载监听器replay(target?)(target?: IAnyStateTreeNode): void将已录制补丁应用到目标节点省略target时应用到原主体undo(target?)(target?: IAnyStateTreeNode): void逆序应用逆补丁回滚变更省略target时回滚原主体且会停止录制需要注意undo()在撤销的同时会使录制器停止这是源码中的既定行为src/core/mst-operations.ts:251 的注释明确说明 stops the recorder if not already stopped。源码实现recordPatches 的底层机制调用签名export function recordPatches( subject: IAnyStateTreeNode, filter?: ( patch: IJsonPatch, inversePatch: IJsonPatch, actionContext: IActionContext | undefined ) boolean ): IPatchRecorderrecordPatches在 src/core/mst-operations.ts:177 导出接受两个参数subject要录制补丁的状态树节点filter?可选过滤函数返回false的补丁将被跳过不记录。该函数能同时拿到正向补丁、逆补丁以及当前动作上下文actionContext由getRunningActionContext()提供可用于按路径、操作类型或动作来源做精细化筛选。内部工作流从实现src/core/mst-operations.ts:188-257可以梳理出完整调用链初始化内部维护data.patches与data.inversePatches两个可变数组以及一个按需生成不可变快照的publicData缓存对象挂载监听器resume()内部调用onPatch(subject, (patch, inversePatch) {...})每次树发生变更时onPatch回调都会立即收到patch与inversePatch对——注意onPatch的语义是立即触发、不等事务结束见 docs/API/index.md:3853 的说明过滤与累积回调内先执行filter若提供通过后把patch压入data.patches、把inversePatch压入data.inversePatches同时将publicData缓存标记为脏置undefined按需缓存patches、inversePatches、reversedInversePatches都是 getter仅在首次访问时通过slice()/slice().reverse()生成不可变副本避免外部修改污染内部数据返回并立即开始录制函数末尾调用recorder.resume()后才返回所以拿到 recorder 的那一刻录制已经生效。recording属性是一个 getter其值等价于内部监听器 disposer 是否存在!!disposer——stop()调用 disposer 并置空resume()在已有监听器时直接返回不再重复挂载。replay 与 undo 的底层实现replay(target?)applyPatch(target || subject, data.patches)把累积的正向补丁原样应用到目标节点。若传入其他节点可实现把 A 上的变更序列同步到 Bundo(target?)applyPatch(target || subject, data.inversePatches.slice().reverse())即把逆补丁反转顺序后依次应用。这正是 docs/concepts/patches.md 中补丁可被逆序应用从而支撑撤销/重做等强大模式这一理念的直接落地。而applyPatch本身src/core/mst-operations.ts:124会先做参数校验assertIsStateTreeNode、校验对象/数组再调用底层节点的applyPatches(asArray(patch))批量应用。实战用法录制、重放与撤销最小可用示例import { types, getSnapshot, recordPatches } from mobx-state-tree const Todo types.model(Todo, { title: types.string, done: false }) const todo Todo.create({ title: Read docs, done: false }) // 开始录制调用后立即生效 const recorder recordPatches(todo) // 通过 action 修改状态保护模式下外部直接赋值会抛错建议在 action 内变更 todo.toggleDone() // 假设模型中有 toggleDone action // 查看录制结果 console.log(recorder.patches) // [{ op: replace, path: /done, value: true }] console.log(recorder.recording) // true // 回滚到录制前的状态同时停止录制 recorder.undo() console.log(getSnapshot(todo).done) // false基于 filter 只录制关心的补丁当状态树很大、只想关注某个子树或某类操作时可传入过滤函数const recorder recordPatches(todoStore, (patch, inversePatch, actionContext) { // 只录制 /todos 路径下的变更 return patch.path.startsWith(/todos/) })该 filter 的第三个参数actionContext来自getRunningActionContext()因此你还可以按发起变更的 action 名称做筛选实现按业务动作分类的审计日志。把变更同步到另一棵树replay 的典型场景const source TodoStore.create({ todos: [] }) const target TodoStore.create({ todos: [] }) const recorder recordPatches(source) // ...执行一系列操作... recorder.stop() // 先停止录制保证补丁序列完整 recorder.replay(target) // 将 source 上发生的变更原样同步到 target这可用于状态同步、演示回放或跨实例复制等场景。测试验证接口行为与补丁语义的仓库证据仓库在tests/core/recordPatches.test.ts 中对recordPatches及IPatchRecorder的完整行为做了系统性验证。测试的核心辅助函数testPatches呈现了标准的录制 → 停止 → 重放 → 撤销闭环第 15-35 行创建实例并调用recordPatches(instance)执行变更然后recorder.stop()断言recorder.patches与recorder.inversePatches与预期一致新建克隆recorder.replay(clone)断言克隆快照与变更后的原实例快照相等调用recorder.undo()断言原实例恢复到录制前的基线快照。测试覆盖了四类结构上的补丁语义简单属性替换第 42-64 行修改text字段产生{ op: replace, path: /text, value: test }逆补丁把value换回原值Hi数组的深层补丁第 66-147 行对children数组的更新、调和、新增、splice 删除依次产生replace、add、remove补丁且逆补丁顺序完全镜像Map 的深层补丁第 149-245 行验证children.get(...).text更新、put调和、set新对象、delete删除在 map 上的补丁表达嵌套对象属性第 247-353 行对可选子对象child的更新、替换、置undefined的补丁行为。这些用例同时验证了undo()在不传 target 时作用于原主体以及replay()可作用于全新克隆节点的能力是本文档所述接口行为最直接的实现级佐证。与其他补丁 API 的关系IPatchRecorder处于补丁体系的中层前后各有协作方底层是 onPatch 监听器与 applyPatch 应用器均定义于 src/core/mst-operations.tsrecordPatches正是二者的组合封装补丁数据模型是 IJsonPatchop/path/value内部逆补丁生成依赖 IReversibleJsonPatch 的oldValue字段在它之上你可以自由构建撤销/重做栈、补丁审计日志或变更即同步的协作功能。与快照监听onSnapshot相比补丁是细粒度的变更流快照只在事务结束时整体通知一次而补丁立即、逐个、可逆地反映每一次变更docs/concepts/patches.md 明确列出这些差异。IPatchRecorder的价值正在于把这条流变成可暂停、可回溯、可重演的录制让撤销/重做从手写逆操作中解放出来。注意事项与最佳实践录制从调用即刻开始recordPatches返回前会自动resume()若需要排除某些前置变更可在挂载前先准备好状态undo()会停止录制撤销后如需继续录制请显式调用resume()patches等数组是只读不可变副本内部通过slice()按需生成外部修改不会影响录制器内部数据也不会被后续补丁污染replay/undo的 target 参数省略时作用于原主体传入其他节点可实现跨树同步但请确保目标节点的类型结构与补丁路径匹配filter 是录制期的闸门传入后即时生效可用于排除噪声补丁但注意被过滤的补丁也不会进入inversePatches撤销范围会随之收窄保护模式下的变更状态树默认受保护所有修改应通过 action 进行录制本身不改变这一约束非法直接赋值仍会抛错。总结IPatchRecorder是 mobx-state-tree 补丁体系中最实用的录制器抽象patches/inversePatches/reversedInversePatches三个数组分别对应正向、逆向、逆序逆补丁三种视角recording反映录制状态stop/resume控制生命周期replay/undo分别完成重放与回滚。其实现src/core/mst-operations.ts:135-258把onPatch与applyPatch组合成一个约 70 行的自洽模块并配合tests/core/recordPatches.test.ts 中的结构级测试覆盖为撤销/重做、变更审计、跨实例状态同步等需求提供了开箱即用且经过验证的解决方案。赞分享状态管理前端【免费下载链接】mobx-state-treeFull-featured reactive state management without the boilerplate项目地址https://gitcode.com/gh_mirrors/mo/mobx-state-tree点击查看免费下载相关推荐Apache Arrow GLib 示例代码实战指南用 C、Lua、Vala 读写 Arrow 文件与流格式Apache Arrow GLib 示例代码实战指南用 C、Lua、Vala 读写 Arrow 文件与流格式 Apache Arrow 官方仓库在 c_gli状态管理前端终极防撤回神器RevokeMsgPatcher完整使用指南让微信/QQ消息撤回失效终极防撤回神器RevokeMsgPatcher完整使用指南让微信/QQ消息撤回失效 RevokeMsgPatcher是一款强大的PC版微信/QQ/TIM防撤桌面应用即时通讯终极微信QQ防撤回神器RevokeMsgPatcher完整使用指南消息撤回也能看终极微信QQ防撤回神器RevokeMsgPatcher完整使用指南消息撤回也能看 RevokeMsgPatcher是一款强大的PC版微信/QQ/TIM防撤桌面应用即时通讯上一篇基于 .NET Aspire 编排 Semantic Kernel Process Framework事件驱动业务流程实战指南下一篇Tasmota 内嵌 Berry 脚本引擎深度剖析寄存器虚拟机、一次编译与标记-清除 GC 的嵌入式设计创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表