一直以来对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 = null
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
track(target, key)
return target[key]
},
set(target, key, value) {
target[key] = value
trigger(target, key)
}
})
}
function ref(value) {
const refObject = {
get value() {
track(refObject, 'value')
return value
},
set value(newValue) {
value = newValue
trigger(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 = effect
update()
activeEffect = null
}
effect()
}
// 以下是用户可感知的
const reactiveObj = reactive({a: 1, b: 2})
whenDepsChange(() => {
console.log(reactiveObj.a)
});
window.onclick = function() {
reactiveObj.a += 1
}