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 分钟前
Vue3入门-计算属性+监听器
前端·javascript·vue.js·vue·js
拾光拾趣录16 分钟前
WebSocket:断线、心跳与重连
前端·websocket
阿眠39 分钟前
vue3实现web端和小程序端个人签名
前端·小程序·apache
哎呦薇1 小时前
从开发到发布:手把手教你将Vue组件上传npm
前端·vue.js
Z7676_1 小时前
静态路由技术
服务器·前端·javascript
慧一居士1 小时前
npm 和 npx 区别对比
前端
用户3802258598241 小时前
vue3源码解析:生命周期
前端·vue.js·源码阅读
遂心_1 小时前
前端路由进化论:从传统页面到React Router的SPA革命
前端·javascript
前端菜鸟杂货铺1 小时前
前端首屏优化及可实现方法
前端
遂心_1 小时前
React Fragment与DocumentFragment:提升性能的双剑合璧
前端·javascript·react.js