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); // 0setTimeout(() => {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 = getterOrOptionssetter = __DEV__? () => {warn(`Write operation failed: computed value is readonly`)}: NOOP} else {// 如果传入了包含get和set方法的对象,将get方法赋值给getter,将set方法赋值给settergetter = getterOrOptions.getsetter = 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: booleanpublic readonly __v_isRef = trueconstructor(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 ReactiveEffectRunnerrunner.effect = _effectreturn 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 的值。这种机制使得我们可以方便地创建基于其他响应式依赖项的计算属性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/617546.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity 工具 之 Azure 微软连续语音识别ASR的简单整理

Unity 工具 之 Azure 微软连续语音识别ASR的简单整理 目录 Unity 工具 之 Azure 微软连续语音识别ASR的简单整理 一、简单介绍 二、实现原理 三、注意实现 四、实现步骤 五、关键脚本 一、简单介绍 Unity 工具类&#xff0c;自己整理的一些游戏开发可能用到的模块&#x…

TIDB的忘了root用户密码和数据库密码解决办法

方法一&#xff1a; 1、修改配置文件重启tidb&#xff0c;无密码登录修改root密码 找到配置文件 tidb.toml &#xff0c;在[security] 作用域下增加如下配置&#xff1a; [security] skip-grant-tabletrue 重启tidb&#xff1a; sh run_tidb.sh 2、重启后&#xff0c;就可以无密…

Vue:将以往的JQ页面,重构成Vue组件页面的大致思路梳理(组件化编码大致流程)

一、实现静态组件 组件要按照功能点拆分&#xff0c;命名不要与HTML元素冲突。 1、根据UI提供的原型图&#xff0c;进行结构拆分&#xff0c;拆分的粒度以是否方便给组件起名字为依据。并梳理好对应组件的层级依赖关系。 2、拆分好结构后&#xff0c;开始对应的写组件&#x…

怎么节约cdn流量- 速盾网络(Sudun)

节约CDN流量的有效方法&#xff1a;速盾网络&#xff08;Sudun&#xff09;的实用建议 在当今数字化时代&#xff0c;网站和应用程序依赖于CDN&#xff08;内容分发网络&#xff09;来加速内容传输&#xff0c;提高用户体验。然而&#xff0c;有效地管理CDN流量对于优化成本和…

vue3-响应式基础之reactive

reactive() 还有另一种声明响应式状态的方式&#xff0c;即使用 reactive() API。与将内部值包装在特殊对象中的 ref 不同&#xff0c;reactive() 将使对象本身具有响应性&#xff1a; 「点击按钮1」 <script lang"ts" setup> import { reactive } from vuec…

ATECLOUD-POWER测试系统如何检测电源稳定性?

电源模块做为一种电源供应器为电子设备提供供电&#xff0c;广泛应用于汽车电子、航空航天、医疗、通信等各个领域&#xff0c;因此检测电源模块的稳定性是非常重要的&#xff0c;确保其为电子设备提供稳定的电压和电流&#xff0c;保证电子设备可以正常稳定工作。 电源模块的稳…

四川古力未来科技有限公司:抖音小店的崛起之路

随着互联网的飞速发展&#xff0c;电子商务已经成为人们日常生活中不可或缺的一部分。作为一家以科技为核心的公司&#xff0c;四川古力未来科技有限公司在电子商务领域中崭露头角&#xff0c;特别是其抖音小店的发展引人注目。 四川古力未来科技有限公司的抖音小店自开业以来&…

【docker】Docker Stack 详细使用及注意事项

一、什么是 Docker Stack Docker Stack 是 Docker Swarm 环境中用于管理一组相关服务的工具。它使得在 Swarm 集群中部署、管理和扩展一组相互关联的服务变得简单。主要用于定义和编排容器化应用的多个服务。以下是 Docker Stack 的一些关键特点&#xff1a; 服务集合&#xf…

RT-Thread:STM32 PHY 调试,使用软件包 WIZNET 驱动 W5500

说明&#xff1a; 1. 本文记录使用 RT-Thread 软件包 WIZNET驱动 W5500 的调试笔记。 2. 采用 RT-Thread Studio 工程 STM32F407VET6 芯片&#xff0c;W5500 PHY芯片&#xff0c;两者之间使用SPI接口链接 。 注意&#xff1a; 1.在按流程建立工程&#xff0c;和移植完 wizn…

SQL语句详解三-DML(数据操作语言)

