【Vue3源码学习】响应式源码解析:reactive、effect、ref

源码版本 Vue3.2.24

废话不多说,直接开始!!!

reactive响应式

源码地址:packages/reactivity/reactive.ts

先看一下在 Vue3 中定义的几个用来标记目标对象 target 的类型的ReactiveFlags,下面先是枚举的属性

export const enum ReactiveFlags {
  SKIP = '__v_skip',
  IS_REACTIVE = '__v_isReactive',
  IS_READONLY = '__v_isReadonly',
  RAW = '__v_raw'
}

export interface Target {
  [ReactiveFlags.SKIP]?: boolean        // 不做响应式处理的数据
  [ReactiveFlags.IS_REACTIVE]?: boolean // target 是否是响应式
  [ReactiveFlags.IS_READONLY]?: boolean // target 是否是只读
  [ReactiveFlags.RAW]?: any             // 表示 proxy 对应的源数据,target 已经是 proxy 对象时会有该属性
}
reactive()

对非只读数据调用 createReactiveObject()创建响应式

export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version. 如果目标对象是一个只读的响应数据,则直接返回目标对象
  if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
    return target
  }
  // 否则调用  createReactiveObject 创建 observe
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap
  )
}

这里代码很简单,主要就是调用 createReactiveObject()

createReactiveObject()
/**
 * @description: 
 * @param {target} 目标对象
 * @param {isReadonly} 是否只读
 * @param {baseHandlers} 基本类型的handlers 
 * @param {collectionHandlers} 主要针对(set、map、weakSet、weakMap)的 handlers 
 */
function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>,
  proxyMap: WeakMap<Target, any>
) {
  // typeof 不是 object 类型的,在开发模式抛出警告,生产环境直接返回目标对象
  if (!isObject(target)) {
    if (__DEV__) {
      console.warn(`value cannot be made reactive: ${String(target)}`)
    }
    return target
  }
  // 已经经是响应式的就直接返回(取ReactiveFlags.RAW 属性会返回true,因为进行reactive的过程中会用weakMap进行保存,通过target能判断出是否有ReactiveFlags.RAW属性)
  // 例外:对reactive对象进行readonly()
  if (
    target[ReactiveFlags.RAW] &&
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target
  }
  // target already has corresponding Proxy 对已经Proxy的,则直接从WeakMap数据结构中取出这个Proxy对象
  const existingProxy = proxyMap.get(target)
  if (existingProxy) {
    return existingProxy
  }
  // only a whitelist of value types can be observed. 只对targetTypeMap类型白名单中的类型进行响应式处理
  const targetType = getTargetType(target)
  if (targetType === TargetType.INVALID) {
    return target
  }
  const proxy = new Proxy(  // proxy代理target
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )
  proxyMap.set(target, proxy)  // 添加到 map 里
  return proxy
}

大概了解了这个方法里要做的事,可以看出一个很重要的东西,就是proxy的第二个参数 handlers。其中baseHandlers 处理数组,对象,collectionHandlers处理 MapSetWeakMapWeakSet

在 basehandlers 中包含了四种 handler

  • mutableHandlers 可变处理
  • readonlyHandlers 只读处理
  • shallowReactiveHandlers 浅观察处理(只观察目标对象的第一层属性)
  • shallowReadonlyHandlers 浅观察 && 只读处理

其中 readonlyHandlersshallowReactiveHandlersshallowReadonlyHandlers 都是 mutableHandlers 的变形版本。

所以接下来只看mutableHandlers的具体实现。

mutableHandlers()

源码地址:packages/reactivity/src/baseHandlers.ts

mutableHandlers 定义是这样的

const get = /*#__PURE__*/ createGetter()
const set = /*#__PURE__*/ createSetter()
export const mutableHandlers: ProxyHandler<object> = {
  get,            // 用于拦截对象的读取属性操作
  set,            // 用于拦截对象的设置属性操作
  deleteProperty, // 用于拦截对象的删除属性操作
  has,            // 检查一个对象是否拥有某个属性
  ownKeys         // 针对 getOwnPropertyNames,  getOwnPropertySymbols, keys 的代理方法
}

