vue3 源码解析(3)— computed 计算属性的实现

这篇具有很好参考价值的文章主要介绍了vue3 源码解析(3)— computed 计算属性的实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

本文是 vue3 源码分析系列的第三篇文章,主要介绍 vue3 computed 原理。computed 是 vue3 的一个特性,可以根据其他响应式数据创建响应式的计算属性。计算属性的值会根据依赖的数据变化而自动更新,而且具有缓存机制,提高了性能。在这篇文章中,我们将深入探讨 computed 的实现原理,并通过源码分析来理解其工作机制。

computed 的基本用法

在 vue3 中,computed 的基本用法如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>computed</title>
</head>
<body>
<script src="../packages/reactivity/dist/reactivity.global.js"></script>
<script>
  let { reactive, computed } = VueReactivity;
  const state = reactive({
    count: 0
  });
  const double = computed(() => state.count * 2);
  console.log(double.value); // 0
  setTimeout(() => {
    state.count++;
    console.log(double.value); // 2
  }, 1000);
</script>

</body>
</html>

在上面的例子中,我们首先创建了一个响应式对象 state,然后我们使用 computed 创建了一个计算属性 double。这个计算属性的值是 state.count 的两倍。当 state.count 的值发生变化时,double 的值也会自动更新。

computed 的实现原理

computed 函数的源码如下:

// 定义一个名为computed的函数,该函数接受一个参数getterOrOptions,这个参数可以是计算属性的getter函数,也可以是包含get和set方法的对象
export function computed<T>(getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>) {
  // 定义两个变量getter和setter,分别用于存储计算属性的getter函数和setter函数
  let getter: ComputedGetter<T>
  let setter: ComputedSetter<T>
  
  // 判断getterOrOptions是否为函数类型,如果是,说明只传入了getter函数
  const onlyGetter = isFunction(getterOrOptions)
  if (onlyGetter) {
    // 如果只传入了getter函数,将getterOrOptions赋值给getter,同时定义setter函数
    getter = getterOrOptions
    setter = __DEV__
      ? () => {
          warn(
            `Write operation failed: computed value is readonly`
          )
        }
      : NOOP
  } else {
    // 如果传入了包含get和set方法的对象,将get方法赋值给getter,将set方法赋值给setter
    getter = getterOrOptions.get
    setter = getterOrOptions.set
  }
  
  // 创建一个新的ComputedRefImpl实例,并返回。这个实例接收getter、setter和一个布尔值参数
  return new ComputedRefImpl(
    getter,
    setter,
    isFunction(getterOrOptions) || !getterOrOptions.set
  ) as any
}

computed 函数接受一个函数或一个对象作为参数,如果是函数,就是计算属性的 getter 函数。如果是对象,就是包含 get 和 set 函数的选项对象。computed 函数会返回一个 ComputedRefImpl 的实例。ComputedRefImpl 的构造函数如下:

ComputedRefImpl

ComputedRefImpl 函数的源码如下:

class ComputedRefImpl<T> {
  // 定义一个私有变量 _dirty,表示是否需要重新计算值。初始化为 true 表示需要重新计算
  private _dirty = true
  // 定义一个公共的 reactive effect,用于观察和响应值的变化
  public readonly effect: ReactiveEffect<T>
  
  // 定义两个公共的只读属性,用于标识这个引用是否只读和是否是响应式的引用
  public readonly __v_isReadonly: boolean
  public readonly __v_isRef = true

  constructor(
    getter: ComputedGetter<T>,
    private readonly _setter: ComputedSetter<T>,
    isReadonly: boolean
  ) {
    // 创建一个 reactive effect,当值发生变化时触发。如果值没有变化则不触发。 
    this.effect = effect(getter, {
      lazy: true, // 懒加载,只有当需要时才执行
      scheduler: () => { // 调度器函数
        if (!this._dirty) {
          this._dirty = true // 重置 _dirty 为 true,表示需要重新计算
        }
      }
    })
    
    this.effect.computed = this
  }
  
  // 定义一个 getter 方法,返回当前的值。
  get value() {
    if (this._dirty) { // 如果 _dirty 为 true,表示需要被重新计算
      this._value = this.effect() // 调用 effect 方法来计算值
      this._dirty = false // 设置 _dirty 为 false,表示值已经被计算过
    }
    return this._value
  }
  
  // 定义一个 setter 方法,设置新的值并调用传入的 setter 方法来处理这个新值
  set value(newValue: T) {
    this._setter(newValue)
  }
}

ComputedRefImpl 构造函数内部创建了一个 effect 副作用函数,函数传入了 getter 函数,和一个选项对象。选项对象中有两个属性,一个是 lazy,表示副作用函数是否延迟执行,一个是 scheduler,表示副作用函数的调度器,用于在依赖的数据变化时触发副作用函数的重新执行。

effect

effect 函数的源码如下:

function effect<T = any>(fn: () => T, options?: ReactiveEffectOptions): ReactiveEffect<T> {
  // 创建一个新的effect函数,调用run方法执行原始的函数
  const _effect = new ReactiveEffect(fn, NOOP, () => {
    if (_effect.dirty) {
      _effect.run()
    }
  })
  // 如果options中没有设置lazy为true,就立即执行effect函数
  if (!options || !options.lazy) {
    _effect.run()
  }
  
  const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
  runner.effect = _effect
  return runner
}

  • 由于在 ComputedRefImpl 类中定义 effect 函数时,传入的 lazy 为 true 函数并不会自动执行。

  • 当我们访问 double.value 时会触发 ComputedRefImpl 类里面的 get 这个函数,执行 effect 函数。在此过程中会建立属性依赖关系。

  • 执行完之后,将 this._dirty 设置为 false,这一步在后面计算属性是否会重新执行起到关键作用。

