vue数据拦截怎么实现
Vue 数据拦截的实现方式
Vue 的数据拦截核心是通过 Object.defineProperty 或 Proxy 实现的响应式系统,以下是具体实现方法:

使用 Object.defineProperty(Vue 2.x)
通过劫持对象的属性访问和修改,触发依赖收集和更新通知。

function defineReactive(obj, key, val) {
// 递归处理嵌套对象
observe(val);
Object.defineProperty(obj, key, {
get() {
console.log(`读取 ${key}: ${val}`);
return val;
},
set(newVal) {
if (newVal !== val) {
console.log(`设置 ${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: 'bar' };
observe(data);
data.foo; // 输出 "读取 foo: bar"
data.foo = 'baz'; // 输出 "设置 foo 为 baz"
使用 Proxy(Vue 3.x)
通过代理对象拦截所有属性的操作,支持数组和动态新增属性。
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
console.log(`读取 ${key}: ${target[key]}`);
return target[key];
},
set(target, key, val) {
if (target[key] !== val) {
console.log(`设置 ${key} 为 ${val}`);
target[key] = val;
}
return true;
}
});
}
// 测试
const data = reactive({ foo: 'bar' });
data.foo; // 输出 "读取 foo: bar"
data.foo = 'baz'; // 输出 "设置 foo 为 baz"
实现依赖收集与更新
结合发布-订阅模式,在拦截时触发依赖更新:
class Dep {
constructor() {
this.subscribers = new Set();
}
depend() {
if (activeEffect) this.subscribers.add(activeEffect);
}
notify() {
this.subscribers.forEach(effect => effect());
}
}
let activeEffect = null;
function watchEffect(effect) {
activeEffect = effect;
effect();
activeEffect = null;
}
// 修改 defineReactive 加入依赖收集
function defineReactive(obj, key, val) {
const dep = new Dep();
observe(val);
Object.defineProperty(obj, key, {
get() {
dep.depend();
return val;
},
set(newVal) {
if (newVal !== val) {
observe(newVal);
val = newVal;
dep.notify();
}
}
});
}
// 测试
const data = { count: 0 };
observe(data);
watchEffect(() => {
console.log(`当前计数: ${data.count}`);
});
data.count++; // 触发日志输出
注意事项
- 性能问题:
Object.defineProperty需要递归遍历对象,初始化性能较差。 - 数组限制:Vue 2.x 需重写数组方法(如
push)实现拦截。 - Proxy 优势:Vue 3.x 的
Proxy直接支持动态属性和数组操作。
通过上述方法可以实现类似 Vue 的响应式数据拦截,核心在于在数据访问时收集依赖,在数据变更时触发更新。






