vue监听实现原理
Vue 监听实现原理
Vue 的监听机制主要依赖于响应式系统和依赖收集,通过 Object.defineProperty 或 Proxy 实现数据劫持,并在数据变化时触发更新。
核心概念:响应式系统
Vue 的响应式系统通过劫持数据的访问和修改,在数据变化时自动更新依赖该数据的视图或计算属性。以下是实现的关键步骤:
-
数据劫持
对于对象属性,Vue 使用Object.defineProperty(Vue 2)或Proxy(Vue 3)拦截属性的读取和设置操作。Object.defineProperty示例:Object.defineProperty(obj, key, { get() { // 依赖收集 return val }, set(newVal) { // 触发更新 val = newVal } })Proxy示例(Vue 3):new Proxy(obj, { get(target, key) { /* 依赖收集 */ }, set(target, key, newVal) { /* 触发更新 */ } })
-
依赖收集
在属性的getter中,Vue 会将当前正在执行的“依赖”(如组件的渲染函数、计算属性等)记录到一个全局的依赖管理器中(如Dep类)。- 每个属性对应一个
Dep实例,用于存储所有依赖它的“订阅者”(Watcher实例)。 - 当属性被访问时,当前
Watcher会被添加到Dep的订阅列表中。
- 每个属性对应一个
-
触发更新
当属性被修改时,setter会通知对应的Dep实例,遍历所有订阅的Watcher并执行其更新逻辑(如重新渲染组件)。
监听的具体实现
Vue 提供了 watch 和 computed 两种监听方式,底层均基于上述响应式系统:
-
watch的实现- 创建一个
Watcher实例,传入回调函数和监听的属性路径。 - 在初始化时,
Watcher会主动触发一次属性的getter,从而完成依赖收集。 - 当属性变化时,
Watcher的回调函数会被调用。
- 创建一个
-
computed的实现- 计算属性本质是一个惰性求值的
Watcher,仅在依赖的属性变化时重新计算。 - 计算属性会缓存结果,避免重复计算。
- 计算属性本质是一个惰性求值的
示例代码解析
以下是一个简化的 Watcher 和 Dep 实现:
class Dep {
constructor() {
this.subscribers = []
}
depend() {
if (target && !this.subscribers.includes(target)) {
this.subscribers.push(target)
}
}
notify() {
this.subscribers.forEach(sub => sub())
}
}
let target = null
function watchEffect(fn) {
target = fn
fn() // 触发依赖收集
target = null
}
const depsMap = new WeakMap()
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
let dep = depsMap.get(target)
if (!dep) {
dep = new Dep()
depsMap.set(target, dep)
}
dep.depend()
return Reflect.get(target, key)
},
set(target, key, newVal) {
Reflect.set(target, key, newVal)
const dep = depsMap.get(target)
if (dep) dep.notify()
return true
}
})
}
总结
Vue 的监听机制通过以下步骤实现:
- 数据劫持拦截属性的读写操作。
- 依赖收集在属性访问时记录依赖关系。
- 触发更新在属性修改时通知所有依赖项。
watch和computed基于相同的响应式系统,但分别适用于异步回调和高性能计算场景。







