• 【Vue3 源码解析】computed


    export function computed<T>(
      getter: ComputedGetter<T>,
      debugOptions?: DebuggerOptions
    ): ComputedRef<T>
    export function computed<T>(
      options: WritableComputedOptions<T>,
      debugOptions?: DebuggerOptions
    ): WritableComputedRef<T>
    export function computed<T>(
      getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
      debugOptions?: DebuggerOptions,
      isSSR = false
    ) {
      // 格式化参数
      let getter: ComputedGetter<T>
      let setter: ComputedSetter<T>
    
      // 如果传过来的是一个函数(即函数式写法),那么就是只读的
      const onlyGetter = isFunction(getterOrOptions)
      if (onlyGetter) {
        getter = getterOrOptions  // 我们传过来的函数赋值给 getter
        setter = __DEV__  // 不支持 setter, 否则会报错
          ? () => {
              console.warn('Write operation failed: computed value is readonly')
            }
          : NOOP
      } else {  // 选项式写法 可读可写
        getter = getterOrOptions.get  
        setter = getterOrOptions.set
      }
    
      // 将 getter 和 setter 传入这个类
      const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR)
    
      if (__DEV__ && debugOptions && !isSSR) {
        cRef.effect.onTrack = debugOptions.onTrack
        cRef.effect.onTrigger = debugOptions.onTrigger
      }
    
      return cRef as any
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    这段代码是 computed 函数内部的逻辑,用于根据传入的参数 getterOrOptions 创建计算属性的 gettersetter,然后创建一个 ComputedRefImpl 实例并返回。

    以下是代码的详细解释:

    1. 首先,定义了 gettersetter 变量,用于后续存储计算属性的获取和设置函数。

    2. 通过检查 getterOrOptions 是否为函数来判断是函数式写法还是选项式写法。函数式写法表示计算属性是只读的,因此 getterOrOptions 直接赋值给 getter 变量,而 setter 变量则被设置为一个函数。这个函数用于在开发环境下报错,提示用户不能对只读的计算属性进行写入操作。

      // 只读的计算属性,直接使用传入的 getter 函数
      if (onlyGetter) {
        getter = getterOrOptions;  // 传入的函数作为 getter
        setter = __DEV__  // 如果是开发环境,设置 setter 为一个警告函数
          ? () => {
              console.warn('Write operation failed: computed value is readonly')
            }
          : NOOP;  // 否则设置为空函数 NOOP
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    3. 如果不是函数式写法,表示计算属性是可读可写的。此时,从传入的 getterOrOptions 对象中提取 getset 函数,分别赋值给 gettersetter 变量。

      } else {  // 选项式写法,可以读写
        getter = getterOrOptions.get;  // 从选项对象中提取 getter
        setter = getterOrOptions.set;  // 从选项对象中提取 setter
      }
      
      • 1
      • 2
      • 3
      • 4
    4. 创建 ComputedRefImpl 实例 cRef,将 gettersetter、只读标志以及 isSSR 参数传入。cRef 代表了最终的计算属性对象。

      // 创建 ComputedRefImpl 实例
      const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR);
      
      • 1
      • 2
    5. 在开发环境下,如果传入了调试选项 debugOptions 并且不是服务端渲染(isSSRfalse),则将调试选项中的 onTrackonTrigger 回调函数分别赋值给 cRef.effect.onTrackcRef.effect.onTrigger,用于跟踪计算属性的依赖和触发情况。

      if (__DEV__ && debugOptions && !isSSR) {
        cRef.effect.onTrack = debugOptions.onTrack;
        cRef.effect.onTrigger = debugOptions.onTrigger;
      }
      
      • 1
      • 2
      • 3
      • 4
    6. 最后,将创建的 cRef 对象返回,它包含了计算属性的值、获取和设置函数等信息。

      return cRef as any;
      
      • 1

    总之,这段代码是 computed 函数内部的逻辑,用于根据传入的参数创建计算属性的 gettersetter 函数,并最终创建一个 ComputedRefImpl 实例,返回计算属性对象。根据不同的写法和配置,可以创建只读或可读可写的计算属性。

    然后我们具体看一下 ComputedRefImpl 实例:

    export class ComputedRefImpl<T> {
      public dep?: Dep = undefined
    
      private _value!: T
      public readonly effect: ReactiveEffect<T>
    
      public readonly __v_isRef = true
      public readonly [ReactiveFlags.IS_READONLY]: boolean = false
    
      public _dirty = true  // 看是否是脏的,是否需要重新计算
      public _cacheable: boolean
    
      constructor(
        getter: ComputedGetter<T>,
        private readonly _setter: ComputedSetter<T>,
        isReadonly: boolean,
        isSSR: boolean
      ) {
        // 创建 ReactiveEffect 对象,用于管理计算属性的响应式依赖
        // 依赖变化并且脏值为 false ,依赖变化这个函数才会执行
        this.effect = new ReactiveEffect(getter, () => {
          if (!this._dirty) {
            this._dirty = true
            triggerRefValue(this)
          }
        })
    
        // 将计算属性与 ReactiveEffect 对象关联
        this.effect.computed = this
        this.effect.active = this._cacheable = !isSSR // 根据是否在服务器端渲染设置是否缓存计算结果
        this[ReactiveFlags.IS_READONLY] = isReadonly // 标记是否为只读计算属性
      }
    
      // 收到脏值为 true ,直接执行 get value 函数
      get value() {
        // 计算属性的获取方法,用于获取计算属性的值
        const self = toRaw(this) // 获取原始的计算属性对象,脱离 Proxy 代理
        trackRefValue(self) // 跟踪计算属性的引用
        if (self._dirty || !self._cacheable) {
          // 如果属性标记为脏(需要重新计算)或者不可缓存,则重新计算属性值
          self._dirty = false
          self._value = self.effect.run()! // 运行计算属性的 effect 函数以计算新值
        }
        return self._value
      }
    
      set value(newValue: T) {
        // 计算属性的设置方法,用于设置计算属性的值
        this._setter(newValue) // 调用用户提供的 setter 函数
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    这段代码定义了 ComputedRefImpl 类,该类是 Vue 3 中计算属性(computed)的实现的一部分。

    在这里插入图片描述

    对代码的主要解释如下:

    1. ComputedRefImpl 类用于实现计算属性。它接受以下参数:

      • getter: 计算属性的获取函数,用于计算属性的值。
      • _setter: 计算属性的设置函数,用于设置计算属性的值。
      • isReadonly: 一个布尔值,表示计算属性是否只读。
      • isSSR: 一个布尔值,表示是否在服务器端渲染。
    2. 在构造函数中,创建了一个 ReactiveEffect 对象,该对象将 getter 函数传递给它,并定义了一个回调函数。当计算属性的依赖发生变化时,这个回调函数将被触发。回调函数会将 _dirty 属性设置为 true,表示计算属性需要重新计算,并调用 triggerRefValue(this),通知相关依赖进行更新。

    3. 将创建的 ReactiveEffect 对象与当前的 ComputedRefImpl 实例关联,以便后续进行管理和跟踪。

    4. 根据传入的参数,设置 effectactive 属性和 _cacheable 属性。active 表示是否需要在计算属性失活时停止计算,_cacheable 表示是否可以缓存计算结果。

    5. 定义 get value() 方法,用于获取计算属性的值。在获取属性值之前,会调用 trackRefValue(self) 来跟踪计算属性的引用。如果计算属性被标记为脏(需要重新计算)或者不可缓存,将重新计算属性值,并将 _dirty 设置为 false,以表示属性值已经计算完毕。

    6. 定义 set value(newValue: T) 方法,用于设置计算属性的值。它会调用用户提供的 _setter 函数来进行属性值的设置。

    总之,这段代码实现了计算属性的核心逻辑,包括属性值的获取和设置,以及属性值的计算和缓存管理。计算属性可以根据其依赖自动更新,并且可以选择是否设置为只读。

  • 相关阅读:
    【python自动化】Playwright基础教程(七)Keyboard键盘
    Spring编程常见错误50例-Spring Bean定义常见错误
    考研操作系统-1.计算机系统概述
    认识物联网
    基于Android系统PJSIP库植入g729编码
    一个C++基类的封装
    『从零开始学小程序』媒体组件video组件
    Mysql JDBC 编程
    Kubernetes和Docker对不同OS和CPU架构的适配关系
    并查集解析
  • 原文地址:https://blog.csdn.net/XiugongHao/article/details/133206268