【源码系列#03】Vue3计算属性原理(Computed)

这篇具有很好参考价值的文章主要介绍了【源码系列#03】Vue3计算属性原理(Computed)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

专栏分享:vue2源码专栏,vue3源码专栏,vue router源码专栏,玩具项目专栏,硬核💪推荐🙌

欢迎各位ITer关注点赞收藏🌸🌸🌸

语法

传入一个 getter 函数,返回一个默认不可手动修改的 ref 对象

const count = ref(1)
const plusOne = computed(() => count.value + 1)

console.log(plusOne.value) // 2

plusOne.value++ // 错误!

或者传入一个拥有 get 和 set 函数的对象,创建一个可手动修改的计算状态

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

源码实现

  • @issue1 computed参数兼容只传getter方法和handler对象的情况

  • @issue2 缓存特性,只要依赖的变量值没有发生变化,就取缓存中的值

    _dirty作为缓存标识,如果依赖的变量值有变化,则将 _dirty 值置为 true,后续读取计算属性时,重新执行getter;否则直接取_value

  • @issue3 嵌套effect,firstname -> 计算属性fullName -> effect,下一章节详细介绍

import { isFunction } from '@vue/shared'
import { ReactiveEffect, trackEffects, triggerEffects } from './effect'

/**
 * @issue1 computed参数兼容只传getter方法和handler对象
 * @issue2 缓存,只要依赖的变量值没有发生变化,就取缓存中的值
 * @issue3 嵌套effect,firname -> fullName -> effect
 */
class ComputedRefImpl {
  public effect
  public _dirty = true // 默认应该取值的时候进行计算
  public _value
  public dep = new Set()
  public __v_isReadonly = true
  public __v_isRef = true
  constructor(public getter, public setter) {
    // 我们将用户的getter放到effect中,这里面firstname和lastname就会被这个effect收集起来
    this.effect = new ReactiveEffect(getter, () => {
      // 稍后依赖的属性firstname、lastname变化了,会执行此调度函数
      if (!this._dirty) {
        this._dirty = true
        // 实现一个触发更新 @issue3
        triggerEffects(this.dep)
      }
    })
  }
  
  // 类中的访问器属性 底层就是Object.defineProperty
  // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/get
  get value() {
    // 做依赖收集 @issue3
    trackEffects(this.dep)
    // @issue2
    if (this._dirty) {
      // 说明这个值是脏的
      this._dirty = false
      this._value = this.effect.run()
    }
    return this._value
  }
  
  set value(newValue) {
    this.setter(newValue)
  }
}

export const computed = getterOrOptions => {
  let onlyGetter = isFunction(getterOrOptions)

  let getter
  let setter
  // @issue1 
  if (onlyGetter) {
    getter = getterOrOptions
    setter = () => {
      console.warn('no set')
    }
  } else {
    getter = getterOrOptions.get
    setter = getterOrOptions.set
  }
  return new ComputedRefImpl(getter, setter)
}

trackEffects 和 triggerEffects 方法如下

export function trackEffects(dep) { // 收集dep 对应的effect
  if (activeEffect) {
    let shouldTrack = !dep.has(activeEffect) // 去重了
    if (shouldTrack) {
      dep.add(activeEffect)
      // 存放的是属性对应的set
      activeEffect.deps.push(dep) // 让effect记录住对应的dep, 稍后清理的时候会用到
    }
  }
}

export function triggerEffects(effects) { 
  effects = new Set(effects);
  for (const effect of effects) {
    if (effect !== activeEffect) { // 如果effect不是当前正在运行的effect
      if (effect.scheduler) {
        effect.scheduler()
      } else {
        effect.run(); // 重新执行一遍
      }
    }
  }
}

嵌套 effect

让我们分析一下这个测试用例

const { effect, reactive, computed } = VueReactivity
const state = reactive({ firname: '李', lastname: '柏成' })

const fullName = computed(() => {
  // defineProperty中的getter
  return state.firstname + state.lastname
})

effect(() => {
  app.innerHTML = fullName.value
})

setTimeout(() => {
  state.firstname = '王'
}, 1000)

// 1. firstname要依赖于计算属性的effect
// 2. 计算属性收集了外层effect
// 3. 依赖的值变化了会触发计算属性effect重新执行, 计算属性重新执行的时候会触发外层effect来执行

// computed 特点:缓存
console.log('fullName.value', fullName.value)
console.log('fullName.value', fullName.value)
  1. 当执行到 renderEffect 时,默认先执行一次 effect.run(),activeEffect --> renderEffect,并运行 this.fn() --> app.innerHTML = fullName.value
effect(() => {
  app.innerHTML = fullName.value
})
  1. 当访问 fullName.value 时,在 getter 方法中执行 trackEffects(this.dep),计算属性fullName 依赖收集 当前的 activeEffect(renderEffect)
  2. 当运行 this._value = this.effect.run() 时,activeEffect --> computedEffect,并运行 this.fn() ---> return state.firstname + state.lastname
  3. 访问了state.firstname,属性 firstname 依赖收集当前的 activeEffect(computedEffect)
  4. 访问了state.lastname,属性 lastname 依赖收集当前的 activeEffect(computedEffect)
  5. 一秒钟后,firstname 发生了变化。。。firstname变化触发更新 triggerEffects --> computedEffect.scheduler()
  6. 在计算属性 scheduler 中,触发更新 triggerEffects(this.dep) --> renderEffect.run() ,最终重新渲染页面 app.innerHTML = fullName.value

【源码系列#03】Vue3计算属性原理(Computed)文章来源地址https://www.toymoban.com/news/detail-749178.html

到了这里,关于【源码系列#03】Vue3计算属性原理(Computed)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 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计算属性的基础练习

    Vue3前端开发,computed计算属性的基础练习! 在新版里面,和传统的vue2一样,计算属性,都是对一些数据运算进行了封装操作。返回结果是一个实时监控的效果。区别在于,写法不同。效果是一样。 下面给大家展示一个简单的案例。 如图,自定义一个组件,ComputedDemo.vue。里

    2024年01月18日
    浏览(48)
  • Vue3的computed计算属性和watch监视(四)

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

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

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

    2024年02月09日
    浏览(98)
  • Web前端 ---- 【Vue3】computed计算属性和watch侦听属性(侦听被ref和reactive包裹的数据)

    目录 前言 computed watch watch侦听ref数据 ref简单数据类型 ref复杂数据类型 watch侦听reactive数据 本文介绍在vue3中的computed计算属性和watch侦听属性。介绍watch如何侦听被ref和reactive包裹的数据 在vue3中,计算属性computed也是组合式api,也就是说要先引入,再在setup中使用 语法 完整:

    2024年01月18日
    浏览(59)
  • 03.vue3的计算属性

    模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。所以,对于任何包含响应式数据的复杂逻辑,你都应该使用 计算属性 。 简单来说计算属性可以理解为:要用的属性不存在,要通过已有属性计算得来.底层

    2023年04月14日
    浏览(33)
  • Vue计算属性Computed传参

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

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

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

    2024年01月16日
    浏览(49)
  • 【vue2】计算属性(computed)与侦听器(watch)详解

    🥳博       主: 初映CY的前说(前端领域) 🌞个人信条: 想要变成得到,中间还有做到! 🤘本文核心: 计算属性与侦听属性的用法 目录( 文末有给大家准备好的Xmind思维导图 ) 一、计算属性computed ①默认get()方法,仅是获取值 ②不仅仅是获取值,还具有修改属性功能

    2024年01月16日
    浏览(50)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包