vue数据劫持实现
Vue 数据劫持实现原理
Vue 的数据劫持主要通过 Object.defineProperty 或 Proxy 实现,用于监听数据变化并触发视图更新。
使用 Object.defineProperty
Object.defineProperty 是 Vue 2.x 的核心实现方式,通过对对象属性的劫持实现响应式。
function defineReactive(obj, key, val) {
// 递归处理嵌套对象
observe(val);
Object.defineProperty(obj, key, {
get() {
console.log(`get ${key}: ${val}`);
return val;
},
set(newVal) {
if (newVal !== val) {
console.log(`set ${key}: ${newVal}`);
observe(newVal); // 处理新值为对象的情况
val = newVal;
}
}
});
}
function observe(obj) {
if (typeof obj !== 'object' || obj === null) {
return;
}
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key]);
});
}
// 测试
const data = { foo: 'foo', bar: { a: 1 } };
observe(data);
data.foo; // 输出: get foo: foo
data.foo = 'new foo'; // 输出: set foo: new foo
data.bar.a; // 输出: get a: 1
使用 Proxy
Vue 3.x 改用 Proxy 实现,能直接监听整个对象而非属性,且支持数组变化监听。
function reactive(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
const observed = new Proxy(obj, {
get(target, key, receiver) {
console.log(`get ${key}`);
const result = Reflect.get(target, key, receiver);
return reactive(result); // 递归代理嵌套对象
},
set(target, key, val, receiver) {
console.log(`set ${key}: ${val}`);
return Reflect.set(target, key, val, receiver);
},
deleteProperty(target, key) {
console.log(`delete ${key}`);
return Reflect.deleteProperty(target, key);
}
});
return observed;
}
// 测试
const data = reactive({ foo: 'foo', bar: { a: 1 } });
data.foo; // 输出: get foo
data.foo = 'new foo'; // 输出: set foo: new foo
data.bar.a; // 输出: get bar -> get a
数组劫持的特殊处理
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) {
const result = original.apply(this, args);
console.log(`Array ${method}: ${args}`);
// 触发更新逻辑
return result;
};
});
function observeArray(arr) {
arr.__proto__ = arrayMethods;
}
// 测试
const arr = [];
observeArray(arr);
arr.push(1); // 输出: Array push: 1
总结
- Vue 2.x:基于
Object.defineProperty,需递归遍历对象属性,对数组需特殊处理。 - Vue 3.x:基于
Proxy,可拦截整个对象操作,无需递归初始化,性能更好。







