Mobx
May 24, 2022
build your own mobx
以下是一段 mobx 的简单使用例子
const store = observable({ a: 1, b: { c: 1 } })
autorun(() => {
if (store.a === 2) {
console.log(store.b.c)
}
})
store.a = 2 // 1
store.b.c = 5 // 5
store.b.c = 6 // 6
上面这个例子中,修改传入 observable 的对象的某个属性,会自动触发 autorun 中使用了该属性的方法
当属性被使用时,我们需要搜集使用该属性的方法;当该属性被赋值时,我们需要调用使用了该属性的方法,因此首先我们需要一个收集方法与调用方法的工具
class EventEmitter {
list = new WeakMap()
// 当属性被使用时,以该属性值 key,将使用了该属性的 fn 存放入数组中
on(obj, fn) {
let targetEvents = this.list.get(obj)
if (!targetEvents) {
targetEvents = []
this.list.set(obj, targetEvents)
}
if (!targetEvents.includes(fn)) {
targetEvents.push(fn)
}
}
// 当该属性被赋值时,依次调用 key 为该值的数组存放的方法
emit(obj, ...args) {
const targetEvents = this.list.get(obj)
if (targetEvents) {
const fns = targetEvents
if (fns && fns.length > 0) {
fns.forEach((fn) => {
fn && fn(...args)
})
}
}
}
}
接下来,我们就需要监听 observable 传入的对象的使用与赋值,并且还要与 autorun 的方法相关联
const em = new EventEmitter()
let currentFn
const autorun = (fn) => {
const warpFn = () => {
currentFn = warpFn
fn() // 依赖收集
currentFn = null
}
warpFn()
}
const observable = (obj) => {
return new Proxy(obj, {
get: (target, key) => {
if (typeof target[key] === 'object') {
return observable(target[key])
} else {
// 如果有当前执行的函数,则进入依赖收集
if (currentFn) {
em.on(target, currentFn)
}
return target[key]
}
},
set: (target, key, value) => {
if (target[key] !== value) {
target[key] = value
em.emit(target, key)
}
return true
}
})
}