IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 【Vue3源码学习】响应式系统API: computed -> 正文阅读

[JavaScript知识库]【Vue3源码学习】响应式系统API: computed

computed的基本使用

computed 是组件的计算属性,它的含义是依赖于其他状态而生成的状态,与响应式紧密相关。

computed 有两种创建方式:

  1. computed 函数传递一个 getter 方法创建 immutable reactive ref object

    const count = ref(1)
    const plusOne = computed(() => count.value + 1)
    console.log(plusOne.value) // 2
    plusOne.value = 3 // error,因为plusOne是immutable ref obj
    
  2. computed 函数传递一个有 getset 方法的对象来创建一个 writable ref object

    const count = ref(1)
    const plusOne = computed({
      get: () => count.value + 1,
      set: val => {
        count.value = val - 1
      }
    })
    plusOne.value = 1
    console.log(count.value) // 0
    

computed原理

computed 的特性就在于能够缓存计算的值(提升性能),只有当 computed 的依赖发生变化时才会重新计算,否则读取 computed 的值则一直是之前的值。computed 是怎么做到的呢?让我们一起来看看源码来解惑。

computed源码

源码地址:packages\reactivity\src\computed.ts

computed创建

computed 函数接收一个 getter 方法或者是一个含有 get 方法和 set 方法的对象,并返回一个 ref 对象。

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
) {
  let getter: ComputedGetter<T>
  let setter: ComputedSetter<T>

  const onlyGetter = isFunction(getterOrOptions)
  // 当getterOrOptions为函数的时候,说明是只读computed,会将其赋值给与getter
  if (onlyGetter) {
    getter = getterOrOptions
    setter = __DEV__
    ? () => {
      console.warn('Write operation failed: computed value is readonly')
    }
    : NOOP
  } else {
    // 当getterOrOptions为对象的时候,说明是是自定义的getter setter,会将set和get分别赋值给setter,getter。
    getter = getterOrOptions.get
    setter = getterOrOptions.set
  }

  const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter)

  if (__DEV__ && debugOptions) {
    cRef.effect.onTrack = debugOptions.onTrack
    cRef.effect.onTrigger = debugOptions.onTrigger
  }
  return cRef as any
}

ComputedRefImpl产生ref对象

通过上面的代码我们可以看出computed 函数返回的ref对象是执行构造方法 ComputedRefImpl 而创建的一个实例。 ComputedRefImpl 的构造方法一共做了两件事:

  • 调用 effect 方法生成 watcher 监听函数并赋值给实例的 effect 属性。
  • 设置ref对象是否为 readonly
class ComputedRefImpl<T> {
  public dep?: Dep = undefined
  private _value!: T
  private _dirty = true  // 脏数据flag 用来判断是否需要重新计算
  public readonly effect: ReactiveEffect<T> 

  public readonly __v_isRef = true // ref响应式对象标识
  public readonly [ReactiveFlags.IS_READONLY]: boolean // ReactiveFlags只读标识

  constructor(
    getter: ComputedGetter<T>,
    private readonly _setter: ComputedSetter<T>,
    isReadonly: boolean
  ) {
    // 调用 ReactiveEffect 方法生成监听函数并赋值给实例的 effect 属性
    this.effect = new ReactiveEffect(getter, () => {
      // 由于初始化时,计算过一次computed值,_dirty已经设为了false
      // 所以当内部依赖发生变化时,会由此进入,设置_dirty为true,这样获取computed值时会再次计算
      if (!this._dirty) {
        this._dirty = true
        triggerRefValue(this)
      }
    })
    // 设置ref对象是否为 readonly
    this[ReactiveFlags.IS_READONLY] = isReadonly
  }

  get value() {
    // the computed ref may get wrapped by other proxies e.g. readonly() #3376
    const self = toRaw(this)
    trackRefValue(self) // 依赖收集
    // 初始化时,_dirty为true,会计算一次computed值
    if (self._dirty) {
      // 设置_dirty为false, 防止再次获取时重新计算,这就是 computed 缓存值的实现原理
      self._dirty = false
      self._value = self.effect.run()!
    }
    return self._value
  }

  set value(newValue: T) {
    this._setter(newValue)
  }
}

computed缓存值的实现分析

我们发现声明一个 computed 时其实并不会执行getter方法,只有在读取 computed 值时才会执行它的 getter 方法,而这个方法实在构造函数 ComputedRefImplgetter 方法定义实现的。

getter 方法会在读取 computed 值的时候执行(包含依赖收集),由于脏数据的开关,初始化时 _dirty 被设为 true) ,在 getter 方法中会计算一遍computed 的值并设置self._dirty = false,在数据源不发生变化的情况下之后再获取 computed 的值时由于 _dirtyfalse 就不会重新计算。这就是 computed 缓存值的实现原理。

computed重新计算值

在数据源发生变化时,在ComputedRefImpl构造函数里为对象添加的effect 函数会给对象的响应式对象生成监听函数,并对 scheduler 进行了设置。
所以,当computed 内部依赖的状态发生改变,执行对应的监听函数,这其中自然会执行 scheduler 里的操作。而在 scheduler 中将 _dirty 设为了 true 。从而在下次取值时,进行重新计算。

结语

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

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-04 15:27:10  更:2022-03-04 15:27:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 8:50:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码