当前位置: 首页 > news >正文

vue3源码解析——watch和watchEffect区别

watchwatchEffect是Vue 3.0中新增的两个响应式API,用于监听数据的变化。watch适用于需要获取新值和旧值,或者需要懒执行的场景,而watchEffect适用于需要监听多个数据源,并且需要立即执行的场景。它们之间的区别如下:

  1. watch是监听单个数据源,可以设置immediatedeep选项,可以获取新值和旧值;watchEffect则是监听一组数据源,不能设置immediatedeep选项,不能获取新值和旧值。
  2. watch是懒执行的,只有在数据变化时才会执行回调函数,而watchEffect则是立即执行的,不管数据是否变化。
  3. watch可以监听计算属性,而watchEffect不能监听计算属性。

watch和watchEffect用法区别

watchwatchEffect在使用上也有一些区别,具体如下:

watch的使用方法:

watch(source, callback, options)

其中,source可以是一个响应式数据源,也可以是一个函数,返回一个响应式数据源。callback是回调函数,会在数据变化时执行,可以获取新值和旧值。options是一个对象,可以设置immediatedeep等选项。

 watch使用示例:

import { ref, watch} from 'vue'const count = ref(0)// 使用watch监听数据变化,并在下一次DOM更新前执行回调函数
watch(count, (newVal, oldVal) => {console.log('count变化了', newVal, oldVal)
}, {flush: 'pre'
})// 修改数据
count.value++

watchEffect的使用方法:

watchEffect(callback, options)

其中,callback是一个函数,会在组件渲染时执行,并且会在数据变化时重新执行。options是一个对象,可以设置flush选项。

需要注意的是,watchEffect的回调函数中可以直接使用响应式数据源,不需要显式获取新值和旧值,因为watchEffect会自动收集依赖,在数据变化时重新执行回调函数。

watchEffect示例

import { ref, watchEffect } from 'vue'const count = ref(0)// 使用watchEffect监听数据变化,默认在下一次DOM更新后执行回调函数
watchEffect(() => {console.log('count2变化了', count.value)
})// 修改数据
count.value++

源码分析

watch和watchEffect相关源码都在core-main\packages\runtime-core\src\apiWatch.ts文件中

watchEffect方法

在源码里,watchEffect是一个函数,接收两个参数,第一个是effect,也就是用户传入的执行函数。第二个是options,设置flush属性,flush属性表示回调函数的执行时机。默认 flush 选项是 pre,即在依赖变化之后立即执行副作用函数。

watchEffect内部调用了doWatch方法,doWatch方法接收三个参数,watchEffect在调用的doWatch时候将第二个参数设为null 。这里是重点,为什么,因为等下看watch实现的时候就知道了,watch也调用了doWatch方法。只不过第二个参数不为null了。watchEffect先看到这,你就记住要去调doWatch方法。接下来去看doWatch怎么实现的。

flush具体有以下几个选项:

  • 'pre':在下一次 DOM 更新前执行回调函数(默认选项)。
  • 'post':在下一次 DOM 更新后执行回调函数。
  • 'sync':立即执行回调函数。

watchPostEffect和watchSyncEffect 方法

 值得注意的是,这个watchEffect方法下面,还对外抛出了两个方法,watchPostEffect和watchSyncEffect。前面说了通过flush属性控制effect执行的时机,watchEffect默认是flush:pre,DOM 更新前执行。如果你想改变flush,除了在watchEffect改变flush的值,还可以直接调watchPostEffect和watchSyncEffect方法。

watch方法

在源码里,watch是一个函数,接收三个参数,第一个是source,也就是用户的要监听的属性。第二个是回调函数,通常使用箭头函数,监听新旧值的变化并执行内部的方法。第三个是options,可以设置flush\immediate\deep\once属性。默认 flush 选项也是 pre,即在依赖变化之后立即执行副作用函数。

watch内部也调用了doWatch方法,doWatch方法接收watch传入的三个参数。

 

核心doWatch方法

从前面的分析可以看到,watchEffect和watch都执行了doWatch方法。唯一不同的是watchEffect使用doWatch时第二个参数使用的是null。那么看下doWatch的执行逻辑吧

doWatch接收的参数

doWatch方法接收三个参数:source、cb、options。具体解释如下:

souce:要监听的数据源,可以是一个 ref 对象、一个 reactive 对象、一个数组或一个函数。

cb:是回调函数,在watch中使用。(newValue,oldValue)=>{}。

