Vue3响应式系统源码解析-Effect篇
2024-01-22 11:22:40
在上一篇中,我们介绍了 Vue3 响应式系统的基本原理和实现,以及如何劫持数据。但还有两个大问题一直没解决,即具体是如何收集依赖,又是如何触发监听函数的。
从前文中,我们大致能猜到:向 effect 函数传递一个原始函数,会创建一个监听函数,并且会立即执行一次。而第一次执行时,就能通过读操作中的 track 函数将当前正在读取的属性收集起来,形成依赖关系。
但是,这仅仅是我们猜测,我们还是来细致地看看源码吧。
依赖收集
依赖收集的原理
Vue3 的依赖收集原理非常巧妙,它利用了 JavaScript 的 Proxy 对象。Proxy 对象可以劫持一个对象的属性访问操作,并在访问时执行我们自己的代码。
在 Vue3 中,每个响应式对象都被一个 Proxy 对象包裹起来。这个 Proxy 对象会劫持对象的属性访问操作,并在访问时收集依赖关系。
例如,当我们访问一个响应式对象的属性时,Proxy 对象会执行以下步骤:
- 检查该属性是否已经被劫持。如果没有,则创建一个新的监听函数并将其与该属性关联。
- 将当前正在执行的 effect 函数添加到该监听函数的依赖数组中。
- 返回属性的值。
这样,我们就实现了依赖的收集。
依赖收集的实现
在 Vue3 中,依赖的收集是在 track
函数中实现的。track
函数接受两个参数:一个目标对象和一个属性名。它会检查该属性是否已经被劫持,如果没有,则创建一个新的监听函数并将其与该属性关联。然后,将当前正在执行的 effect 函数添加到该监听函数的依赖数组中。
function track(target, key) {
let dep = targetMap.get(target)
if (!dep) {
dep = new Dep()
targetMap.set(target, dep)
}
let depIds = dep.depIds
let effect = activeEffect
if (effect) {
let id = effect.id
if (depIds.has(id)) {
return
}
depIds.add(id)
effect.deps.push(dep)
}
}
触发监听函数
触发监听函数的原理
Vue3 的监听函数触发原理也非常巧妙,它利用了 JavaScript 的事件循环。
在 Vue3 中,每个监听函数都被保存在一个队列中。当响应式对象的属性发生变化时,Vue3 会将该属性的监听函数添加到队列中。然后,在下一个事件循环中,Vue3 会依次执行队列中的监听函数。
这样,我们就实现了监听函数的触发。
触发监听函数的实现
在 Vue3 中,监听函数的触发是在 trigger
函数中实现的。trigger
函数接受一个目标对象和一个属性名作为参数。它会获取该属性的监听函数队列,然后依次执行队列中的监听函数。
function trigger(target, key) {
let dep = targetMap.get(target)
if (!dep) {
return
}
let depIds = dep.depIds
depIds.forEach((id) => {
let effect = effectMap.get(id)
if (effect) {
effect.run()
}
})
}
总结
以上就是 Vue3 响应式系统中依赖收集和监听函数触发的原理和实现。通过这两个机制,Vue3 可以实现响应式数据的变化跟踪和监听函数的自动执行,从而实现响应式系统。