vue怎么实现数据劫持
Vue 数据劫持的实现原理
Vue 的数据劫持主要通过 Object.defineProperty 或 Proxy 实现,用于监听数据变化并触发视图更新。
使用 Object.defineProperty(Vue 2.x)
Object.defineProperty 是 Vue 2.x 的核心实现方式,通过劫持对象的属性访问和修改。
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
console.log(`读取属性 ${key}: ${val}`);
return val;
},
set(newVal) {
if (val === newVal) return;
console.log(`设置属性 ${key}: ${newVal}`);
val = newVal;
}
});
}
const data = {};
defineReactive(data, 'message', 'Hello Vue');
data.message; // 触发 getter
data.message = 'Updated'; // 触发 setter
使用 Proxy(Vue 3.x)
Vue 3.x 改用 Proxy 实现数据劫持,能监听整个对象而非单个属性,支持数组和嵌套对象。
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
console.log(`读取属性 ${key}`);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log(`设置属性 ${key}: ${value}`);
return Reflect.set(target, key, value, receiver);
}
});
}
const data = reactive({ message: 'Hello Vue' });
data.message; // 触发 get
data.message = 'Updated'; // 触发 set
嵌套对象的劫持
递归处理嵌套对象以确保深层属性也能被监听。
function observe(obj) {
if (typeof obj !== 'object' || obj === null) return;
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key]);
observe(obj[key]); // 递归劫持嵌套属性
});
}
const nestedData = { user: { name: 'Alice' } };
observe(nestedData);
nestedData.user.name = 'Bob'; // 触发嵌套属性的 setter
数组的劫持
Vue 2.x 通过重写数组的变异方法(如 push、pop)实现监听。
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(method => {
const original = arrayProto[method];
arrayMethods[method] = function(...args) {
console.log(`数组方法 ${method} 被调用`);
return original.apply(this, args);
};
});
function observeArray(arr) {
arr.__proto__ = arrayMethods;
arr.forEach(item => observe(item));
}
const arr = [1, 2, 3];
observeArray(arr);
arr.push(4); // 触发拦截
总结
- Vue 2.x 使用
Object.defineProperty劫持对象属性,需递归处理嵌套对象和重写数组方法。 - Vue 3.x 使用
Proxy直接监听整个对象,简化实现并支持更多场景。 - 数据劫持的核心目的是在数据变化时触发依赖更新(如视图渲染)。







