vue实现响应式原理
Vue 响应式原理的核心机制
Vue 的响应式系统基于 数据劫持 和 依赖收集,通过以下机制实现:
Object.defineProperty(Vue 2.x)
- 对数据对象的属性进行劫持,通过
getter和setter拦截读写操作。 getter中收集依赖(Watcher),setter中触发更新。
// 简化版数据劫持示例
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log('收集依赖');
return val;
},
set(newVal) {
if (newVal !== val) {
console.log('触发更新');
val = newVal;
}
}
});
}
Proxy(Vue 3.x)
- 使用 ES6 的
Proxy代理整个对象,支持动态新增属性监听。 - 通过
get和set陷阱函数实现依赖收集和触发更新。
// 简化版 Proxy 示例
const reactive = (target) => {
return new Proxy(target, {
get(target, key) {
console.log('收集依赖');
return target[key];
},
set(target, key, value) {
if (target[key] !== value) {
console.log('触发更新');
target[key] = value;
}
return true;
}
});
};
依赖收集与派发更新
Watcher 和 Dep
- 每个响应式属性关联一个
Dep(依赖管理器),用于存储依赖的Watcher。 - 组件渲染时创建
Watcher,触发getter将Watcher添加到Dep中。 - 数据变化时通过
setter通知Dep,执行所有Watcher的更新逻辑。
class Dep {
constructor() {
this.subscribers = [];
}
depend() {
if (target && !this.subscribers.includes(target)) {
this.subscribers.push(target);
}
}
notify() {
this.subscribers.forEach(sub => sub());
}
}
异步更新队列
- 多次数据修改会合并为一次更新,通过
nextTick实现异步批量处理。 - 避免频繁 DOM 操作,提升性能。
// 简化版异步更新
let queue = [];
let flushing = false;
function queueWatcher(watcher) {
if (!flushing) {
queue.push(watcher);
nextTick(flushQueue);
}
}
function flushQueue() {
flushing = true;
queue.forEach(watcher => watcher.run());
queue = [];
flushing = false;
}
数组响应式处理
- 重写数组的
push、pop等变异方法,在调用时触发更新。 - Vue 2.x 通过原型链拦截实现,Vue 3.x 的
Proxy天然支持。
// Vue 2.x 数组劫持示例
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
['push', 'pop'].forEach(method => {
arrayMethods[method] = function() {
const result = arrayProto[method].apply(this, arguments);
dep.notify(); // 手动触发更新
return result;
};
});
总结对比
Vue 2.x 限制
- 无法检测对象属性的添加/删除(需用
Vue.set/Vue.delete)。 - 数组需通过变异方法触发更新。
Vue 3.x 改进
Proxy支持全对象监听,包括动态属性。- 性能优化,减少递归劫持的开销。







