vue数据劫持实现
Vue 数据劫持的实现原理
Vue 的数据劫持主要通过 Object.defineProperty 或 Proxy 实现,用于监听数据变化并触发视图更新。
使用 Object.defineProperty
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 (newVal === val) 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 改用 Proxy 实现数据劫持,能监听整个对象而非单个属性。

function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
console.log(`获取属性 ${key}: ${target[key]}`);
return target[key];
},
set(target, key, value) {
if (target[key] === value) return true;
console.log(`设置属性 ${key}: ${value}`);
target[key] = value;
return true;
}
});
}
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: 'Vue' } };
observe(nestedData);
nestedData.user.name = 'React'; // 触发嵌套属性的 setter
数组的劫持
数组需特殊处理,重写变异方法(如 push、pop)。
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
['push', 'pop', 'shift'].forEach(method => {
arrayMethods[method] = function(...args) {
console.log(`数组方法 ${method} 被调用`);
return arrayProto[method].apply(this, args);
};
});
function observeArray(arr) {
arr.__proto__ = arrayMethods;
arr.forEach(item => observe(item));
}
const arr = [1, 2, 3];
observeArray(arr);
arr.push(4); // 触发自定义 push 方法
总结
- Vue 2.x 使用
Object.defineProperty劫持对象属性,需递归处理嵌套对象和数组。 - Vue 3.x 改用
Proxy实现更全面的劫持,能直接监听整个对象和数组变化。 - 数组需通过重写原型方法实现劫持,确保变异方法可被监听。






