Vue 3 使用 Proxy 重构了其响应式系统(Reactivity System),这是 Vue 3 最重要的改进之一。通过 Proxy
,Vue 3 实现了更高效、更灵活的依赖追踪和更新触发机制。
1. 响应式系统的核心目标
Vue 的响应式系统需要解决两个核心问题:
-
依赖收集:自动追踪数据属性被哪些组件、计算属性或侦听器(
effect
)使用。 -
触发更新:当数据变化时,自动通知所有依赖它的地方进行更新。
Vue 2 使用 Object.defineProperty
实现响应式,但它存在以下问题:
-
无法检测属性的添加或删除(需要
Vue.set
/Vue.delete
)。 -
对数组的某些操作(如通过索引修改元素、修改
length
)无法触发更新。 -
性能问题:递归遍历对象所有属性并转换为
getter/setter
。
Vue 3 使用 Proxy
解决了这些问题。
2. Proxy 在 Vue 3 中的具体应用
(1) 创建响应式对象
Vue 3 通过 reactive()
函数将普通对象转换为响应式对象:
import { reactive } from 'vue';
const obj = reactive({ count: 0 });
底层实现中,reactive()
内部使用 Proxy
代理目标对象。
(2) 依赖收集(Track)
当访问响应式对象的属性时,Proxy
的 get
陷阱会触发依赖收集:
const handlers = {
get(target, key, receiver) {
// 1. 触发依赖收集
track(target, key);
// 2. 返回属性值(如果是对象,递归代理)
return Reflect.get(target, key, receiver);
}
};
-
track(target, key)
:将当前正在运行的effect
(如组件的渲染函数)记录到依赖列表中。
(3) 触发更新(Trigger)
当修改响应式对象的属性时,Proxy
的 set
陷阱会触发更新:
const handlers = {
set(target, key, value, receiver) {
// 1. 设置属性值
const result = Reflect.set(target, key, value, receiver);
// 2. 触发依赖更新
trigger(target, key);
return result;
}
};
-
trigger(target, key)
:通知所有依赖此属性的effect
重新执行(如重新渲染组件)。
(4) 递归代理嵌套对象
Proxy
可以自动处理嵌套对象的响应式:
const obj = reactive({
nested: { a: 1 } // 嵌套对象也会被代理
});
// 访问 obj.nested.a 会触发依赖收集
// 修改 obj.nested.a 会触发更新
-
Vue 3 在
get
陷阱中,如果发现属性值是对象,会递归调用reactive()
将其转换为响应式对象。
(5) 支持动态属性和数组
Proxy
天然支持动态属性和数组操作:
const arr = reactive([1, 2, 3]);
arr.push(4); // 触发更新(Vue 2 中需要特殊处理)
const obj = reactive({});
obj.newProp = 42; // 触发更新(Vue 2 中需要 Vue.set)
3. 对比 Vue 2 的改进
特性 | Vue 2 (Object.defineProperty ) | Vue 3 (Proxy ) |
---|---|---|
动态属性 | 不支持(需 Vue.set ) | 支持 |
数组操作 | 需重写数组方法(如 push ) | 直接支持 |
性能 | 初始化时递归遍历所有属性 | 按需代理(惰性转换) |
嵌套对象 | 需要递归初始化 | 按需递归代理 |
删除属性 (delete ) | 需要 Vue.delete | 直接支持 |
4. 核心代码实现(简化版)
以下是 Vue 3 响应式系统的简化实现:
// 依赖收集和触发逻辑
const targetMap = new WeakMap(); // 存储所有依赖关系
function track(target, key) {
if (activeEffect) {
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) {
const dep = depsMap.get(key);
if (dep) {
dep.forEach(effect => effect());
}
}
}
// 响应式代理
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
track(target, key);
const res = Reflect.get(target, key, receiver);
if (typeof res === 'object' && res !== null) {
return reactive(res); // 递归代理嵌套对象
}
return res;
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
trigger(target, key);
return result;
}
});
}
// 当前正在运行的 effect
let activeEffect = null;
function effect(fn) {
activeEffect = fn;
fn();
activeEffect = null;
}