【Vue3源码学习】— CH2.2 baseHandlers详解

本文详细解读了Vue3响应式系统中BaseReactiveHandler的核心作用,涉及get操作的依赖追踪、自动解包以及Proxy的使用。通过实例展示了在reactiveObject.a属性访问时的处理过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在上一篇我们中,我们通过createReactiveObject方法创建并返回了一个proxy对象,这个方法里的一个参数是:baseHandlers。
本文将深入探讨baseHandlers.ts文件中定义的处理逻辑,这些逻辑是Vue 3响应式系统能够实现其强大功能的关键。

1. BaseReactiveHandler: 响应式系统的核心

BaseReactiveHandler是Vue响应式系统中所有处理器的基础,提供了处理普通对象和数组代理行为的基本逻辑。这些处理器利用Proxy来拦截对对象的操作,比如属性访问、设置、枚举和函数调用等,以实现响应式特性。

2. get操作: 依赖追踪与自动解包

当通过代理对象访问属性时,get方法会被触发。这个过程不仅涉及到依赖追踪(track),以便于在属性值变化时通知相关的副作用重新执行,也处理了ref类型的自动解包。此外,对于嵌套对象,Vue会递归地返回其响应式或只读代理,确保整个对象树的响应式完整性。

3. 源代码解析

class BaseReactiveHandler implements ProxyHandler<Target> {
  /**
   * 构造函数接收两个参数:_isReadonly和_isShallow
   * 分别表示代理对象是否为只读和是否为浅响应式
   * 这两个属性将影响处理器如何响应对代理对象的操作
   */
  constructor(
    protected readonly _isReadonly = false,
    protected readonly _isShallow = false,
  ) {}
  /**
   * get方法是ProxyHandler接口中的一个方法,用来拦截对代理对象属性的读取操作
   * 接受三个参数:
   * target:代表原始对象,即你通过reactive函数转换成响应式对象之前的那个原始对象。
   * key:代表正在被访问的属性名,比如在reactiveObject.a的访问中,key就是"a"。
   * receiver:代表代理对象或者继承了代理对象的某个对象,最常见的情况是代理对象本身。
   */
  get(target: Target, key: string | symbol, receiver: object) {
    const isReadonly = this._isReadonly,
        isShallow = this._isShallow;
    /**
     * 特殊标志属性处理
     * 当访问的是Vue内部定义的特殊标志(ReactiveFlags)时,根据代理对象的状态返回相应的布尔值
     */
    if (key === ReactiveFlags.IS_REACTIVE) {
      //检查是否为响应式:如果当前代理对象是响应式的,返回!isReadonly
      return !isReadonly
    } else if (key === ReactiveFlags.IS_READONLY) {
      //检查是否为只读:如果当前代理对象是只读的,返回isReadonly
      return isReadonly
    } else if (key === ReactiveFlags.IS_SHALLOW) {
      //检查是否为浅层响应式:如果访问的是浅层响应式,返回isShallow
      return isShallow
    } else if (key === ReactiveFlags.RAW) {
      //获取原始对象:当访问ReactiveFlags.RAW时,意图是获取代理对象背后的原始对象
      if (
        //判断receiver是否等于代理映射中的target
        receiver ===
          (isReadonly
            ? isShallow
              ? shallowReadonlyMap
              : readonlyMap
            : isShallow
              ? shallowReactiveMap
              : reactiveMap
          ).get(target) ||
        /**
         * 检查target和receiver是否有相同的原型:
         * 如果它们的原型相同,这通常意味着receiver是对target的一个用户定义的代理,或者两者通过某种方式相关联,但本质上仍然代表同一个对象或对象结构。
         * 在这种情况下,即使receiver不直接是响应式系统内部维护的代理映射中的对象,也应该将对ReactiveFlags.RAW属性的访问视为对原始对象的合法访问,并返回target。
         */
        Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)
      ) {
        return target
      }
      return
    }
   
   /**
    * 其他情况:普通属性访问逻辑
    * 当访问的不是这些内部状态标志时,就会按照普通的属性访问来处理,进行如依赖追踪(track)和解包ref值等操作
    */

    //当访问的键是一个数组操作或hasOwnProperty方法时,可能会通过arrayInstrumentations进行特殊处理,以确保响应式系统的正确行为
    const targetIsArray = isArray(target)

    if (!isReadonly) {
      if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
        return Reflect.get(arrayInstrumentations, key, receiver)
      }
      if (key === 'hasOwnProperty') {
        return hasOwnProperty
      }
    }

    //获取属性值:通过ES6的Reflect API,更安全、更规范地进行元编程操作,如属性访问
    const res = Reflect.get(target, key, receiver)

    if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
      return res
    }

    //依赖追踪:如果访问的不是特殊键,且对象不是只读的,会进行依赖追踪(track),以便在属性值变化时触发更新
    if (!isReadonly) {
      track(target, TrackOpTypes.GET, key)
    }

    if (isShallow) {
      return res
    }

    //自动解包ref:如果访问的属性值是一个ref,并且不是数组的索引访问,会自动解包ref的值
    if (isRef(res)) {
      return targetIsArray && isIntegerKey(key) ? res : res.value
    }

    //递归响应式处理:如果属性值是一个对象,并且不是只读的且不是浅层响应式的,则会递归地将该对象也转换为响应式或只读
    if (isObject(res)) {
      return isReadonly ? readonly(res) : reactive(res)
    }
    return res
  }
}

4. 源码流程图

在这里插入图片描述

5. 示例:reactiveObject.a 获取属性时发生了什么?

const original = { a: 1 };
const reactiveObject = reactive(original);
console.log(reactiveObject.a);// 输出:1

5.1 Proxy代理拦截

当尝试访问reactiveObject.a时,Proxy拦截器捕获到这一操作,并调用相应的get方法。

5.2 调用get方法

Proxy根据配置的处理器(例如BaseReactiveHandler),调用get方法来处理这次访问,传入的参数分别是原始对象(target),属性名(“a”,即key),和代理对象本身(receiver)。

5.3 get方法执行

get方法根据这些参数执行相应的逻辑,如检查访问的是否是特殊标志属性、执行依赖追踪、返回属性值等:

5.3.1 响应式标志检查

首先检查是否访问的是特殊的响应式标志属性:不是

5.3.2 a属性是一个普通值

使用Reflect.get从原始对象中获取请求的属性值 :1

5.3.3依赖追踪(track)

尽管a不是ref,但是访问reactiveObject.a时,Vue的响应式系统仍然会对这次属性访问进行依赖追踪(通过track函数)

5.3.4 返回属性值1

6. 总结

通俗点来说,这里定义了我们在vue里,对一个对象的属性访问时,vue内部会执行的逻辑。在下一篇,我们将继续学习继承BaseReactiveHandler的MutableReactiveHandler是如何实现set、has等更多方法。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值