vue响应式的实现
Vue 响应式实现原理
Vue 的响应式系统基于 数据劫持 和 依赖收集 实现,核心是通过 Object.defineProperty(Vue 2)或 Proxy(Vue 3)监听数据变化,触发视图更新。
Vue 2 的实现方式
数据劫持
通过 Object.defineProperty 重写对象的属性访问器(getter/setter),在数据读取和修改时拦截操作:
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log(`读取 ${key}: ${val}`);
return val;
},
set(newVal) {
if (newVal !== val) {
console.log(`设置 ${key}: ${newVal}`);
val = newVal;
}
}
});
}
依赖收集
每个响应式属性关联一个 Dep(依赖管理器),在 getter 中收集依赖(Watcher),在 setter 中通知更新:
class Dep {
constructor() {
this.subscribers = [];
}
depend() {
if (target && !this.subscribers.includes(target)) {
this.subscribers.push(target);
}
}
notify() {
this.subscribers.forEach(sub => sub());
}
}
let target = null;
function watchEffect(effect) {
target = effect;
effect();
target = null;
}
数组处理
重写数组的变异方法(如 push、pop),在调用时手动触发更新:
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
['push', 'pop', 'shift'].forEach(method => {
const original = arrayProto[method];
arrayMethods[method] = function(...args) {
const result = original.apply(this, args);
dep.notify(); // 触发更新
return result;
};
});
Vue 3 的实现方式
Proxy 代理
使用 Proxy 直接监听整个对象,避免 Vue 2 中递归遍历和数组 hack 的问题:
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;
}
});
}
Effect 与依赖追踪
通过 effect 函数和 WeakMap 结构管理依赖关系:
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 effects = depsMap.get(key);
effects && effects.forEach(effect => effect());
}
性能优化对比
-
Vue 2 的局限性
- 需要递归遍历对象的所有属性初始化响应式。
- 无法检测新增/删除属性(需使用
Vue.set/Vue.delete)。 - 数组需通过重写方法实现响应式。
-
Vue 3 的优势
Proxy直接代理整个对象,无需递归初始化。- 支持动态新增/删除属性。
- 性能更高,内存占用更少。
实践示例
Vue 2 响应式数据
const vm = new Vue({
data: { count: 0 },
template: `<div>{{ count }}</div>`
});
vm.count++; // 视图自动更新
Vue 3 Composition API

import { ref, effect } from 'vue';
const count = ref(0);
effect(() => {
console.log(`Count is: ${count.value}`);
});
count.value++; // 触发 effect






