vue3源码解析:reactive原理

在文中我们分析了 ref 的实现,本节我们来看看另一个核心响应式 API ------ reactive 的实现原理。reactive 主要用于为对象类型创建响应式代理。

实例引入

首先通过一个例子来看看 reactive 的用法:

js 复制代码
import { reactive } from "vue";

// 创建响应式对象
const state = reactive({
  count: 0,
  list: [1, 2, 3],
  user: {
    name: "Tom",
    age: 20,
  },
});

// 直接访问和修改
console.log(state.count); // 0
state.count++;

// 深层响应式
state.user.age = 21;
state.list.push(4);

这个例子展示了 reactive 的特点:

  1. 直接访问和修改属性,不需要 .value
  2. 支持深层响应式转换
  3. 支持复杂数据结构(数组、嵌套对象等)

核心数据结构

1. WeakMap 缓存结构

js 复制代码
// 存储原始对象到代理对象的映射
export const reactiveMap = new WeakMap<Target, any>();
export const shallowReactiveMap = new WeakMap<Target, any>();
export const readonlyMap = new WeakMap<Target, any>();
export const shallowReadonlyMap = new WeakMap<Target, any>();

2. 目标类型枚举

js 复制代码
enum TargetType {
  INVALID = 0, // 无效类型
  COMMON = 1, // 普通对象/数组
  COLLECTION = 2, // 集合类型(Set/Map等)
}

function targetTypeMap(rawType: string) {
  switch (rawType) {
    case "Object":
    case "Array":
      return TargetType.COMMON;
    case "Map":
    case "Set":
    case "WeakMap":
    case "WeakSet":
      return TargetType.COLLECTION;
    default:
      return TargetType.INVALID;
  }
}

3. reactive 工厂函数

js 复制代码
export function reactive<T extends object>(target: T): Reactive<T> {
  // 如果是只读的,直接返回
  if (isReadonly(target)) {
    return target;
  }
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap
  );
}

4. 创建响应式对象

js 复制代码
function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>,
  proxyMap: WeakMap<Target, any>
) {
  // 非对象直接返回
  if (!isObject(target)) {
    return target;
  }

  // 已有代理则返回缓存
  const existingProxy = proxyMap.get(target);
  if (existingProxy) {
    return existingProxy;
  }

  // 获取目标类型
  const targetType = getTargetType(target);
  if (targetType === TargetType.INVALID) {
    return target;
  }

  // 创建代理
  const proxy = new Proxy(
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  );

  // 缓存代理
  proxyMap.set(target, proxy);
  return proxy;
}

4. Proxy 处理器实现

reactive 使用两种处理器来拦截不同类型的操作:

4.1 基础处理器(baseHandlers)
js 复制代码
// 用于普通对象和数组的处理器
export const mutableHandlers: ProxyHandler<object> = {
  get(target: object, key: string | symbol, receiver: object) {
    // 1. 处理特殊 key
    if (key === ReactiveFlags.IS_REACTIVE) {
      return true;
    }

    // 2. 获取原始值
    const res = Reflect.get(target, key, receiver);

    // 3. 依赖收集
    track(target, TrackOpTypes.GET, key);

    // 4. 处理嵌套对象 - 实现深层响应式
    if (isObject(res)) {
      return reactive(res);
    }

    // 5. 解包 ref
    if (isRef(res)) {
      return res.value;
    }

    return res;
  },

  set(target: object, key: string | symbol, value: unknown, receiver: object) {
    // 1. 获取旧值
    const oldValue = (target as any)[key];

    // 2. 设置新值
    const result = Reflect.set(target, key, value, receiver);

    // 3. 触发更新
    if (hasChanged(value, oldValue)) {
      trigger(target, TriggerOpTypes.SET, key, value, oldValue);
    }

    return result;
  },

  // 拦截 in 操作符
  has(target: object, key: string | symbol) {
    const result = Reflect.has(target, key);
    track(target, TrackOpTypes.HAS, key);
    return result;
  },

  // 拦截 delete 操作
  deleteProperty(target: object, key: string | symbol) {
    // 1. 检查属性是否存在且可配置
    const hadKey = hasOwn(target, key);
    const oldValue = (target as any)[key];

    // 2. 执行删除
    const result = Reflect.deleteProperty(target, key);

    // 3. 如果删除成功且属性确实存在,触发更新
    if (result && hadKey) {
      trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue);
    }

    return result;
  },

  // 拦截 Object.keys 等操作
  ownKeys(target: object) {
    track(
      target,
      TrackOpTypes.ITERATE,
      isArray(target) ? "length" : ITERATE_KEY
    );
    return Reflect.ownKeys(target);
  },
};
4.2 集合类型处理器(collectionHandlers)
js 复制代码
// 用于 Map、Set 等集合类型的处理器
export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = {
  get(
    target: CollectionTypes,
    key: string | symbol,
    receiver: CollectionTypes
  ) {
    // 1. 获取集合原型上的方法
    const targetProto = getProto(target);
    const method = targetProto[key];

    // 2. 处理特殊方法
    if (key === "size") {
      track(target, TrackOpTypes.ITERATE, ITERATE_KEY);
      return Reflect.get(target, key, target);
    }

    // 3. 包装集合方法
    return function (...args: unknown[]) {
      // 3.1 执行原始方法
      const result = method.apply(target, args);

      // 3.2 依赖收集
      if (key === "get" || key === "has") {
        track(target, TrackOpTypes.GET, args[0]);
      } else if (key === "add" || key === "set" || key === "delete") {
        // 3.3 触发更新
        trigger(target, TriggerOpTypes.SET, args[0], args[1]);
      }

      return result;
    };
  },
};