这里:

  • get has ownKeys 会触发依赖收集 track()
  • set deleteProperty 会触发更新 trigger()

其中有两个重要的方法就是 getset 对应的 createGettercreateSetter

createGetter()
/**
 * @description: 用于拦截对象的读取属性操作
 * @param {isReadonly} 是否只读 
 * @param {shallow} 是否浅观察  
 */
function createGetter(isReadonly = false, shallow = false) {
   /**
   * @description: 
   * @param {target} 目标对象
   * @param {key} 需要获取的值的键值
   * @param {receiver} 如果遇到 setter,receiver则为setter调用时的this值 
   */
  return function get(target: Target, key: string | symbol, receiver: object) {
    //  ReactiveFlags 是在reactive中声明的枚举值,如果key是枚举值则直接返回对应的布尔值
    if (key === ReactiveFlags.IS_REACTIVE) {
      return !isReadonly
    } else if (key === ReactiveFlags.IS_READONLY) {
      return isReadonly
    } else if (
      // 如果key是raw  receiver 指向调用者,则直接返回目标对象。
      // 这里判断是为了保证触发拦截 handle 的是 proxy 本身而不是 proxy 的继承者
      // 触发拦的两种方式:一是访问 proxy 对象本身的属性,二是访问对象原型链上有 proxy 对象的对象的属性,因为查询会沿着原型链向下找
      key === ReactiveFlags.RAW &&
      receiver ===
        (isReadonly
          ? shallow
            ? shallowReadonlyMap
            : readonlyMap
          : shallow
            ? shallowReactiveMap
            : reactiveMap
        ).get(target)
    ) {
      return target
    }

    const targetIsArray = isArray(target)
    // 如果目标对象 不为只读、是数组、key属于arrayInstrumentations:['includes', 'indexOf', 'lastIndexOf']方法之一,即触发了这三个方法之一
    if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
      // 通过 proxy 调用,arrayInstrumentations[key]的this一定指向 proxy
      return Reflect.get(arrayInstrumentations, key, receiver)
    }

    const res = Reflect.get(target, key, receiver)

    // 如果 key 是 symbol 内置方法,或者访问的是原型对象__proto__,直接返回结果,不收集依赖
    if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
      return res
    }

    // 不是只读类型的 target 就收集依赖。因为只读类型不会变化,无法触发 setter,也就会触发更新
    if (!isReadonly) {
      track(target, TrackOpTypes.GET, key) // 收集依赖,存储到对应的全局仓库中
    }

    // 如果是浅观察,不做递归转化,就是说对象有属性值还是对象的话不递归调用 reactive()
    if (shallow) {
      return res
    }

    // 如果get的结果是ref
    if (isRef(res)) {
      // ref unwrapping - does not apply for Array + integer key.
      // 返回 ref.value,数组除外
      const shouldUnwrap = !targetIsArray || !isIntegerKey(key)
      return shouldUnwrap ? res.value : res
    }
    // 由于 proxy 只能代理一层,如果子元素是对象,需要递归继续代理
    if (isObject(res)) {
      // Convert returned value into a proxy as well. we do the isObject check
      // here to avoid invalid value warning. Also need to lazy access readonly
      // and reactive here to avoid circular dependency.
      return isReadonly ? readonly(res) : reactive(res)
    }

    return res
  }
}

track() 依赖收集放到后面,和派发更新一起

createSetter()

源码地址:packages/reactivity/src/baseHandlers.ts

/**
 * @description: 拦截对象的设置属性操作 
 * @param {shallow} 是否是浅观察 
 */
