一直以来对vue的依赖自动追踪的原理很感兴趣,像魔法一样。对于交给vue的对象返回后,在哪里使用了这个返回的对象vue会自动追踪,等这个对象改变时vue会自动通知到之前使用改变量的方法,整个过程和react很不一样,react的useEffect需要用户手动声明依赖而vue不同。
深入响应式系统这篇官方文档通俗易懂的介绍了vue的响应式原理,对文档中省略的部分补充后,文档中代码可以正常运行。阅读完文章后对其进行解释。
const reactiveObj = reactive({a: 1, b: 2})whenDepsChange(() => {console.log(reactiveObj.a)
});window.onclick = function() {reactiveObj.a += 1
}
当点击页面时会触发reactiveObj.a
属性的修改,属性a
修改后会自动通知到whenDepsChange
重新执行。
- reactive会对入参的对象进行代理并返回新的代理对象,该代理对象监听每个属性的get和set,在属性被访问时对应的get方法会自动探测具体是哪个方法在读该属性,将方法和属性的关系存起来。
whenDepsChange
被执行时,会将入参的匿名函数设置到全局,例如变量activeEffect
。执行whenDepsChange
的匿名函数入参,在匿名函数中会读取属性a
,属性a
的get
方法会拿到全局的activeEffect
(就是在读取属性a
的匿名函数),将其和属性a
关联起来。- 点击window,设置属性
a
,执行其set
方法,取出当时存的关联关系,更新属性后执行对应的函数。
let activeEffect = nullfunction reactive(obj) {return new Proxy(obj, {get(target, key) {track(target, key)return target[key]},set(target, key, value) {target[key] = valuetrigger(target, key)}})
}function ref(value) {const refObject = {get value() {track(refObject, 'value')return value},set value(newValue) {value = newValuetrigger(refObject, 'value')}}return refObject
}const globalSubscribers = new WeakMap()function getSubscribersForProperty(target, key) {if (!globalSubscribers.get(target)) {const map = new Map()globalSubscribers.set(target, map)map.set(key, new Set())}if (!globalSubscribers.get(target).get(key)) {const map = globalSubscribers.get(target)map.set(key, new Set())}return globalSubscribers.get(target).get(key)
}function track(target, key) {if (activeEffect) {const effects = getSubscribersForProperty(target, key)effects.add(activeEffect)}
}function trigger(target, key) {const effects = getSubscribersForProperty(target, key)effects.forEach(effect => effect())
}function whenDepsChange(update) {const effect = () => {activeEffect = effectupdate()activeEffect = null}effect()
}// 以下是用户可感知的
const reactiveObj = reactive({a: 1, b: 2})whenDepsChange(() => {console.log(reactiveObj.a)
});window.onclick = function() {reactiveObj.a += 1
}
参考
深入响应式系统