这些处理器实现了以下核心功能:

  1. 依赖追踪

    • get/has: 通过 track 收集依赖
    • ownKeys: 追踪迭代相关操作
  2. 触发更新

    • set: 属性值变化时触发更新
    • deleteProperty: 删除属性时触发更新
    • 集合方法: add/set/delete 等操作时触发更新
  3. 深层响应式

    • 访问嵌套对象时递归创建响应式代理
    • 确保深层属性也具有响应式
  4. 特殊处理

    • Ref 自动解包
    • 响应式标记检查
    • 集合类型方法包装
  5. 性能优化

    • 缓存已创建的代理
    • 避免不必要的更新触发

调用流程

让我们通过示例代码来分析 reactive 的完整调用流程:

1. 创建阶段

当执行 const state = reactive({ count: 0 }) 时:

  1. 调用 reactive() 函数

  2. 检查目标对象类型

  3. 通过 createReactiveObject 创建代理:

    • 检查缓存
    • 确定目标类型
    • 创建对应的 Proxy
    • 缓存代理对象

2. 访问阶段

当访问 state.count 时:

  1. 触发 Proxy 的 get 陷阱

  2. 执行 baseHandlers 中的 get:

    • 解包 ref 值
    • 追踪依赖
    • 返回属性值

3. 修改阶段

当执行 state.count++ 时:

  1. 触发 Proxy 的 set 陷阱

  2. 执行 baseHandlers 中的 set:

    • 对比新旧值
    • 设置新值
    • 触发更新

4. 深层响应式

对于嵌套属性(如 state.user.age):

  1. 首次访问 user 属性时递归创建代理
  2. 后续操作都会触发对应层级的响应式处理
  3. 实现深层响应式转换

与 ref 实现的区别

1. 响应式实现方式

ref 的实现:

  • 基于类的 getter/setter
  • 只拦截 .value 属性
  • 内部通过 dep 存储依赖

reactive 的实现:

  • 基于 Proxy 的代理
  • 拦截所有属性操作
  • 每个属性都有自己的 dep

2. 依赖收集

ref:

js 复制代码
class RefImpl {
  get value() {
    this.dep.track();
    return this._value;
  }
}

reactive:

js 复制代码
const mutableHandlers = {
  get(target, key) {
    track(target, TrackOpTypes.GET, key);
    return Reflect.get(target, key);
  },
};

主要区别:

  • ref 只在访问 .value 时收集依赖
  • reactive 在访问任何属性时都会收集依赖
  • reactive 需要为每个属性维护单独的依赖关系

3. 更新触发

ref:

js 复制代码
class RefImpl {
  set value(newVal) {
    if (hasChanged(newVal, this._rawValue)) {
      this._value = newVal;
      this.dep.trigger();
    }
  }
}

reactive:

js 复制代码
const mutableHandlers = {
  set(target, key, value) {
    const oldValue = target[key];
    const result = Reflect.set(target, key, value);
    if (hasChanged(value, oldValue)) {
      trigger(target, TriggerOpTypes.SET, key, value, oldValue);
    }
    return result;
  },
};

主要区别:

  • ref 只需要触发单个 dep 的更新
  • reactive 需要根据具体属性触发对应的更新
  • reactive 支持更多的操作类型(add/delete/set)

4. 嵌套处理

ref:

  • 嵌套对象需要手动使用 reactive 转换
  • 主要关注 .value 的响应式

reactive:

  • 自动递归转换嵌套对象
  • 访问时按需转换(惰性)
  • 支持复杂数据结构的响应式转换

5. 性能考虑

ref:

  • 结构简单,开销小
  • 适合原始值的响应式
  • .value 访问会带来一定开销

reactive:

  • Proxy 拦截带来一定开销
  • 惰性转换减少不必要的代理创建
  • WeakMap 缓存优化性能
  • 适合大型对象的响应式处理

这些实现上的差异也决定了它们的最佳使用场景:

  • ref: 适合单个值的响应式管理
  • reactive: 适合复杂对象的响应式处理

总结

通过分析可以看到,reactive 的实现主要包含以下几个关键点:

  1. 使用 WeakMap 缓存代理对象,避免重复创建
  2. 区分普通对象和集合类型,使用不同的处理器
  3. 通过 Proxy 实现属性的拦截和响应式
  4. 支持深层响应式转换
  5. 自动解包 ref 类型的属性值

这种基于 Proxy 的设计让 reactive 能够提供更完整的响应式能力,特别适合处理复杂的对象数据。

相关推荐
摆烂工程师1 天前
2025年12月最新的 Google AI One Pro 1年会员教育认证通关指南
前端·后端·ai编程
Gavin在路上1 天前
DDD之用事件风暴重构“电商订单履约”(11)
java·前端·重构
我命由我123451 天前
VSCode - VSCode 颜色值快速转换
前端·ide·vscode·前端框架·编辑器·html·js
qq_12498707531 天前
基于SpringBoot+vue的小黄蜂外卖平台(源码+论文+部署+安装)
java·开发语言·vue.js·spring boot·后端·mysql·毕业设计
前端涂涂1 天前
怎么设计一个加密货币 谁有权利发行数字货币 怎么防止double spending attack 怎么验证交易合法性 铸币交易..
前端
JuneTT1 天前
【JS】使用内连配置强制引入图片为base64
前端·javascript
前端涂涂1 天前
4.BTC-协议
前端
老前端的功夫1 天前
移动端兼容性深度解析:从像素到交互的全方位解决方案
前端·前端框架·node.js·交互·css3
代码与野兽1 天前
AI交易,怎么让LLM自己挑选数据源?
前端·javascript·后端
CC码码1 天前
前端文本分割工具,“他”来了
前端·javascript·程序员