vue响应式实现
Vue 响应式实现原理
Vue 的响应式系统基于 Object.defineProperty(Vue 2.x)或 Proxy(Vue 3.x)实现,通过数据劫持和依赖收集实现数据变化时的自动更新。
Vue 2.x 的实现方式
Vue 2.x 使用 Object.defineProperty 对对象的属性进行劫持,通过 getter 和 setter 监听数据变化。
function defineReactive(obj, key, val) {
// 递归处理嵌套对象
observe(val);
Object.defineProperty(obj, key, {
get() {
console.log(`get ${key}: ${val}`);
return val;
},
set(newVal) {
if (newVal !== val) {
console.log(`set ${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]);
});
}
依赖收集通过 Dep 和 Watcher 实现:
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
class Watcher {
constructor(vm, key, cb) {
this.vm = vm;
this.key = key;
this.cb = cb;
Dep.target = this;
this.vm[this.key]; // 触发 getter,收集依赖
Dep.target = null;
}
update() {
this.cb.call(this.vm, this.vm[this.key]);
}
}
Vue 3.x 的实现方式
Vue 3.x 改用 Proxy 实现响应式,解决了 Object.defineProperty 的局限性(如数组监听、新增属性等)。
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
console.log(`get ${key}`);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log(`set ${key}: ${value}`);
return Reflect.set(target, key, value, receiver);
}
});
}
依赖收集和触发更新通过 effect 和 track/trigger 实现:
let activeEffect;
const targetMap = new WeakMap();
function track(target, key) {
if (!activeEffect) return;
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
dep.add(activeEffect);
}
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const dep = depsMap.get(key);
if (dep) {
dep.forEach(effect => effect());
}
}
function effect(fn) {
activeEffect = fn;
fn();
activeEffect = undefined;
}
响应式系统的核心流程
数据劫持:通过 Object.defineProperty 或 Proxy 拦截对象的读写操作。
依赖收集:在属性被访问时(get),收集当前正在执行的依赖(如组件的渲染函数)。
触发更新:在属性被修改时(set),通知所有相关依赖进行更新。
数组的特殊处理
Vue 2.x 中通过重写数组的变异方法(如 push、pop)实现响应式:
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);
const ob = this.__ob__;
ob.dep.notify();
return result;
};
});
Vue 3.x 的 Proxy 可以直接监听数组变化,无需特殊处理。
响应式系统的优化
Vue 3.x 的响应式系统相比 Vue 2.x 有以下改进:
性能更好:Proxy 是语言层面的支持,比 Object.defineProperty 更高效。
功能更强大:可以检测到属性的添加和删除,以及数组索引和长度的变化。
代码更简洁:无需像 Vue 2.x 那样对数组进行特殊处理。