scheduler

scheduler 函数通常用于控制计算属性的更新调度。 函数提供了一种额外的调度控制机制,使你能够更好地控制计算属性的更新时机和逻辑。那么 scheduler 是如何被执行的:

function trigger (target, type, key, newValue?, oldValue?) {
  // 此处省略部分代码
  for (const effect of effects) {
    // computed 表示这是一个计算属性
    if (effect.computed) {
      // 执行调度器函数
      // 重新修改 dirty 的值,标记该计算属性为“脏”状态,表示其值已被修改
      effect.options.scheduler()
    } else {
      effect()
    }
  }
}
  • 对于 effects 中的每一个 effect,如果它是一个计算属性,那么会调用它的调度器函数来进行更新。否则,直接调用 effect() 来运行副作用。

  • scheduler 函数,用来在依赖项变化时改变 _dirty 标志位,表示计算属性需要重新计算。

  • 当我们再次访问 double.value 时,由于 this._dirty = true 又会重新执行 get 这个函数,然后更新 double 的值。

通过这个机制使得 vue3 能够精确地控制哪些副作用需要在响应式数据变化时更新,从而提高了性能。

总结

通过对 computed 的源码分析,我们可以看到 vue3 如何实现计算属性的。当我们创建一个计算属性时,vue 会收集这个计算属性的所有依赖项。然后,当这些依赖项发生变化时,vue 会重新计算这个计算属性的值。并通过 _dirty 这个标志位来判断是否需要执行副作用函数并更新 _value 的值。这种机制使得我们可以方便地创建基于其他响应式依赖项的计算属性。文章来源地址https://www.toymoban.com/news/detail-795208.html

到了这里,关于vue3 源码解析(3)— computed 计算属性的实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Vue3/ Vue3 计算属性computed函数 语法 与 介绍 、Vue3 Vue2computed计算属性 能不能传参 怎么传参

    语法: // 第一种语法get方法 (没有set) const 函数名 = computed(() = {   return  }) // 第二种语法 get set 方法 带有set参数 可以设置 const 函数名 = computed(() = { get() { return 结果 }, set( val ){  } }) 触发场景:  如果要访问计算属性 会自动执行 get 如果要修改计算属性 会自动执行 set 简介

    2024年02月02日
    浏览(49)
  • vue3-computed计算属性!!!

    1.计算属性具有缓存特性,在computed中的响应式数据不发生变化时,就不重新加载computed中的逻辑。(作用于大量耗时的逻辑解构,并为其数据不经常发生变化,可采取computed计算属性提高程序效率--常用于购物车数据计算) 代码如下:  通过F12检测数据变化:

    2024年02月05日
    浏览(48)
  • vue全家桶进阶之路33:Vue3 计算属性computed

    在Vue3中,计算属性可以使用 computed 函数来定义。 computed 函数接受两个参数:第一个参数是一个函数,该函数返回计算属性的值;第二个参数是一个可选的配置对象,可以包含getter和setter函数,以及控制计算属性缓存的缓存配置。 Vue3中的计算属性与Vue2中的计算属性相比有以

    2023年04月18日
    浏览(44)
  • Vue3的computed计算属性和watch监视(四)

    监视【ref】定义的【基本数据】类型 监视【ref】定义的【对象类型】数据 监视【reactive】定义的【对象类型】数据  与 场景二 不同的是,newVal和oldVal是一样的,表明通过Object.assign重新赋值的时候,并不是生成一个新的对象,而是新的值覆盖了旧值 监视【ref】或者【reactiv

    2024年02月21日
    浏览(44)
  • vue 计算属性未重新计算 / computed 未触发 / computed 原理&源码分析

    点击可打开demo 这里在一秒后改了数组里value属性的值 虽然数据有更新,但打开控制台,可以发现computed函数只在初始化时执行了一次 按理说一秒后改变了value值,应该执行两次才对呀? 但如果computed属性这样写,明确写明展开了每一项,获取到了value属性,就能执行第二次

    2024年02月06日
    浏览(46)
  • Vue3 计算属性和侦听器实战(computed、watch)——简易点餐页面

    这篇文章记录一下 Vue3 计算属性和侦听器 (computed、watch) 实战的内容,这篇文章我们在有计算属性和侦听器的基础上,我们来制作一个简易点餐页面,接下来我们一起来从零到一开始制作。 计算属性和侦听器相关文章推荐: 深入与浅谈 Vue 中计算属性和侦听器的区别和使用

    2024年02月09日
    浏览(98)
  • 前端Vue入门-day02-vue指令、computed计算属性与watch侦听器

    (创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 指令补充 指令修饰符 v-bind 对于样式控制的增强  操作class 案例:京东秒杀 tab 导航高亮 操作style  v-model 应用于其他表单元素  computed 计算属性 基础语法 computed 计算属

    2024年02月11日
    浏览(42)
  • Vue计算属性Computed传参

    关于computed计算属性传参的问题,因为computed是计算属性,如果给conputed传参则会直接报错,并且报computed is not function。 解决办法: 方法一: 通过返回函数来进行传参: 代码: 分析: 既然计算属性不能做函数一样进行传参,但是computed有一个 return 我们可以利用起来,所以我

    2024年02月16日
    浏览(55)
  • Vue-计算属性(computed)简单说明和使用

    学习vue的计算属性之前,我们先写一个案例,我们先用插值语法实现,然后再使用vue的计算属性实现,经过对比,我们就能掌握计算属性的精髓和原理 写一个简单的例子,姓和名分别用两个输入框控制,最后通过一个span标签拼接成一个全名 首先通过简单的插值语法实现,需

    2024年01月16日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包