vue数据劫持实现
Vue 数据劫持实现原理
Vue 的数据劫持核心是通过 Object.defineProperty(Vue 2.x)或 Proxy(Vue 3.x)实现的响应式系统。以下是具体实现方式:
基于 Object.defineProperty 的实现(Vue 2.x)
数据劫持初始化
通过遍历对象的属性,递归地将每个属性转换为 getter/setter:
function observe(data) {
if (typeof data !== 'object' || data === null) return;
return new Observer(data);
}
class Observer {
constructor(data) {
this.walk(data);
}
walk(data) {
Object.keys(data).forEach(key => {
defineReactive(data, key, data[key]);
});
}
}
function defineReactive(obj, key, val) {
observe(val); // 递归处理嵌套对象
Object.defineProperty(obj, key, {
get() {
console.log(`读取 ${key}`);
return val;
},
set(newVal) {
if (newVal === val) return;
console.log(`设置 ${key}`);
val = newVal;
observe(newVal); // 新值为对象时继续劫持
}
});
}
数组方法劫持
重写数组的 push、pop 等原型方法以触发更新:

const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
['push', 'pop', 'shift'].forEach(method => {
arrayMethods[method] = function(...args) {
const result = arrayProto[method].apply(this, args);
console.log(`数组变更:${method}`);
return result;
};
});
基于 Proxy 的实现(Vue 3.x)
Proxy 可以直接拦截整个对象,无需递归初始化:
function reactive(data) {
if (typeof data !== 'object' || data === null) return data;
return new Proxy(data, {
get(target, key, receiver) {
console.log(`读取 ${key}`);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log(`设置 ${key}`);
return Reflect.set(target, key, value, receiver);
},
deleteProperty(target, key) {
console.log(`删除 ${key}`);
return Reflect.deleteProperty(target, key);
}
});
}
嵌套对象处理 Proxy 自动处理嵌套对象,无需显式递归:

const obj = reactive({
nested: { a: 1 }
});
obj.nested.a = 2; // 自动触发 setter
差异对比
-
初始化性能
Object.defineProperty需要递归遍历所有属性。Proxy直接代理整个对象,按需触发。
-
数组支持
Object.defineProperty需特殊处理数组方法。Proxy天然支持数组索引和 length 变化。
-
新属性响应
Object.defineProperty需手动调用Vue.set。Proxy自动检测动态添加的属性。
实现注意事项
- 循环引用:需使用
WeakMap存储已代理对象避免重复。 - 性能优化:Vue 3 的 Proxy 实现中通过
Reflect避免不必要的操作。 - 浏览器兼容性:Proxy 无法被 polyfill,Vue 3 放弃支持 IE11。
以上实现省略了依赖收集和派发更新的细节(如 Dep/Watcher 机制),但展示了数据劫持的核心逻辑。