function createSetter(shallow = false) {
    /**
   * @description: 
   * @param {target} 目标对象
   * @param {key} 设置的属性的名称
   * @param {value} 要改变的属性值 
   * @param {receiver} 如果遇到 setter,receiver则为setter调用时的this值 
   */
  return function set(
    target: object,
    key: string | symbol,
    value: unknown,
    receiver: object
  ): boolean {
    let oldValue = (target as any)[key]
    // 如果模式不是浅观察
    if (!shallow) {
      // 拿新值和老值的原始值,因为新传入的值可能是响应式数据,如果直接和 target 上原始值比较是没有意义的
      value = toRaw(value)
      oldValue = toRaw(oldValue)
       // 目标对象不是数组,旧值是ref,新值不是ref,则直接赋值,注意这里提到ref
      if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
        oldValue.value = value
        return true
      }
    } else {
      // in shallow mode, objects are set as-is regardless of reactive or not
    }
    // 检查对象是否有这个属性
    const hadKey =
      isArray(target) && isIntegerKey(key)
        ? Number(key) < target.length
        : hasOwn(target, key)
    // 赋值
    const result = Reflect.set(target, key, value, receiver)
    // don't trigger if target is something up in the prototype chain of original 
    // receiver 是 proxy 实例才派发更新,防止通过原型链触发拦截器触发更新
    if (target === toRaw(receiver)) {
      if (!hadKey) {
        // 如是不存在则trigger ADD
        trigger(target, TriggerOpTypes.ADD, key, value)
      } else if (hasChanged(value, oldValue)) {
        // 如果新旧值不相等则trigger SET
        trigger(target, TriggerOpTypes.SET, key, value, oldValue)
      }
    }
    return result
  }
}

trigger() 派发更新放到后面

deleteProperty()
/**
 * @description: 用于拦截对象的删除属性操作 
 * @param {target} 目标对象 
 * @param {key} 键值 
 * @return {Boolean}
 */
function deleteProperty(target: object, key: string | symbol): boolean {
  // 检查一个对象是否包含当前key
  const hadKey = hasOwn(target, key)
  const oldValue = (target as any)[key]
  // Reflect 作用在于完成目标对象的默认,这里即指删除
  const result = Reflect.deleteProperty(target, key)
  // 如果该值被成功删除则调用 trigger(trigger 为 effect 里的方法,effect 为 reactive 的核心)
  if (result && hadKey) {
    trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
  }
  return result
}
has()&&ownKeys
/**
 * @description: 检查一个对象是否拥有某个属性 
 * @param {target} 目标对象 
 * @param {key} 键值 
 * @return {Boolean}
 */
function has(target: object, key: string | symbol): boolean {
  const result = Reflect.has(target, key)
  if (!isSymbol(key) || !builtInSymbols.has(key)) {
    // track 也为 effect 里的方法
    track(target, TrackOpTypes.HAS, key)
  }
  return result
}
// 返回一个由目标对象自身的属性键组成的数组
function ownKeys(target: object): (string | symbol)[] {
  track(target, TrackOpTypes.ITERATE, isArray(target) ? 'length' : ITERATE_KEY)
  return Reflect.ownKeys(target)
}

接下来看一下依赖收集和派发更新的内容。相关代码全部在 effect.ts文件中。

effect 依赖收集和派发更新

源码地址:packages/reactivity/src/effect.ts -145行

主要内容如下:

  • 创建 effect 入口函数
  • track 依赖收集
  • trigger 派发更新
  • cleanupEffect 清除 effect
  • stop 停止effect
  • trackStack 收集栈的暂停(pauseTracking)、恢复(enableTracking)和重置(resetTracking)
effect()

这里主要就是暴露一个创建 effect 的方法

export function effect<T = any>(
  fn: () => T,
  options?: ReactiveEffectOptions
): ReactiveEffectRunner {
  // 如果已经是 effect 函数,就直接重置为原始对象
  if ((fn as ReactiveEffectRunner).effect) {
    fn = (fn as ReactiveEffectRunner).effect.fn
  }

  const _effect = new ReactiveEffect(fn)
  // 创建`effect`
  if (options) {
    extend(_effect, options)
    if (options.scope) recordEffectScope(_effect, options.scope)
  }
  // 如果 lazy 不为真就直接执行一次 effect。计算属性的 lazy 为 true
  if (!options || !options.lazy) {
    _effect.run()
  }
  const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
  runner.effect = _effect
  return runner
}

