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({
posted @   木燃不歇  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示