options:是一个配置对象,接收以下属性

  • deep:是否深度监听,默认值为 false。如果设置为 true,则会递归地监听 source 对象的所有属性和子属性。
  • immediate:是否立即执行回调函数,默认值为 false。如果设置为 true,则会在监听器被创建时立即执行一次回调函ctions。
  • flush:指定回调函数的刷新时机,可以是 'pre''post''sync',默认值为 'pre'
  • onTrack:一个函数,用于在追踪依赖时调用。
  • onTrigger:一个函数,用于在触发依赖时调用。

 

getter 函数——获取监听数据执行结果

doWatch方法第一个核心是定义一个getter函数,用于获取需要监听的数据。getter 的值是根据 source 的不同类型而得到。

  1. 如果 source 是一个 ref 对象,那么 getter 返回 source.value
  2. 如果 source 是一个 reactive 对象,那么 getter 返回 reactiveGetter(source)
  3. 如果 source 是一个数组,那么 getter返回一个数组,其中每个元素是一个值或一个函数的执行结果。具体来说,如果数组中的某个元素是一个 ref 对象,那么返回 s.value,如果是一个 reactive 对象,那么返回 reactiveGetter(s),如果是一个函数,那么返回 callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER)
  4. 如果 source 是一个函数,那么 getter 的值取决于 cb 的值:
    • 如果 cb 存在,那么 getter 的值是一个函数,返回 callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
    • 否则没有cbgetter 的值是一个函数,返回一个值或一个函数的执行结果。(也就是watchEffect)具体来说,如果 cleanup 存在,那么首先执行 cleanup(),然后返回 callWithAsyncErrorHandling(source, instance, ErrorCodes.WATCH_CALLBACK, [onCleanup])

 effect——响应式数据依赖管理

doWatch 的第二个关键是通过new Reactive()创建一个effect实例。接收getter、NOOP、scheduler。getter是一个函数,根据source不同返回一个值或一个函数的执行结果。NOOP是一个空的箭头函数。scheduler是任务job的调度器,默认flush:pre走的是queueJob任务队列调度器,通过队列管理任务,先进先出。

 这个effect是什么?scheduler是什么?queueJob(job)又是什么呢?接着看源码

ReactiveEffect作用——依赖收集

这个ReactiveEffect是实现vue3响应式数据更新的核心类。当数据变化时,可以通过 trigger 函数触发所有依赖该数据的效果,从而更新相关的视图。reactiveEffect 类的作用包括:

  1. 构造函数:接受四个参数,分别是用于执行的函数 fn、触发更新的函数 trigger可选的调度器函数 scheduler 和可选的效果作用域 scope
  2. dirty 属性:一个只读属性,表示当前的脏标记,可以是 DirtyLevels.NotDirtyDirtyLevels.DirtyDirtyLevels.MaybeDirtyDirtyLevels.QueryingDirty 之一。
  3. run 方法:执行当前效果,即调用 fn 函数,并在执行前后进行一些必要的操作,如更新脏标记、记录活跃的效果等。
  4. stop 方法:停止当前效果,即取消对 fn 函数的依赖,并进行一些必要的清理操作。

调度器scheduler——管理job的工具

Vue 3 中的 scheduler.ts 文件是用来实现调度器(scheduler)功能的。调度器在 Vue 中负责管理异步更新的调度和执行,确保在适当的时机更新组件以及处理副作用函数。具体来说,scheduler.ts 文件包含了以下主要功能:

  1. 调度更新:调度器负责管理组件的更新调度,根据更新的优先级和策略来决定何时执行更新操作,以提高性能和效率。

  2. 异步更新:调度器可以将更新操作异步执行,通过微任务或宏任务来延迟更新操作,以避免阻塞主线程,提高页面的响应性。

  3. 副作用函数的调度:调度器还负责管理副作用函数的执行时机,根据副作用函数的依赖关系和执行策略来调度副作用函数的执行。

  4. 性能优化:调度器可以根据具体情况对更新操作进行优化,比如批量更新、合并更新等操作,以减少不必要的更新和提高性能。

queueJob 函数来判断当前任务是否已存在于任务队列中,如果不存在,则将当前任务插入到任务队列中。当任务队列中的任务数量达到一定的阈值时,scheduler 函数会通过 queueFlush 函数来执行任务队列中的任务。 

watch和watchEffect的核心区别——job函数做了什么

前面经过分析,doWatch拿到了三个参数(source,cb,options);然后根据source的不同,定义了一个getter函数,得到了监听数据的执行结果。然后又定义了一个effect管理依赖收集,通过effect的dirty判断依赖的属性有没有变化,effect的run方法可以运行接收的第一个fn函数,在这里fn是getter函数。effect还有一个调度器函数,默认是queueJob(job)。queueJob用来管理一个任务队列。核心方法是看单个job是怎么实现的。