可以看出主要的就是在 effect 里使用 new ReactiveEffect 创建了一个 _effect 实例,并且函数最后返回的 runner 方法就是指向 ReactiveEffect 里的 run 方法。换句话说,执行 effect 方法时,实际上就是执行 run 方法。
下面看一下 ReactiveEffectrun 方法都干了些什么

ReactiveEffect

这里主要做的就是在依赖收集前用栈数据结构 effectStrack 来做 effect 的执行调试,保证当前 effect 的优先级最高,并及时清除己收集依赖的内存。

需要注意的是标记完成后就会执行 fn() 函数,这个 fn 函数就是副作用函数封闭的函数,如果是在组件渲染,就是 fn 就是组件渲染函数,执行的时候就会就会访问数据,就会触发 target[key]getter,然后触发 track 进行依赖收集,这也就是 Vue3 的依赖收集过程

// 临时存储响应式函数
const effectStack: ReactiveEffect[] = []
// 依赖收集栈
const trackStack: boolean[] = []
// 最大嵌套深度
const maxMarkerBits = 30
export class ReactiveEffect<T = any> {
  active = true
  deps: Dep[] = []

  // can be attached after creation
  computed?: boolean
  allowRecurse?: boolean
  onStop?: () => void
  // dev only
  onTrack?: (event: DebuggerEvent) => void
  // dev only
  onTrigger?: (event: DebuggerEvent) => void

  constructor(
    public fn: () => T,
    public scheduler: EffectScheduler | null = null,
    scope?: EffectScope | null
  ) {
    // effectScope 相关处理,在另一个文件effectScope.ts
    recordEffectScope(this, scope)
  }

  run() {
    if (!this.active) {
      return this.fn()
    }
    // 如果effectStack栈中没有当前的 effect
    if (!effectStack.includes(this)) {
      try {
         // activeEffect 表示当前依赖收集系统正在处理的 effect, 先把当前 effect 设置为全局全局激活的 effect,在 getter 中会收集 activeEffect 持有的 effect
        // 然后入栈
        effectStack.push((activeEffect = this))
        enableTracking()

        // 记录递归深度位数
        trackOpBit = 1 << ++effectTrackDepth
        // 如果 effect 嵌套层数没有超过 30 层,一般超不了    
        if (effectTrackDepth <= maxMarkerBits) {
          // 给依赖打标记,就是遍历 _effect 实例中的 deps 属性,给每个 dep 的 w 属性标记为 trackOpBit 的值
          initDepMarkers(this)
        } else {
          // 超过就 清除当前 effect 相关依赖 通常情况下不会
          cleanupEffect(this)
        }
        // 在执行 effect 函数,比如访问 target[key],会触发 getter
        return this.fn()
      } finally {
        if (effectTrackDepth <= maxMarkerBits) {
          // 完成依赖标记
          finalizeDepMarkers(this)
        }

        // 恢复到上一级
        trackOpBit = 1 << --effectTrackDepth
        // 重置依赖收集状态
        resetTracking()
        // 出栈
        effectStack.pop()
        // 将当前 activeEffect 指向栈最后一个 effect
        const n = effectStack.length
        activeEffect = n > 0 ? effectStack[n - 1] : undefined
      }
    }
  }

  stop() {
    if (this.active) {
      cleanupEffect(this)
      if (this.onStop) {
        this.onStop()
      }
      this.active = false
    }
  }
}
// 每次 effect 运行都会重新收集依赖, deps 是 effect 的依赖数组, 需要全部清空
function cleanupEffect(effect: ReactiveEffect) {
  const { deps } = effect
  if (deps.length) {
    for (let i = 0; i < deps.length; i++) {
      deps[i].delete(effect)
    }
    deps.length = 0
  }
}
track()

