Proxy 的核心机制
在 Vue 3 中,Proxy 主要用于 拦截对象的基本操作,包括 属性读取(get)、修改(set)、删除(deleteProperty) 等。这是 Vue 3 取代 Object.defineProperty 的根本原因。
Proxy 允许拦截的操作:
操作 | 作用 |
---|---|
get(target, key, receiver) | 读取属性时触发 |
set(target, key, value, receiver) | 设置属性时触发 |
deleteProperty(target, key) | 删除属性时触发 |
has(target, key) | 判断 key 是否存在(key in obj) |
ownKeys(target) | 获取对象的所有键(Object.keys(obj)) |
apply(target, thisArg, args) | 代理函数调用 |
construct(target, args) | 代理 new 操作符 |
Proxy 的核心实现
让我们通过一个 手写 Proxy 劫持 的例子,模拟 Vue 3 响应式数据的基本实现:
const handler = {
get(target, key) {
console.log(`读取属性:${key}`);
return Reflect.get(target, key);
},
set(target, key, value) {
console.log(`修改属性:${key} -> ${value}`);
return Reflect.set(target, key, value);
}
};
const data = { message: "Hello Vue 3" };
const proxyData = new Proxy(data, handler);
console.log(proxyData.message); // 读取属性:message
proxyData.message = "Updated"; // 修改属性:message -> Updated
解读:
• Reflect.get(target, key):用于获取目标对象的值,等价于 target[key]。
• Reflect.set(target, key, value):用于修改目标对象的值,等价于 target[key] = value。
• Vue 3 就是基于 Proxy 设计 reactive() 来创建响应式数据。
Vue 3 如何基于 Proxy 构建响应式系统
Vue 3 的核心响应式 API reactive() 就是对 Proxy 的封装,具体实现如下:
function reactive(target) {
if (typeof target !== "object" || target === null) {
return target;
}
return new Proxy(target, {
get(target, key, receiver) {
console.log(`访问属性:${key}`);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log(`修改属性:${key} -> ${value}`);
return Reflect.set(target, key, value, receiver);
}
});
}
const state = reactive({ count: 0 });
console.log(state.count); // 访问属性:count
state.count++; // 修改属性:count -> 1
Vue 3 reactive() 实现的核心点
1. 使用 Proxy 劫持整个对象,而不是每个属性。
2. 支持深层次代理(但 Vue 3 默认是惰性代理,访问时才创建嵌套对象的代理)。
3. 拦截数组和对象的方法(如 push、pop、delete) 。
Vue 3 的依赖收集与副作用处理机制
Vue 3 采用 “响应式依赖收集 + 依赖触发” 来完成视图更新,核心组件包括:
• reactive():创建响应式数据(基于 Proxy)。
• effect():收集依赖,记录哪些属性被访问。
• trigger():数据变更时通知所有依赖执行。
1. Vue 3 依赖收集的核心实现
let activeEffect = null; // 当前正在执行的 effect
const targetMap = new WeakMap(); // 存储依赖
function effect(fn) {
activeEffect = fn;
fn(); // 立即执行一次,收集依赖
activeEffect = null;
}
function track(target, key) {
if (activeEffect) {
let depsMap = targetMap.get(target);
if (!depsMap) {
depsMap = new Map();
targetMap.set(target, depsMap);
}
let deps = depsMap.get(key);
if (!deps) {
deps = new Set();
depsMap.set(key, deps);
}
deps.add(activeEffect);
}
}
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const effects = depsMap.get(key);
if (effects) {
effects.forEach(fn => fn()); // 触发所有依赖
}
}
// 结合 Proxy 创建 Vue 3 响应式对象
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
track(target, key); // 依赖收集
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
trigger(target, key); // 触发更新
return result;
}
});
}
2. Vue 3 响应式数据的完整示例
const state = reactive({ count: 0 });
effect(() => {
console.log(`count 更新:${state.count}`);
});
state.count++; // 触发更新:count 更新:1
state.count = 5; // 触发更新:count 更新:5
执行流程:
1. effect(() => console.log(state.count)) 执行时,触发 get(),收集 state.count 的依赖。
2. state.count++ 触发 set(),调用 trigger() 通知所有依赖执行 effect()。
Vue 3 Proxy 响应式系统的优化
1. 依赖按需收集
Vue 2 在初始化时遍历整个对象,而 Vue 3 只有在 访问属性时才进行代理,减少性能消耗。
2. 自动清理无效依赖
Vue 3 采用 WeakMap + Set 进行依赖存储,避免内存泄漏,Vue 2 需要手动管理依赖删除。
3. 只更新受影响的组件
Vue 3 的 trigger() 机制让每个组件只更新它所依赖的部分,避免 Vue 2 中全局重新计算的问题。
总结
Proxy 使 Vue 3 的响应式系统更高效,支持新增属性监听、数组操作拦截等。依赖追踪采用 WeakMap + Set 存储,提高性能,避免 Vue 2 的内存泄漏问题。 Vue 3 采用 Lazy Proxy(惰性代理),只有访问属性时才进行代理,减少初始化开销。Vue 3 的响应式机制通过effect() 进行自动依赖收集,让数据更新更智能、更高效。