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} 从 ${val} 到 ${newVal}`);
val = newVal;
}
});
}
const data = { name: 'Vue' };
defineReactive(data, 'name', data.name);
data.name; // 输出: "获取属性 name: Vue"
data.name = 'React'; // 输出: "设置属性 name 从 Vue 到 React"
使用 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} 从 ${target[key]} 到 ${value}`);
target[key] = value;
return true;
}
});
}
const data = reactive({ name: 'Vue' });
data.name; // 输出: "获取属性 name: Vue"
data.name = 'React'; // 输出: "设置属性 name 从 Vue 到 React"
深度监听
对于嵌套对象,需递归劫持所有层级的属性。
Object.defineProperty 实现
function observe(obj) {
if (typeof obj !== 'object' || obj === null) return;
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key]);
observe(obj[key]); // 递归劫持嵌套属性
});
}
const data = { user: { name: 'Alice' } };
observe(data);
data.user.name = 'Bob'; // 触发 setter
Proxy 实现
function deepReactive(obj) {
if (typeof obj !== 'object' || obj === null) return obj;
Object.keys(obj).forEach(key => {
obj[key] = deepReactive(obj[key]); // 递归处理嵌套属性
});
return reactive(obj);
}
const data = deepReactive({ user: { name: 'Alice' } });
data.user.name = 'Bob'; // 触发 setter
数组的劫持
Vue 2.x 中需特殊处理数组方法(如 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);
};
});
function observeArray(arr) {
arr.__proto__ = arrayMethods;
arr.forEach(item => observe(item));
}
const list = [1, 2, 3];
observeArray(list);
list.push(4); // 输出: "数组方法 push 被调用"
注意事项
Object.defineProperty无法检测新增或删除的属性,需使用Vue.set或Vue.delete。Proxy能直接监听新增/删除属性,但需注意浏览器兼容性(IE 不支持)。- 性能上
Proxy优于递归defineProperty,尤其对复杂对象结构。