track 就是依赖收集器,负责把依赖收集起来统一放到一个依赖管理中心

// targetMap 为依赖管理中心,用于存储响应式函数、目标对象、键之间的映射关系
// 相当于这样
// targetMap(weakmap)={
//    target1(map):{
//      key1(dep):[effect1,effect2]
//      key2(dep):[effect1,effect2]
//    }
// }
// 给每个 target 创建一个 map,每个 key 对应着一个 dep
// 用 dep 来收集依赖函数,监听 key 值变化,触发 dep 中的依赖函数
type KeyToDepMap = Map<any, Dep>
const targetMap = new WeakMap<any, KeyToDepMap>()
export function isTracking() {
  return shouldTrack && activeEffect !== undefined
}

/**
 * @description: 
 * @param {target} 目标对象 
 * @param {type} 收集的类型,  函数的定义在下方 
 * @param {key} 触发 track 的 object 的 key 
 */
export function track(target: object, type: TrackOpTypes, key: unknown) {
  // 如果当前没有激活 effect,就不用收集
  if (!isTracking()) {
    return
  }
 
  // targetMap 依赖管理中心,用于收集依赖和触发依赖
  // 检查targetMap中有没有当前target
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    // 没有则新建一个
    targetMap.set(target, (depsMap = new Map()))
  }
  // deps 来收集依赖函数,当监听的 key 值发生变化时,触发 dep 中的依赖函数
  let dep = depsMap.get(key)
  if (!dep) {
    depsMap.set(key, (dep = createDep()))
  }
  // 开发环境会触发onTrack, 仅用于调试
  const eventInfo = __DEV__
    ? { effect: activeEffect, target, type, key }
    : undefined

  trackEffects(dep, eventInfo)
}
trackEffects()

这里把当前激活的 effect 收集进对应的 effect集合,也就是 dep

这里了解一下两个标识符:

  • dep.n:n 是 newTracked 的缩写,表示是否是最新收集的(是否当前层)
  • dep.w:w 是 wasTracked 的缩写,表示是否已经被收集,避免重复收集
/**
 * @description: 
 * @param {dep} dep依赖收集器 
 * @param {debuggerEventExtraInfo} 
 */
export function trackEffects(
  dep: Dep,
  debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
  let shouldTrack = false
  // 如果 effect 嵌套层数没有超过 30 层
  if (effectTrackDepth <= maxMarkerBits) {
    if (!newTracked(dep)) {
      dep.n |= trackOpBit // set newly tracked  标记新依赖
      shouldTrack = !wasTracked(dep)
    }
  } else {
    // Full cleanup mode. 嵌套超限,就切换清除依赖模式
    shouldTrack = !dep.has(activeEffect!)
  }
  // 如果可以收集
  if (shouldTrack) {
    // 收集当前激活的 effect 作为依赖
    dep.add(activeEffect!)
    // 当前激活的 effect 收集 dep 集合
    activeEffect!.deps.push(dep)
    // 开发环境下触发 onTrack 事件
    if (__DEV__ && activeEffect!.onTrack) {
      activeEffect!.onTrack(
        Object.assign(
          {
            effect: activeEffect!
          },
          debuggerEventExtraInfo
        )
      )
    }
  }
}
trigger()

triggertrack 收集的依赖对应的触发器,也就是负责根据映射关系,获取响应式函数,再派发通知 triggerEffects 进行更新

