- 状态管理
- 前端
【免费下载链接】mobx-state-tree
Full-featured reactive state management without the boilerplate
导读
IActionTrackingMiddleware2Hooks是 mobx-state-tree 提供的行动追踪中间件钩子接口,配合createActionTrackingMiddleware2工厂函数使用,可以轻松实现对同步与异步 action(含 flow)从开始到结束的完整生命周期追踪。本文将围绕该接口的三个成员filter、onStart、onFinish,结合 createActionTrackingMiddleware2.ts 源码与 actionTrackingMiddleware2.test.ts 测试,讲解钩子的调用顺序、env/parentCall上下文传播机制,以及如何基于它实现日志、加载态管理、鉴权拦截等实战能力。
接口定义:三个钩子的完整契约
IActionTrackingMiddleware2Hooks定义于 src/middlewares/createActionTrackingMiddleware2.ts:8,是一个泛型接口,接受类型参数TEnv(用于描述通过env在嵌套 action 之间传递的上下文对象类型),默认在createActionTrackingMiddleware2<TEnv = any>中使用。
export interface IActionTrackingMiddleware2Hooks<TEnv> { filter?: (call: IActionTrackingMiddleware2Call<TEnv>) => boolean onStart: (call: IActionTrackingMiddleware2Call<TEnv>) => void onFinish: (call: IActionTrackingMiddleware2Call<TEnv>, error?: any) => void }三个成员的作用与调用时机如下表:
| 成员 | 是否可选 | 签名 | 调用时机 |
|---|---|---|---|
filter | 可选(?) | (call) => boolean | 每个 action 开始前,决定该 action 是否被追踪 |
onStart | 必填 | (call) => void | 通过filter的 action 开始执行时 |
onFinish | 必填 | (call, error?) => void | action 结束(成功或失败)时,error携带异常信息 |
其中call的类型为IActionTrackingMiddleware2Call<TEnv>,它继承IActionContext并额外扩展了两个字段:
export interface IActionTrackingMiddleware2Call<TEnv> extends Readonly<IActionContext> { env: TEnv | undefined readonly parentCall?: IActionTrackingMiddleware2Call<TEnv> }env:当前 action 携带的环境对象(自定义数据,可在父子 action 间共享,初始为undefined);parentCall:当前 action 的父 action 调用上下文(最外层 action 为undefined)。
而IActionContext(见 actionContext.ts:4)提供了六个只读基础字段:name(action 名)、id(事件唯一 id)、parentActionEvent(父 action 事件)、context(被调用节点)、tree(根节点)、args(action 参数数组)。
钩子调用流程:filter → onStart → (嵌套 action)→ onFinish
createActionTrackingMiddleware2的 JSDoc(createActionTrackingMiddleware2.ts:49)给出了明确的调用流程约定:
- 对每个 action:若
filter通过 → 触发onStart→ 递归执行内部嵌套 action → 触发onFinish; - 无论 action 是同步还是异步(flow),整体流程保持一致。
以「actiona内部依次调用b1、`b2」为例,事件序列为:
filter(a) onStart(a) filter(b1) onStart(b1) onFinish(b1) filter(b2) onStart(b2) onFinish(b2) onFinish(a)测试 actionTrackingMiddleware2.test.ts:387 的complete in the expected recursive order用例完整验证了这一顺序:parentAction的onStart先触发,随后childAction1与childAction2各自完成filter → onStart → onFinish,最后parentAction的onFinish兜底收尾。这种「父开始→子进出→父结束」的嵌套结构,使中间件天然支持对 action 调用树的整体观测。
filter:精确挑选被追踪的 action
filter返回true才追踪,返回false则跳过。源码中通过passesFilter决定是否传入钩子(createActionTrackingMiddleware2.ts:94):
const passesFilter = !middlewareHooks.filter || middlewareHooks.filter(newCall) const hooks = passesFilter ? middlewareHooks : undefined省略filter时默认全部追踪。测试 actionTrackingMiddleware2.test.ts:310 与 actionTrackingMiddleware2.test.ts:343 分别验证了「通过 filter 的trackThisOne会触发 onStart/onFinish」与「未通过 filter 的doNotTrackThisOne完全不触发任何钩子」。
典型用法是按 action 名或参数定向追踪,例如只记录setX:
const mware = createActionTrackingMiddleware2({ filter(call) { return call.name === "setX" && call.args.length > 0 }, onStart(call) { /* ... */ }, onFinish(call, error) { /* ... */ } })onStart 与 onFinish:成对的生命周期钩子
onStart在 action 真正执行前触发,常用于打点、设置env或开启 loading 状态;onFinish在 action 结束后触发,其第二个参数error?在 action 抛出异常时携带错误对象。源码中RunningAction类负责这一成对逻辑(createActionTrackingMiddleware2.ts:14):构造函数立即调用onStart,finish(error?)方法保证只调用一次onFinish(通过running标志防重入),并透传错误。
class RunningAction { constructor( public readonly hooks: IActionTrackingMiddleware2Hooks<any> | undefined, readonly call: IActionTrackingMiddleware2Call<any> ) { if (hooks) { hooks.onStart(call) } } finish(error?: any) { if (this.running) { this.running = false if (this.hooks) { this.hooks.onFinish(this.call, error) } } } // ... }成功与失败的对称性在测试中得到印证:同步失败场景(setX内throw "error")下,onFinish收到的error为真,且父子 action 的onFinish都会收到该错误(actionTrackingMiddleware2.test.ts:96)。异常还会沿调用链向上传播——源码中同步 action 抛错时先runningAction.finish(e)再重新throw e(createActionTrackingMiddleware2.ts:102),保证错误不会被中间件吞掉。
注册中间件:addMiddleware 接入模型
IActionTrackingMiddleware2Hooks需要交给createActionTrackingMiddleware2包装成IMiddlewareHandler,再通过addMiddleware(target, handler, includeHooks?)挂载到目标节点上(src/core/action.ts:175)。典型接入方式:
import { addMiddleware, createActionTrackingMiddleware2, types } from "mobx-state-tree" const tracker = createActionTrackingMiddleware2<any>({ filter(call) { return true }, onStart(call) { console.log(`开始执行: ${call.name}`, call.args) call.env = { startedAt: Date.now() } // env 会向下传递给嵌套 action }, onFinish(call, error) { console.log(`结束执行: ${call.name}`, error ? `错误: ${error}` : "成功") } }) const M = types.model({ x: 0 }).actions(self => ({ inc() { self.x++ } })) const store = M.create() addMiddleware(store, tracker, false) // 第二个参数为中间件,第三个参数控制是否包含 hook 事件 store.inc()env 与 parentCall:父子 action 的上下文桥接
这是IActionTrackingMiddleware2Hooks相比第一代createActionTrackingMiddleware(见 create-action-tracking-middleware.ts,需onResume/onSuspend/onSuccess/onFail五个钩子且以rootId全局查找)更易用的关键设计。中间件在处理"action"事件时构造newCall(createActionTrackingMiddleware2.ts:86):
const newCall: IActionTrackingMiddleware2Call<TEnv> = { ...call, // 浅拷贝父 action 的 env env: parentRunningAction && parentRunningAction.call.env, parentCall: parentRunningAction && parentRunningAction.call }env是父 action 的浅拷贝,子 action 天然继承父级写入的env,在onStart中写入call.env = {...}后,所有后代 action 都能读取到;parentCall直接指向父 action 的call对象,方便通过call.parentCall.name追溯调用来源。
测试 actionTrackingMiddleware2.test.ts:169(对应 issue #1250)演示了filter输出call.parentCall引用:flow 异步执行期间、其他同步 action 穿插执行时,parentCall正确指向发起 flow 的父 action。测试 actionTrackingMiddleware2.test.ts:11 则通过call.env = call.id验证了env在嵌套 action 间的正确复制。
异步支持:基于 RunningAction 的 flow 生命周期追踪
createActionTrackingMiddleware2的名字中「2」即强调对异步流程的增强。与同步 action 的「执行完立即 finish」不同,异步 flow 可能跨多个事件循环周期。源码通过RunningAction上的flowsPending计数器配合四种 flow 事件实现追踪(createActionTrackingMiddleware2.ts:114):
flow_spawn:flowsPending++,标记有异步流程派生;flow_resume/flow_resume_error:直接放行(流程恢复执行中);flow_throw:减计数,待 pending 清零后finish(error);flow_return:减计数,待 pending 清零后finish()。
同步 action 执行完后(createActionTrackingMiddleware2.ts:108)若hasFlowsPending为真,则不立即finish,而是等最后一个flow_return/flow_throw到来时统一收尾。测试 actionTrackingMiddleware2.test.ts:113(flow action用例)验证了嵌套 flow 的成功与失败两种路径下,onStart/onFinish仍按「父开始→子进出→父结束」顺序输出。
值得注意的是,mobx-state-tree 官方 API 文档(docs/API/index.md:2231)明确建议:优先迁移到createActionTrackingMiddleware2,因为它更易用。而 docs/overview/utilties.md:20 也将createActionTrackingMiddleware2列为「让追踪异步 action 的中间件编写不再繁琐」的推荐工具。
实战组合:用三个钩子实现 action 日志与加载态
将上述机制组合起来,即可实现一个带嵌套缩进、耗时统计和错误捕获的通用日志中间件:
import { addMiddleware, createActionTrackingMiddleware2 } from "mobx-state-tree" type LogEnv = { depth: number; startedAt: number } const logger = createActionTrackingMiddleware2<LogEnv>({ filter(call) { return true // 追踪所有 action }, onStart(call) { const depth = (call.parentCall && call.parentCall.env ? call.parentCall.env.depth : 0) + 1 call.env = { depth, startedAt: Date.now() } console.log(`${" ".repeat(depth - 1)}→ ${call.name}(${JSON.stringify(call.args)})`) }, onFinish(call, error) { const indent = " ".repeat(call.env!.depth - 1) if (error) { console.error(`${indent}✗ ${call.name} 失败:`, error) } else { console.log(`${indent}← ${call.name} 完成 (${Date.now() - call.env!.startedAt}ms)`) } } }) addMiddleware(store, logger, false)由于env会从父 action 浅拷贝给子 action(createActionTrackingMiddleware2.ts:90),子 action 总能通过call.parentCall.env读取父级深度,从而实现调用树缩进;onFinish的error参数则让异常路径与成功路径区分清晰。
关联类型与文档索引
IActionTrackingMiddleware2Hooks位于 mobx-state-tree 公开 API 的接口家族中,与本接口直接相关的类型文档包括:
- IActionTrackingMiddleware2Call:钩子回调的参数类型,含
env与parentCall; - IActionContext:
name/id/args/context/tree/parentActionEvent基础字段来源; - IMiddlewareEvent:底层中间件事件类型,含
rootId/allParentIds/type等扩展字段; - createActionTrackingMiddleware2:接收
IActionTrackingMiddleware2Hooks并返回IMiddlewareHandler的工厂函数; - 第一代 createActionTrackingMiddleware 与其
IActionTrackingMiddlewareHooks(见 create-action-tracking-middleware.ts:5),便于对比迁移。
需要强调的接口约束:onStart与onFinish为必填项,只有filter可省略;onFinish的error参数在 action 正常完成时为undefined,异常终止时携带被抛出的错误值。只要把握「filter 决定是否追踪、onStart 标记开始、onFinish 统一收尾(含异常)」这一契约,即可用极少的样板代码实现健壮的 action 生命周期观测。
- 状态管理
- 前端
【免费下载链接】mobx-state-tree
Full-featured reactive state management without the boilerplate
相关推荐
Presto Release 0.226 技术解读:地理空间修复、分组执行与 Hive 写入优化
Presto Release 0.226 技术解读:地理空间修复、分组执行与 Hive 写入优化 导读 本文以 Presto Release 0.226 官方发
状态管理前端context-engineering-intro 模板生成器:用 `/execute-template-prp` 执行模板生成 PRP,产出领域化上下文工程模板包
context engineering intro 模板生成器:用 /execute template prp 执行模板生成 PRP,产出领域化上下文工程模板包
状态管理前端MobX-State-Tree 全面指南
MobX State Tree 全面指南 项目介绍 MobX State Tree(简称MST)是一个基于MobX构建的状态容器系统。它不仅仅是一个状态库,更是
状态管理前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考