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(`读取 ${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; // 触发 getter
data.foo = 'baz'; // 触发 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, newVal) {
if (target[key] !== newVal) {
console.log(`设置 ${key}: ${newVal}`);
target[key] = newVal;
}
return true;
}
});
}
// 示例
const data = reactive({ foo: 'bar' });
data.foo; // 触发 get
data.foo = 'baz'; // 触发 set
差异对比
-
Object.defineProperty
需遍历对象属性逐个劫持,无法监听新增/删除属性(需配合Vue.set/Vue.delete)。 -
Proxy
直接代理整个对象,支持监听新增/删除属性,性能更优,但兼容性较低(IE 不支持)。
实现依赖收集
数据劫持需结合发布-订阅模式,在 getter 中收集依赖(Watcher),在 setter 中通知更新。
class Dep {
constructor() {
this.subscribers = new Set();
}
depend() {
if (currentWatcher) {
this.subscribers.add(currentWatcher);
}
}
notify() {
this.subscribers.forEach(watcher => watcher.update());
}
}
let currentWatcher = null;
function watchEffect(fn) {
currentWatcher = fn;
fn();
currentWatcher = 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();
}
}
});
}
通过以上方式,Vue 实现了数据变化到视图更新的自动响应。