export function trigger(
  target: object,
  type: TriggerOpTypes,
  key?: unknown,
  newValue?: unknown,
  oldValue?: unknown,
  oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
  const depsMap = targetMap.get(target)
  // 依赖管理中没有, 代表没有收集过依赖,直接返回
  if (!depsMap) {
    // never been tracked
    return
  }

  let deps: (Dep | undefined)[] = []
  // 触发trigger 的时候传进来的类型是清除类型
  if (type === TriggerOpTypes.CLEAR) {
    // collection being cleared
    // trigger all effects for target
    deps = [...depsMap.values()]
  } else if (key === 'length' && isArray(target)) {
    // 如果是数组类型的,并且是数组的 length 改变时
    depsMap.forEach((dep, key) => {
      // 如果数组长度变短时,需要做已删除数组元素的 effects 和 trigger
      // 也就是索引号 >= 数组最新的length的元素们对应的 effects,要将它们添加进队列准备清除
      if (key === 'length' || key >= (newValue as number)) {
        deps.push(dep)
      }
    })
  } else {
    // schedule runs for SET | ADD | DELETE
    // 如果 key 不是 undefined,就添加对应依赖到队列,比如新增、修改、删除
    if (key !== void 0) {
      deps.push(depsMap.get(key))
    }

    // also run for iteration key on ADD | DELETE | Map.SET
    switch (type) {
      case TriggerOpTypes.ADD:
        if (!isArray(target)) {
          // 往队列中添加关联的所有依赖,准备清除
          deps.push(depsMap.get(ITERATE_KEY))
          if (isMap(target)) {
            deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
          }
        } else if (isIntegerKey(key)) {
          // new index added to array -> length changes
          deps.push(depsMap.get('length'))
        }
        break
      case TriggerOpTypes.DELETE:
        if (!isArray(target)) {
          deps.push(depsMap.get(ITERATE_KEY))
          if (isMap(target)) {
            deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
          }
        }
        break
      case TriggerOpTypes.SET:
        if (isMap(target)) {
          deps.push(depsMap.get(ITERATE_KEY))
        }
        break
    }
  }

  // 到这里就拿到了 targetMap[target][key],并存到 deps 里
  // 接着是要将对应的 effect 取出,调用 triggerEffects 执行

  // 判断开发环境,传入eventInfo
  const eventInfo = __DEV__
    ? { target, type, key, newValue, oldValue, oldTarget }
    : undefined

  if (deps.length === 1) {
    if (deps[0]) {
      if (__DEV__) {
        triggerEffects(deps[0], eventInfo)
      } else {
        triggerEffects(deps[0])
      }
    }
  } else {
    const effects: ReactiveEffect[] = []
    for (const dep of deps) {
      if (dep) {
        effects.push(...dep)
      }
    }
    if (__DEV__) {
      triggerEffects(createDep(effects), eventInfo)
    } else {
      triggerEffects(createDep(effects))
    }
  }
}
triggerEffects()

源码地址:packages/reactivity/src/effect.ts -330行

执行 effect 函数,也就是『派发更新』中的更新了

export function triggerEffects(
  dep: Dep | ReactiveEffect[],
  debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
  // spread into array for stabilization 遍历 effect 的集合函数
  for (const effect of isArray(dep) ? dep : [...dep]) {
    /** 
      这里判断 effect !== activeEffect的原因是:不能和当前effect 相同
      比如:count.value++,如果这是个effect,会触发getter,track收集了当前激活的 effect,
      然后count.value = count.value+1 会触发setter,执行trigger,
      就会陷入一个死循环,所以要过滤当前的 effect
    */
    if (effect !== activeEffect || effect.allowRecurse) {
      if (__DEV__ && effect.onTrigger) {
        effect.onTrigger(extend({ effect }, debuggerEventExtraInfo))
      }
      // 如果有 scheduler 就执行,计算属性有 scheduler
      if (effect.scheduler) {
        effect.scheduler()
      } else {
        effect.run() // 执行 effect 函数
      }
    }
  }
}

创建 ref

源码地址:packages/reactivity/src/ref.ts

直接结合源码来看,ref 接收一个可选的 unknown 做为入参,接着直接调用 createRefcreateRef 先判断 value 是否已经是一个 ref, 如果是则直接返回,否则 new RefImpl() 创建的。

