vue怎么实现数据劫持
Vue 数据劫持的实现原理
Vue 的数据劫持主要通过 Object.defineProperty 或 Proxy 实现,用于监听数据变化并触发视图更新。以下是核心实现方式:
使用 Object.defineProperty
Vue 2.x 版本采用 Object.defineProperty 对对象的属性进行劫持,通过 getter 和 setter 拦截读写操作。
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
console.log(`读取属性 ${key}`);
return val;
},
set(newVal) {
if (newVal === val) return;
console.log(`设置属性 ${key} 为 ${newVal}`);
val = newVal;
}
});
}
const data = { name: 'Vue' };
defineReactive(data, 'name', data.name);
data.name = 'React'; // 触发 setter
- 局限性:无法监听新增/删除的属性,需配合
Vue.set或Vue.delete使用。
使用 Proxy
Vue 3.x 改用 Proxy 实现数据劫持,支持对整个对象的拦截,包括新增或删除属性。
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
console.log(`读取属性 ${key}`);
return Reflect.get(target, key);
},
set(target, key, value) {
if (target[key] === value) return true;
console.log(`设置属性 ${key} 为 ${value}`);
return Reflect.set(target, key, value);
},
deleteProperty(target, key) {
console.log(`删除属性 ${key}`);
return Reflect.deleteProperty(target, key);
}
});
}
const data = reactive({ name: 'Vue' });
data.name = 'React'; // 触发 set
delete data.name; // 触发 deleteProperty
- 优势:支持动态属性增删,性能更优。
数组的劫持处理
Vue 对数组方法(如 push、pop)进行重写,通过原型链拦截实现响应式。
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
['push', 'pop', 'shift'].forEach(method => {
const original = arrayProto[method];
arrayMethods[method] = function(...args) {
console.log(`数组方法 ${method} 被调用`);
return original.apply(this, args);
};
});
const arr = [];
arr.__proto__ = arrayMethods;
arr.push(1); // 触发拦截
依赖收集与派发更新
Vue 通过 Dep(依赖收集器)和 Watcher(观察者)实现数据变化到视图更新的联动。
- Dep:每个响应式属性对应一个 Dep 实例,用于存储依赖的 Watcher。
- Watcher:在 getter 中收集依赖,在 setter 中通知 Watcher 更新。
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(cb) {
target = cb;
cb();
target = null;
}
总结
- Vue 2.x 使用
Object.defineProperty劫持对象属性,需额外处理数组和动态属性。 - Vue 3.x 使用
Proxy提供更全面的劫持能力,性能更优。 - 依赖收集机制确保数据变化时精确更新相关视图。