文章目录 DML添加数据删除数据修改数据 DML DML(数据操作语言)&#xff1a;增删改表中数据&#xff0c;针对的对象是数据库中表格的数据。 添加数据 语法 -- 方式一 insert into 表名(列名1 , 列名2 , ... , 列名n) values(值1 , 值2 , ... , 值n); -- 方式二 insert into 表…

LeetCode---121双周赛---数位dp

题目列表 2996. 大于等于顺序前缀和的最小缺失整数 2997. 使数组异或和等于 K 的最少操作次数 2998. 使 X 和 Y 相等的最少操作次数 2999. 统计强大整数的数目 一、大于等于顺序前缀和的最小缺失整数 简单的模拟题&#xff0c;只要按照题目的要求去写代码即可&#xff0c;…

2-Linux-应用-部署icwp-Linux虚拟机【Django+Vue+Nginx+uwsgi+Linux】

本文概述 本文章讲述基于Linux CentOS 7系统&#xff08;虚拟机&#xff09;&#xff0c;部署DjangoVue开发的前后端分离项目。 项目源码不开放&#xff0c;但是操作步骤可以借鉴。 该文章将项目部署在Linux虚拟机上&#xff0c;暂不使用Docker 相关指令尽量展示执行路径&am…

通义灵码 - 免费的阿里云 VS code Jetbrains AI 编码辅助工具

系列文章目录 前言 通义灵码&#xff0c;是阿里云出品的一款基于通义大模型的智能编码辅助工具&#xff0c;提供行级/函数级实时续写、自然语言生成代码、单元测试生成、代码注释生成、代码解释、研发智能问答、异常报错排查等能力&#xff0c;并针对阿里云 SDK/OpenAPI 的使用…

最强联网Chat GPT 火爆全网高速 永久免费

&#x1f534;高速联网 秒响应支持语音通话&#x1f388; 首先介绍一下她的功能吧&#x1f601; 女友消息代回机&#x1f44c;&#x1f3fb; 朋友圈文案&#x1f44c;&#x1f3fb; 聊天话术&#x1f44c;&#x1f3fb; 高情商回复&#x1f44c;&#x1f3fb; 脱单助…

redis缓存雪崩、穿透和击穿

缓存雪崩 对于系统 A&#xff0c;假设每天高峰期每秒 5000 个请求&#xff0c;本来缓存在高峰期可以扛住每秒 4000 个请求&#xff0c;但是缓存机器意外发生了全盘宕机或者大量缓存集中在某一个时间段失效。缓存挂了&#xff0c;此时 1 秒 5000 个请求全部落数据库&#xff0c;…

Salesforce财务状况分析

纵观Salesforce发展史和十几年财报中的信息&#xff0c;Salesforce从中小企业CRM服务的蓝海市场切入&#xff0c;但受限于中小企业的生命周期价值和每用户平均收入小且获客成本和流失率不对等&#xff0c;蓝海同时也是死海。 Salesforce通过收购逐渐补足品牌和产品两块短板&am…

golang 记录一次协程和协程池的使用,利用ants协程池来处理定时器导致服务全部阻塞

前言 在实习的项目中有一个地方遇到了需要协程池的地方&#xff0c;在mt推荐下使用了ants库。因此在此篇记录一下自己学习使用此库的情况。 场景描述 此服务大致是一个kafka消息接收、发送相关。接收消息&#xff0c;根据参数设置定时器进行重发。 通过这里新建kafka服务&a…

Python个人学习笔记目录

以下目录基于黑马程序员B站视频个人学习笔记 黑马程序员python教程&#xff0c;8天python从入门到精通&#xff0c;学python看这套就够了 Python-第一阶段-第二章 字面量-CSDN博客 Python-第一阶段-第三章 判断语句-CSDN博客 Python-第一阶段-第四章 循环语句-CSDN博客 Pyt…

阿尔泰科技——PXIe8912/8914/8916高速数据采集卡

阿尔泰科技PXIe8912/8914/8916高速数据采集卡是2通道同步采样数字化仪&#xff0c;专为输入信号高达 100M 的高频和高动态范围的信号而设计。 与Labview无缝连接&#xff0c;提供图形化API函数。模拟输入范围可以通过软件编程设置为1V 或者5V。配备了容量高达 2GB的板载内存。…

【抓包教程】BurpSuite联动雷电模拟器——安卓高版本抓包移动应用教程

前言 近期找到了最适合自己的高版本安卓版本移动应用抓HTTP协议数据包教程&#xff0c;解决了安卓低版本的问题&#xff0c;同时用最简单的办法抓到https的数据包&#xff0c;特此进行文字记录和视频记录。 前期准备 抓包工具&#xff1a;BurpSuite安卓模拟器&#xff1a;雷…