// 判断是不是 ref
export function isRef(r: any): r is Ref {
  return Boolean(r && r.__v_isRef === true)
}

export function ref(value?: unknown) {
  return createRef(value, false)
}

/**
 * @description: 创建 ref
 * @param {rawValue} 原始值 
 * @param {shallow} 是否是浅观察 
 */
function createRef(rawValue: unknown, shallow: boolean) {
  if (isRef(rawValue)) { // 如果已经是 ref 就直接返回
    return rawValue
  }
  // 调用 RefImpl 创建并返回 ref
  return new RefImpl(rawValue, shallow)
}
// 创建一个浅层 ref
export function shallowRef(value?: unknown) {
  return createRef(value, true)
}
// 卸载一个 ref
export function unref<T>(ref: T | Ref<T>): T {
  return isRef(ref) ? (ref.value as any) : ref
}

RefImpl

从上面我们知道 ref 对象是通过 new RefImpl() 创建的,RefImpl 类的实现比较简单:判断是不是浅观察,如果是浅观察直接构造一个 ref 返回,不是则将 rawValue 转换成 reactive 再构造一个 ref 返回

class RefImpl<T> {
  private _value: T
  private _rawValue: T

  public dep?: Dep = undefined
  public readonly __v_isRef = true  // 每一个 ref 实例下都有一个__v_isRef 的只读属性,标识它是一个 ref

  constructor(value: T, public readonly _shallow: boolean) {
    this._rawValue = _shallow ? value : toRaw(value) // 判断是不是浅比较,如果不是就拿老值
    this._value = _shallow ? value : toReactive(value) // 判断是不是浅比较,如果不是就调toReactive(): 如果是对象就用reactive()包裹,否则返回本身
  }

  get value() {
    trackRefValue(this)  // 进行依赖收集
    return this._value
  }

  set value(newVal) {
    // 如果是浅比较,就取新值,不是就取老值
    newVal = this._shallow ? newVal : toRaw(newVal)
    if (hasChanged(newVal, this._rawValue)) {
      this._rawValue = newVal // 值已更换,重新赋值
      this._value = this._shallow ? newVal : toReactive(newVal)
      triggerRefValue(this, newVal)  // 触发依赖 派发更新
    }
  }
}
trackRefValue()

进行ref 依赖收集的工作:

  • 判断是否激活了 effect;
  • 有没有收集过依赖的 effect,没有就创建一个 dep,准备收集;
  • 然后调用本文上面的 trackEffects 进行依赖收集
export function trackRefValue(ref: RefBase<any>) {
  if (isTracking()) {  // 如果激活了 effect,就收集
    ref = toRaw(ref)
    if (!ref.dep) {  // 如果该属性没有没有收集过依赖函数,就创建一个 dep,用来存放依赖 effect
      ref.dep = createDep()
    }
    if (__DEV__) {
      trackEffects(ref.dep, {
        target: ref,
        type: TrackOpTypes.GET,
        key: 'value'
      })
    } else {
      trackEffects(ref.dep) // 调用本文上面的 trackEffects 收集依赖
    }
  }
}
triggerRefValue()

进行 ref 派发更新,源码比较清晰:区分一下开发环境,然后执行本文上面的 triggerEffects 执行对应在的 effect 函数进行更新

export function triggerRefValue(ref: RefBase<any>, newVal?: any) {
  ref = toRaw(ref)
  if (ref.dep) {
    if (__DEV__) {
      triggerEffects(ref.dep, {
        target: ref,
        type: TriggerOpTypes.SET,
        key: 'value',
        newValue: newVal
      })
    } else {
      triggerEffects(ref.dep)
    }
  }
}

到这里,Vue3 的响应式对象的源码就基本上就看完了

结语

如果本文对你有一丁点帮助,点个赞支持一下吧,感谢感谢

你可能感兴趣的:(#,VUE源码学习记录,javascript,vue,源码,前端)