job的定义还是在doWatch方法内,可以看到,job先是对effect的active和dirty进行判断,如果都不满足,不需要重新执行方法。

接着判断cb,也就是doWatch接收的第二个参数。还记得吗,watch调用doWatch的时候第二个参数是回调函数(newValue,oldValue)=>{},而watchEffect调用的时候第二个参数传的是null。在这里可以看到watch和watchEffect的区别了!

如果cb存在,先去执行effect.run()方法,得到返回值给newValue,然后将newValue和oldValue拿到,执行回调方法cb

如果cb不存在,直接执行effect的run方法

 可以看到,watchEffect就少了一步,就是它不需要去处理新值和旧值的逻辑;如果在依赖的数据发生变化的时候,你必须想知道是哪个数据变化,并且旧值和新值的比较关系,那么你就用watch,如果你不关心旧值,也不关心是哪个数据项变化,你就用watchEffect。在doWatch里会将watchEffect的第一个参数当成getter,在effect执行run的时候去执行getter,也就是你传入watchEffect的函数。

总结

在这部分源码分析中,用了很长的篇幅取介绍doWatch的实现,因为watch和watchEffect都调用了doWatch,只是第二个参数不同,watchEffect传入的是null,watch传入的是cb。

doWatch干了什么事情呢?

首先由要收集数据依赖,收集的是哪写属性呢?

source传入的可能是ref\reactive属性、数组或者函数,这个时候需要对source进行计算,看最终依赖的是什么东西?通过定义一个getter函数,根据source类型不同,得到监听数据最后的执行结果。

怎么收集数据依赖呢?

使用ReactiveEffect类,定义一个effect实例,将前面定义的getter传进去,通过操作effect的run方法执行getter,通过effect的dirty属性判断getter是否改变。

数据改变了,doWatch怎么做呢?

数据发生变化了,也就是effect的dirty属性或者active属性有一个为true了。这时doWatch根据传入的第二个参数cb是否有值进行判断。

如果cb有值,那就是watch方法,watch是不是想知道依赖的getter发生变化后得到的新值和之前的旧值啊,所以cb有值的时候先去执行effect的run方法得到依赖属性变化后的新值newValue,将newValue和oldValue传入cb执行cb回调方法。

如果cb没值,那就是watchEffect方法。直接去执行effect的run方法,将传入的source转换为getter,执行就好了。

总的来说 watch vs watchEffect

watch 用于需要知道是哪个数据项发生变化以及需要比较旧值和新值的情况,因为它提供了旧值和新值的参数。而 watchEffect 则更适合在不关心旧值和具体数据项变化的情况下使用,因为它只关注副作用函数的执行,不提供旧值和新值的参数。

 看到这里,如果你已经稍微理解watch和watchEffect的区别了,请一键三连哦~

相关文章:

vue3源码解析——watch和watchEffect区别

watch和watchEffect是Vue 3.0中新增的两个响应式API,用于监听数据的变化。watch适用于需要获取新值和旧值,或者需要懒执行的场景,而watchEffect适用于需要监听多个数据源,并且需要立即执行的场景。它们之间的区别如下:…...

微服务(基础篇-006-Docker)

目录 初识Docker(1) Docker解决的问题(1.1) Docker与虚拟机(1.2) 镜像和容器(1.3) Docker和DockerHub(1.4) docker架构(1.5) 安…...

深度学习算法概念介绍

前言 深度学习算法是一类基于人工神经网络的机器学习方法,其核心思想是通过多层次的非线性变换,从数据中学习表示层次特征,从而实现对复杂模式的建模和学习。深度学习算法在图像识别、语音识别、自然语言处理等领域取得了巨大的成功&#xf…...

查找算法及查找常用数据结构总结

1.顺序表查找 基本方法: 设查找表以一维数组来存储,要求在此表中查找出关键字的值为x的元素的位置,若查找成功,则返回其位置(即下标),否则,返回一个表示元素不存在的下标&#xff0…...

大语言模型---强化学习

本文章参考,原文链接:https://blog.csdn.net/qq_35812205/article/details/133563158 SFT使用交叉熵损失函数,目标是调整参数使模型输出与标准答案一致,不能从整体把控output质量 RLHF(分为奖励模型训练、近端策略优化…...

前端三剑客 —— CSS (第二节)

目录 内容回顾: CSS选择器*** 属性选择器 伪类选择器 1):link 超链接点击之前 2):visited 超链接点击之后 3):hover 鼠标悬停在某个标签上时 4):active 鼠标点击某个标签时,但没有松开 5):fo…...

牛客NC31 第一个只出现一次的字符【simple map Java,Go,PHP】

