Vue 1.x 响应式系统
Vue 1.x 如何通过 Object.defineProperty 实现数据响应式
问题
Vue 1.x 的响应式系统是如何实现的?它有什么特点?
解答
Vue 1.x 使用 Object.defineProperty 对数据进行劫持,配合 Dep(依赖收集器)和 Watcher(观察者)实现响应式更新。
整体架构
Data --> Observer --> Dep <--> Watcher --> DOM
- Observer:遍历数据,用
Object.defineProperty转换为 getter/setter - Dep:每个属性对应一个 Dep,收集依赖该属性的 Watcher
- Watcher:每个 DOM 绑定对应一个 Watcher,属性变化时更新 DOM
简化实现
// 依赖收集器
class Dep {
constructor() {
this.subs = [] // 存储 Watcher
}
addSub(watcher) {
this.subs.push(watcher)
}
notify() {
// 通知所有 Watcher 更新
this.subs.forEach(watcher => watcher.update())
}
}
// 全局变量,用于收集依赖时标记当前 Watcher
Dep.target = null
// 观察者
class Watcher {
constructor(vm, key, callback) {
this.vm = vm
this.key = key
this.callback = callback
// 触发 getter,收集依赖
Dep.target = this
this.value = vm[key]
Dep.target = null
}
update() {
const newValue = this.vm[this.key]
if (newValue !== this.value) {
this.value = newValue
this.callback(newValue)
}
}
}
// 将数据转换为响应式
function defineReactive(obj, key, val) {
const dep = new Dep()
Object.defineProperty(obj, key, {
get() {
// 收集依赖
if (Dep.target) {
dep.addSub(Dep.target)
}
return val
},
set(newVal) {
if (newVal === val) return
val = newVal
// 通知更新
dep.notify()
}
})
}
// 遍历对象,转换所有属性
function observe(obj) {
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key])
})
}
使用示例
// 创建数据
const data = { message: 'Hello', count: 0 }
// 转换为响应式
observe(data)
// 创建 Watcher,模拟 DOM 绑定
new Watcher(data, 'message', (val) => {
console.log('message 更新:', val)
// 实际场景:更新 DOM
})
new Watcher(data, 'count', (val) => {
console.log('count 更新:', val)
})
// 修改数据,自动触发更新
data.message = 'World' // 输出: message 更新: World
data.count = 1 // 输出: count 更新: 1
Vue 1.x 的特点
Vue 1.x 采用细粒度依赖追踪:每个 DOM 节点绑定都有独立的 Watcher。
// 模板
// <p>{{ name }}</p>
// <p>{{ age }}</p>
// <p>{{ name }} - {{ age }}</p>
// Vue 1.x 会创建 3 个 Watcher
// Watcher1 -> 依赖 name
// Watcher2 -> 依赖 age
// Watcher3 -> 依赖 name, age
这种方式更新精确,但 Watcher 数量多,内存开销大。
关键点
- Object.defineProperty:通过 getter/setter 拦截数据读写
- Dep:每个属性一个 Dep,存储依赖该属性的 Watcher
- Watcher:每个 DOM 绑定一个 Watcher,数据变化时更新对应 DOM
- 细粒度追踪:更新精确但 Watcher 多,内存占用高
- 局限性:无法检测属性新增/删除、数组索引修改
目录