vue实现响应式原理
Vue 响应式原理的核心机制
Vue 的响应式系统基于 JavaScript 的 Object.defineProperty(Vue 2)或 Proxy(Vue 3)实现,通过数据劫持结合发布-订阅模式,自动追踪依赖并触发更新。
数据劫持与依赖收集
在 Vue 2 中,通过 Object.defineProperty 递归遍历对象的属性,将每个属性转换为 getter/setter。当数据被访问时(getter),当前 Watcher(依赖)会被收集到 Dep(依赖管理器)中;当数据变更时(setter),Dep 会通知所有 Watcher 更新视图。

// 简化版数据劫持示例
function defineReactive(obj, key, val) {
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
if (Dep.target) dep.addSub(Dep.target); // 收集依赖
return val;
},
set(newVal) {
if (newVal === val) return;
val = newVal;
dep.notify(); // 触发更新
}
});
}
Vue 3 的 Proxy 优化
Vue 3 使用 Proxy 替代 Object.defineProperty,直接代理整个对象而非属性,避免了递归遍历和数组方法的重写。Proxy 可以拦截更多操作(如新增/删除属性),且性能更高。

// 简化版 Proxy 实现
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
track(target, key); // 依赖收集
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
Reflect.set(target, key, value, receiver);
trigger(target, key); // 触发更新
return true;
}
});
}
依赖更新与批量处理
当数据变化时,Vue 不会立即更新 DOM,而是将 Watcher 推入队列进行去重和批量处理(通过 nextTick 微任务机制)。这减少了重复渲染,提高了性能。
// 简化版异步更新队列
let queue = [];
let waiting = false;
function queueWatcher(watcher) {
if (!queue.includes(watcher)) queue.push(watcher);
if (!waiting) {
waiting = true;
Promise.resolve().then(flushQueue);
}
}
function flushQueue() {
queue.forEach(watcher => watcher.run());
queue = [];
waiting = false;
}
数组的特殊处理
由于 Object.defineProperty 无法监听数组索引变化,Vue 2 通过重写数组的 7 个变异方法(如 push、splice)实现响应式。调用这些方法时会手动触发通知。
// 数组方法重写示例
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);
this.__ob__.dep.notify(); // 触发更新
return result;
};
});
响应式系统的局限性
对于动态新增的属性(Vue 2),需使用 Vue.set 或 this.$set 确保响应性。Vue 3 的 Proxy 则无此限制。对象属性的直接替换(如 obj = newObj)可能丢失响应性,需使用 reactive 包裹新对象。