题目 题目链接: https://www.nowcoder.com/practice/1c82e8cf713b4bbeb2a5b31cf5b0417c 核心 Map参考答案Java import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可*…...

软考系统架构设计师(摘抄)01

架构师承担的责任 系统架构师设计师是承担系统架构设计的核心角色,他不仅是连接用户需求和系统进一步设计与实现的桥梁,也是系统开发早期阶段质量保证的关键角色。系统架构师就是项目的总设计师,他是一个既需要掌控整体又需要洞悉局部瓶颈&a…...

5G无线接入网和接口协议

**部分笔记** 4.3无线协议架构 NR无线协议分为两个平面:用户面和控制面。 用户面(UP):协议栈及用户数据采用的协议 控制面(Control Plane,CP)协议栈即系统的控制信令传输采用的协议簇。 虚线标注的是信令数据的流向。一个UE在…...

【力扣刷题日记】1173.即时食物配送I

前言 练习sql语句,所有题目来自于力扣(https://leetcode.cn/problemset/database/)的免费数据库练习题。 今日题目: 1173.即时食物配送I 表:Delivery 列名类型delivery_idintcustomer_idintorder_datedatecustomer…...

2024年github之node排行榜top50

如果有帮助到您还请动动手帮忙点赞,关注,评论转发,感谢啦!💕💕💕😘😘😘 本文由Butterfly一键发布工具发布 2024年github之node排行榜top50 语言star项目名称…...

当我们在地址栏输入URL的时候浏览器发生了什么

URL 解析 是否合法 首先判断你输入的是一个合法的 URL 还是一个待搜索的关键词,并且根据你输入的内容进行自动完成、字符编码等操作。检查http缓存 DNS 查询 浏览器缓存 -> 操作系统缓存 -> 路由器缓存 -> DNS缓存 -> 根域名服务器查询 TCP 连接 …...

【研发日记】Matlab/Simulink开箱报告(十一)——Requirements Toolbox

目录 前言 Requirements Toolbox 编写需求 需求联接设计 需求跟踪开发进度 追溯性矩阵 分析和应用 总结 前言 见《开箱报告,Simulink Toolbox库模块使用指南(六)——S-Fuction模块(TLC)》 见《开箱报告&#x…...

Elastic 8.13:Elastic AI 助手中 Amazon Bedrock 的正式发布 (GA) 用于可观测性

作者:来自 Elastic Brian Bergholm 今天,我们很高兴地宣布 Elastic 8.13 的正式发布。 有什么新特性? 8.13 版本的三个最重要的组件包括 Elastic AI 助手中 Amazon Bedrock 支持的正式发布 (general availability - GA),新的向量…...

MFC 截取对话框生成图片、截取整个屏幕(可取黑白反色或者整体图片取反色)

HWND hwnd ::GetDesktopWindow();//截整个屏幕,用从这往下4句HDC hdc ::GetDC(hwnd);CDC dc;dc.Attach(hdc);CRect rc,rcw;GetWindowRect(&rcw);GetClientRect(&rc);//只截对话框,用这句//rc.SetRect(0, 0, GetSystemMetrics(SM_CXSCREEN), Ge…...

【LeetCode: 331. 验证二叉树的前序序列化 + DFS】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…...

【Consul】Linux安装Consul保姆级教程

【Consul】Linux安装Consul保姆级教程 大家好 我是寸铁👊 总结了一篇【Consul】Linux安装Consul保姆级教程✨ 喜欢的小伙伴可以点点关注 💝 前言 今天要把编写的go程序放到linux上进行测试Consul服务注册与发现,那怎么样才能实现这一过程&am…...

pytorch常用的模块函数汇总(1)

目录 torch:核心库,包含张量操作、数学函数等基本功能 torch.nn:神经网络模块,包括各种层、损失函数和优化器等 torch.optim:优化算法模块,提供了各种优化器,如随机梯度下降 (SGD)、Adam、RMS…...

素数的计数律:Π函数、歪斜数

相当多的数字! 一、说明 自从人类开始掌握最起码的算术概念以来,有一类数字一直处于最前沿——素数。素数定义简单,但难以捕捉,众所周知,素数是数学中一些最困难问题的罪魁祸首,让几代最优秀的数学家感到…...

图像识别在农业领域的应用

图像识别技术在农业领域的应用正在逐渐成熟,它通过分析处理拍摄的植物或农田的图像,为农业生产提供决策支持。以下是图像识别在农业中的一些关键应用: 病虫害检测:图像识别技术能够识别作物上的病斑、虫害或异常状况。通过比较高…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...

docker详细操作--未完待续

docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...