vue3源码系列之计算属性computed原理剖析
前言
vue3
诸多API中computed
的作用毋庸置疑,也算是我们开发中使用比较多的API了。今天就让我们来探究下computed
底层到底做了什么。如果想要学好computed
个人觉得还是需要将effect
有个深入的理解。这样学的东西才能融会贯通。- 为什么一定强调学习
effect
呢?因为无论是watch
,computed
都是以effect
为基础,所以说基石很重要,如果大家看源码实在很吃力的话,个人觉得看懂我写的就够了。因为我就是按照源码一比一写出来的- 讲述方式:
- 基本使用
- 手写源码
- 源码对照
基本使用
为了防止一部分人对
computed
不是很熟悉,这里也会简单说下使用方式。其实这个使用方式也是结合之后的手写源码来说的。
const data = { name: 'lihh', age: 20 }const state = reactive(data)const newAge = computed(() => state.age)effect(() => {document.getElementById('app').innerHTML = `我叫${state.name}, 我今年${newAge.value}岁了`})setTimeout(() => {state.age++}, 4000)
computed
依赖reactive
的值进行计算,而effect
依赖computed
的值进行计算
- 上述这个实例就是两个
effect
相互嵌套的问题,其中包含着诸多响应式属性以及两个effect
。这里给逐一分析下:- API
effect
本身就是一个effect
computed
是一个effect
- 变量
newAge
是通过reactive
的age
来计算出来的 - 变量
age
收集computedEffect
。而computed
本身收集渲染effect
。所以可谓是牵一发动全身
啊
- API
手写源码
API
computed
是使用比较多的api了。在面试过程中问到的比较多,有个最经典的问题computed 跟 watch有什么不同
。也请大家带着这个疑问来看这段代码
computed
本身的实现
const computed = (getterOrOptions) => {let getterlet setterif (isFunction(getterOrOptions)) {getter = getterOrOptions// 此处标识computed是只读的setter = Function.prototype} else {getter = getterOrOptions.getsetter = getterOrOptions.set}const res = new ComputedRefImpl(getter, setter)return res}
computed
本身有两种使用方式:
const xxx = computed(() => state.age)
const xxx1 = computed({get: () => {}, set: () => {}})
- 这里需要统一做下区分,同时调用实现类
ComputedRefImpl
- 这个方法比较简单,接下来我们重点分析下类
ComputedRefImpl
- 类
ComputedRefImpl
实现
class ComputedRefImpl {constructor(getter, setter) {this.getter = getterthis.setter = setterthis._value = undefinedthis.deps = new Set()this._dirty = truethis.__v_isRef = truethis.effect = new ReactiveEffect(getter, () => {if (!this._dirty) {this._dirty = true// 触发依赖triggerEffect(this.deps)}})}get value() {// 进行依赖收集trackEffect(this.deps)if (this._dirty) {this._value = this.effect.run()this._dirty = false}return this._value}set value(newValue) {this.setter(newValue)}}
- 上述就是关键类
ComputedRefImpl
实现方式了。这里会逐一进行讲解:
this._value
保存获取的值this.deps
保存依赖的effect
。这里的effect就是渲染effect
this._dirty
用来进行缓存的- 整个执行流程:
- 这里不得不提到一点,
compuetd
本身是惰性的,不像watch
以及effect
一样。默认就会执行一次。只有调用的时候才会执行- 还有通过上述代码我们会发现,在构造函数中我们使用了
ReactiveEffect
来创建一个effect
。这个类我们赋值了一个getter
函数以及scheduler函数- 分析下执行流程:
- 步骤1:执行代码
我今年${newAge.value}岁了
的时候,因为牵扯到了newAge.value
。会执行到类ComputedRefImpl
的get value
函数中去- 步骤2:此时激活的effect是
渲染effect
,从而对其进行收集,体现这种话trackEffect(this.deps)
- 步骤3:判断变量
this._dirty
。开始执行if内的代码,获取到最新的值,同时将this._dirty
设置为false。如果在值未修改的情况下再次获取值的时候,直接使用上次返回的值- 步骤4:如果值发生了变化会执行
scheduler
函数,重新将变量_dirty
设置为true。如果下次再次获取值重新执行步骤3
源码对照
- computed入口
地址:
packages\reactivity\src\computed.ts
,函数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
) {// 预制的getter setterlet getter: ComputedGetter<T>let setter: ComputedSetter<T>// 判断是否是函数const onlyGetter = isFunction(getterOrOptions)if (onlyGetter) {getter = getterOrOptionssetter = __DEV__? () => {console.warn('Write operation failed: computed value is readonly')}: NOOP} else {getter = getterOrOptions.getsetter = getterOrOptions.set}const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR)return cRef as any
}
- 类
ComputedRefImpl
实现
地址:
packages\reactivity\src\computed.ts
。类:ComputedRefImpl
这里代码就不粘贴了。太长了没意义。而且写法跟我手写几乎保持一致。所以看我的就够了
结束
如果我的分析对大家有帮助的话,希望大家点赞~~收藏~关注一条龙服务啊
源码地址:https://gitee.com/li_haohao_1/vue-world/tree/master/vue3/computed
vue3源码系列之计算属性computed原理剖析相关推荐
- Vue3源码阅读指南——计算属性(effectcomputed)
在阅读Vue3响应式数据部分的源代码时,effect和computed部分的确有着其设计精巧之处.其代码实现是在packages/reactivity/effect.ts和packages/react ...
- vue 计算属性_lt;Vue 源码笔记系列6gt;计算属性 computed 的实现
1. 前言 原文发布在语雀: <Vue 源码笔记系列6>计算属性 computed 的实现 · 语雀www.yuque.com 上一章我们已经学习过 watch,这一章就来看一下计算属性 ...
- vue源码-对于「计算属性」的理解
vue源码-对于「计算属性」的理解 这是我最近学习vue源码的一个个人总结和理解,所以可能并不适合每一位读者 本文的整体脉络如下,首先尽可能去掉细节,对计算属性源码的大致实现有一个了解,然后举一例子, ...
- java源码系列:HashMap底层存储原理详解——4、技术本质-原理过程-算法-取模具体解决什么问题
目录 简介 取模具体解决什么问题? 通过数组特性,推导ascii码计算出来的下标值,创建数组非常占用空间 取模,可保证下标,在HashMap默认创建下标之内 简介 上一篇文章,我们讲到 哈希算法.哈希 ...
- c++ map 获取key列表_好未来Golang源码系列一:Map实现原理分析
分享老师:学而思网校 郭雨田 一.map的结构与设计原理 golang中map是一个kv对集合.底层使用hash table,用链表来解决冲突 ,出现冲突时,不是每一个key都申请一个结构通过链表串起 ...
- java源码系列:HashMap底层存储原理详解——5、技术本质-原理过程-算法-取模会带来一个什么问题?什么是哈希冲突?为什么要用链表?
目录 取模会带来一个什么问题? 演示什么是哈希冲突(哈希碰撞)? 为什么要用链表? 其他--布隆过滤器 取模会带来一个什么问题? 好,那同学们这样他能达到一个目的,但是呢,它也会带来的一个问题,那它会 ...
- Spring源码系列(十二)Spring创建Bean的过程(二)
1.写在前面 上篇博客主要Spring在创建Bean的时候,第一次调用的Bean的后置处理器的过程,同时笔者也打算将整个Spring创建的Bean的过程,通过这个系列,将Bean的创建过程给讲清楚,废 ...
- Vue 3.0 源码计算属性 Computed的实现
我们来看下 计算属性源码是怎么实现的呢? 参数 getterOrOptions 是判断是写的 第一种是 函数只有getter computed(()=>{ }); 第二种 是 对象 有get 跟 ...
- Vue3计算属性computed
计算属性computed是用来存储属性数据的 对数据进行逻辑处理操作,实现数据包装 计算属性通常依赖于当前vue对象中的普通属性 当依赖的依赖的普通属性发生变化的时候,计算属性也会发生变化 计算属性俩 ...
最新文章
- 戏说肥哥系列之---买车
- Spring Boot由jar包转成war包
- python读取文件的常用方法
- 深入理解事件循环机制
- 2.5 矩阵乘法规则
- 防火墙配置十大任务之十,构建虚拟防火墙
- Spark之hive的UDF自定义函数
- MyBatis学习总结(7)——Mybatis缓存
- 三.redis 排序
- C# DateTime类
- win10开启多用户同时远程登录-很详细
- 【图像加密】基于matlab RSA图像加密解密【含Matlab源码 1442期】
- lm358 pdf应用电路资料及引脚图
- mtk充电电流文件_MT2503 系列充电电流问题
- C语言实现单链表首尾相连
- DAS、NAS、SAN三种高端存储技术分析
- LSI阵列卡的使用教程
- 网络加密流量的相关研究
- 第1节 细胞是生命活动的基本单位
- $timeout、$interval和$watch用法