vue3 reactive
Vue 3 的 reactive
通过 Proxy API 实现响应式,其核心思路比 Vue2 的 Object.defineProperty
更高效且功能更全面。以下是实现思路的详细拆解:
1. 代理对象(Proxy)
利用 Proxy
代理目标对象,拦截 13 种操作(如 get/set/deleteProperty
等),无需递归遍历初始属性。
2. 依赖存储结构
- WeakMap:键为原始对象,值为
Map
(存储属性对应的依赖集合) - Map:键为属性名,值为
Set
(存储该属性关联的effect
函数)
const targetMap = new WeakMap(); // 核心依赖关系存储
// 结构示例:
// targetMap: {
// {obj}: {
// 'count': [effect1, effect2],
// 'name': [effect3]
// }
// }
实现步骤
1. 创建响应式对象(reactive 函数)
function reactive(target) {
// 避免重复代理
if (isReactive(target)) return target;
// 非对象类型直接返回
if (typeof target !== 'object' || target === null) return target;
// 使用 Proxy 代理对象
const proxy = new Proxy(target, {
get(obj, key, receiver) {
// 特殊处理 'is_reactive' 标识
if (key === '__is_reactive') return true;
// 收集依赖
track(obj, key);
// 递归处理嵌套对象
const res = Reflect.get(obj, key, receiver);
return isObject(res) ? reactive(res) : res;
},
set(obj, key, value, receiver) {
const oldValue = obj[key];
const success = Reflect.set(obj, key, value, receiver);
// 只有值变化且设置成功时才触发更新(避免重复触发)
if (success && oldValue !== value) {
trigger(obj, key);
}
return success;
},
// 其他拦截操作如 deleteProperty 等
});
return proxy;
}
2. 依赖收集(track)
let activeEffect = null; // 当前运行的 effect
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); // 将当前 effect 存入依赖集合
}
3. 触发更新(trigger)
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const dep = depsMap.get(key);
if (dep) {
dep.forEach(effect => effect()); // 执行所有关联的 effect
}
}
4. 副作用管理(effect 函数)
function effect(fn) {
const wrapper = () => {
activeEffect = wrapper;
fn(); // 执行副作用函数,触发依赖收集
activeEffect = null;
};
wrapper(); // 立即执行一次以收集依赖
return wrapper;
}
关键技术细节
- 递归代理:
get
拦截中递归调用reactive
,实现嵌套对象的懒代理。 - 性能优化:只有实际被访问的属性会创建代理,避免初始化时全量递归的性能损耗。
- 动态追踪:能自动检测对象属性新增/删除操作,彻底解决 Vue2 的
Vue.set
痛点。 - 避免重复代理:通过
WeakMap
缓存已代理对象,避免重复生成 Proxy。
与 Vue2 的核心差异
特性 | Vue2 (Object.defineProperty ) |
Vue3 (Proxy ) |
---|---|---|
对象类型支持 | 对象/已有属性 | 对象/数组/Map /Set 等 |
动态属性追踪 | 需手动处理(Vue.set ) |
自动追踪 |
数组修改 | 需重写方法 | 直接拦截数组索引操作 |
性能 | 初始化全量递归 | 按需懒代理 |
实现复杂度 | 较高(分离对象/数组逻辑) | 更统一简洁 |
使用示例
const state = reactive({ count: 0, list: [] });
// 定义副作用
effect(() => {
console.log(`Count is: ${state.count}`);
});
state.count++; // 触发 effect 执行 -> 输出 "Count is: 1"
state.list.push('item'); // 自动追踪数组变化
边界处理
// 1. 判断是否是响应式对象
function isReactive(obj) {
return !!obj?.__is_reactive;
}
// 2. 避免代理只读对象
const readonlyObj = { val: 'readonly' };
Object.freeze(readonlyObj);
reactive(readonlyObj); // 不会被代理
// 3. 代理嵌套对象
const obj = reactive({
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南