如何让Mobx计算属性同时实现Lazy与Cached特性?
实现具备懒加载缓存特性的MobX自动观察工具函数
问题描述
现有如下MobX类示例:
class OrderLine { price = 0 amount = 1 constructor(price) { makeAutoObservable(this) this.price = price } get total() { console.log("Computing...") return this.price * this.amount } }
当前total计算属性仅在被autorun调用并访问时才会被缓存。但实际需求希望计算属性同时具备**懒加载(Lazy)和缓存(Cached)**特性:仅在第一次实际读取(引用)时执行计算并缓存,同时避免autorun提前触发计算。
需要实现一个类似makeAutoObservableLazyCached的工具函数,让计算属性无需提前读取值即可完成缓存,同时支持选择性装饰特定计算属性。初始函数框架如下:
const makeAutoObservableLazyCached = (target) => { makeAutoObservable(target) // 需要补充的逻辑 }
解决方案
方案1:全局处理所有计算属性
核心思路是遍历对象属性,识别计算属性并重写其getter,手动管理缓存状态和依赖跟踪,跳过MobX默认的提前计算逻辑:
import { makeAutoObservable, observable, computed } from "mobx" const makeAutoObservableLazyCached = (target) => { makeAutoObservable(target) Object.keys(target).forEach(key => { const descriptor = Object.getOwnPropertyDescriptor(target, key) // 判断是否为计算属性 if (descriptor && typeof descriptor.get === 'function' && !descriptor.writable) { const originalGetter = descriptor.get let cache = { value: undefined, computed: false } const dependencies = observable.box(true) Object.defineProperty(target, key, { get() { // 未计算或依赖变化时重新计算 if (!cache.computed || dependencies.get()) { cache.value = originalGetter.call(target) cache.computed = true dependencies.set(false) } // 跟踪依赖变化,标记缓存失效 computed(() => { originalGetter.call(target) dependencies.set(true) }) return cache.value }, enumerable: descriptor.enumerable, configurable: descriptor.configurable }) } }) return target }
使用时直接替换原makeAutoObservable即可,所有计算属性都会自动启用懒加载缓存。
方案2:选择性装饰特定计算属性
如果只需对部分计算属性生效,可以自定义装饰器实现精准控制:
import { makeAutoObservable, observable, computed } from "mobx" // 自定义懒加载缓存装饰器 const lazyCached = (target, key, descriptor) => { const originalGetter = descriptor.get let cache = { value: undefined, computed: false } const dependencies = observable.box(true) descriptor.get = function() { if (!cache.computed || dependencies.get()) { cache.value = originalGetter.call(this) cache.computed = true dependencies.set(false) } computed(() => { originalGetter.call(this) dependencies.set(true) }) return cache.value } return descriptor } // 保持原makeAutoObservable逻辑的包装函数 const makeAutoObservableLazyCached = (target) => { return makeAutoObservable(target) }
使用时给需要的计算属性添加装饰器即可:
class OrderLine { price = 0 amount = 1 constructor(price) { makeAutoObservableLazyCached(this) this.price = price } @lazyCached get total() { console.log("Computing...") return this.price * this.amount } // 其他计算属性保持默认行为 get tax() { return this.total * 0.1 } }
原理说明
- 手动维护缓存状态:通过
cache变量记录计算结果和计算状态,仅在第一次读取或依赖变化后触发重新计算 - 依赖跟踪:用
computed包裹原始getter,当依赖项变更时标记缓存失效,保证下次读取能获取最新值 - 避免提前计算:跳过MobX默认的计算属性自动触发逻辑,仅在实际读取时执行计算
内容的提问来源于stack exchange,提问作者404
相关产品推荐
相关产品推荐

