vue如何实现数据响应
Vue 数据响应的实现原理
Vue 的数据响应式系统基于 ES5 的 Object.defineProperty 或 ES6 的 Proxy 实现。核心是通过数据劫持结合发布-订阅模式,在数据变化时自动更新视图。
使用 Object.defineProperty 实现
对于 Vue 2.x 版本,主要通过 Object.defineProperty 来实现数据响应:
function defineReactive(obj, key, val) {
const dep = new Dep() // 依赖收集
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.depend() // 收集依赖
}
return val
},
set(newVal) {
if (newVal === val) return
val = newVal
dep.notify() // 通知更新
}
})
}
使用 Proxy 实现
Vue 3.x 改用 Proxy 实现响应式,能更好地处理数组和对象:
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
track(target, key) // 收集依赖
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver)
trigger(target, key) // 触发更新
return result
}
})
}
依赖收集系统
Vue 通过 Dep 和 Watcher 实现依赖收集:
class Dep {
constructor() {
this.subs = []
}
depend() {
if (Dep.target) {
this.subs.push(Dep.target)
}
}
notify() {
this.subs.forEach(watcher => watcher.update())
}
}
数组的特殊处理
Vue 对数组方法进行了特殊处理,重写了会改变数组内容的方法:
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methodsToPatch.forEach(method => {
const original = arrayProto[method]
arrayMethods[method] = function(...args) {
const result = original.apply(this, args)
const ob = this.__ob__
ob.dep.notify()
return result
}
})
响应式 API 使用
Vue 3.x 提供了多个响应式 API:
import { reactive, ref, computed, watch } from 'vue'
// 对象响应式
const state = reactive({ count: 0 })
// 基本类型响应式
const count = ref(0)
// 计算属性
const double = computed(() => count.value * 2)
// 监听器
watch(() => state.count, (newVal, oldVal) => {
console.log(`count changed from ${oldVal} to ${newVal}`)
})
响应式原理总结
- 数据劫持:通过
Object.defineProperty或Proxy拦截数据访问和修改 - 依赖收集:在 getter 中收集依赖,setter 中触发更新
- 虚拟DOM:通过虚拟DOM diff 算法高效更新视图
- 异步更新:使用队列机制批量更新DOM,提高性能
注意事项
- 对象新增属性需要使用
Vue.set或this.$set使其响应式(Vue 2.x) - 数组直接通过索引修改需要使用
Vue.set或数组方法(Vue 2.x) - Vue 3.x 的
Proxy实现解决了这些限制,可以直接修改







