vue3源码解析——watch和watchEffect区别
watch
和watchEffect
是Vue 3.0中新增的两个响应式API,用于监听数据的变化。watch
适用于需要获取新值和旧值,或者需要懒执行的场景,而watchEffect
适用于需要监听多个数据源,并且需要立即执行的场景。它们之间的区别如下:
watch
是监听单个数据源,可以设置immediate
和deep
选项,可以获取新值和旧值;watchEffect
则是监听一组数据源,不能设置immediate
和deep
选项,不能获取新值和旧值。watch
是懒执行的,只有在数据变化时才会执行回调函数,而watchEffect
则是立即执行的,不管数据是否变化。watch
可以监听计算属性,而watchEffect
不能监听计算属性。
watch和watchEffect用法区别
watch
和watchEffect
在使用上也有一些区别,具体如下:
watch
的使用方法:
watch(source, callback, options)
其中,
source
可以是一个响应式数据源,也可以是一个函数,返回一个响应式数据源。callback
是回调函数,会在数据变化时执行,可以获取新值和旧值。options
是一个对象,可以设置immediate
和deep
等选项。
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
的不同类型而得到。
- 如果
source
是一个ref
对象,那么getter
返回source.value
。 - 如果
source
是一个reactive
对象,那么getter
返回reactiveGetter(source)
。 - 如果
source
是一个数组,那么getter
返回一个数组,其中每个元素是一个值或一个函数的执行结果。具体来说,如果数组中的某个元素是一个ref
对象,那么返回s.value
,如果是一个reactive
对象,那么返回reactiveGetter(s)
,如果是一个函数,那么返回callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER)
。 - 如果
source
是一个函数,那么getter
的值取决于cb
的值:- 如果
cb
存在,那么getter
的值是一个函数,返回callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
。 - 否则没有cb,
getter
的值是一个函数,返回一个值或一个函数的执行结果。(也就是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
类的作用包括:
- 构造函数:接受四个参数,分别是用于执行的函数
fn
、触发更新的函数trigger
、可选的调度器函数scheduler
和可选的效果作用域scope
。dirty
属性:一个只读属性,表示当前的脏标记,可以是DirtyLevels.NotDirty
、DirtyLevels.Dirty
、DirtyLevels.MaybeDirty
或DirtyLevels.QueryingDirty
之一。run
方法:执行当前效果,即调用fn
函数,并在执行前后进行一些必要的操作,如更新脏标记、记录活跃的效果等。stop
方法:停止当前效果,即取消对fn
函数的依赖,并进行一些必要的清理操作。
调度器scheduler——管理job的工具
Vue 3 中的
scheduler.ts
文件是用来实现调度器(scheduler)功能的。调度器在 Vue 中负责管理异步更新的调度和执行,确保在适当的时机更新组件以及处理副作用函数。具体来说,scheduler.ts
文件包含了以下主要功能:
-
调度更新:调度器负责管理组件的更新调度,根据更新的优先级和策略来决定何时执行更新操作,以提高性能和效率。
-
异步更新:调度器可以将更新操作异步执行,通过微任务或宏任务来延迟更新操作,以避免阻塞主线程,提高页面的响应性。
-
副作用函数的调度:调度器还负责管理副作用函数的执行时机,根据副作用函数的依赖关系和执行策略来调度副作用函数的执行。
-
性能优化:调度器可以根据具体情况对更新操作进行优化,比如批量更新、合并更新等操作,以减少不必要的更新和提高性能。
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) 安…...
深度学习算法概念介绍
前言 深度学习算法是一类基于人工神经网络的机器学习方法,其核心思想是通过多层次的非线性变换,从数据中学习表示层次特征,从而实现对复杂模式的建模和学习。深度学习算法在图像识别、语音识别、自然语言处理等领域取得了巨大的成功…...
查找算法及查找常用数据结构总结
1.顺序表查找 基本方法: 设查找表以一维数组来存储,要求在此表中查找出关键字的值为x的元素的位置,若查找成功,则返回其位置(即下标),否则,返回一个表示元素不存在的下标࿰…...
大语言模型---强化学习
本文章参考,原文链接: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…...
素数的计数律:Π函数、歪斜数
相当多的数字! 一、说明 自从人类开始掌握最起码的算术概念以来,有一类数字一直处于最前沿——素数。素数定义简单,但难以捕捉,众所周知,素数是数学中一些最困难问题的罪魁祸首,让几代最优秀的数学家感到…...
图像识别在农业领域的应用
图像识别技术在农业领域的应用正在逐渐成熟,它通过分析处理拍摄的植物或农田的图像,为农业生产提供决策支持。以下是图像识别在农业中的一些关键应用: 病虫害检测:图像识别技术能够识别作物上的病斑、虫害或异常状况。通过比较高…...
【JavaSE】java刷题--数组练习
前言 本篇讲解了一些数组相关题目(主要以代码的形式呈现),主要目的在于巩固数组相关知识。 上一篇 数组 讲解了一维数组和二维数组的基础知识~ 欢迎关注个人主页:逸狼 创造不易,可以点点赞吗~ 如有错误,欢迎…...
预处理、编译、汇编、链接过程
预处理、编译、汇编、链接过程 预处理 引入头文件 #include 展开宏定义 #define 处理条件编译指令 #ifdef 删除注释 添加行号 在Linux下可以使用gcc -E命令把hello.c文件预处理成hello.i文件。windows这些操作都集成在编译器visual studio这些里面了。 编译 进行语法分…...
3、Cocos Creator 节点和组件
目录 1、 节点和组件 2、 节点层级和显示顺序 3、坐标系和节点变换属性 坐标系 锚点 旋转 缩放 尺寸 4、 常用技巧 5、参考 1、 节点和组件 Cocos Creator 的工作流程是以组件式开发为核心的,组件式架构也称作 组件 — 实体系统(或 Entity-C…...
【js刷题:数据结构数组篇之长度最小的子数组】
长度最小的子数组 一、题目二、方法1.暴力解法2.滑动窗口是什么滑动窗口的起始位置滑动窗口的结束位置代码展示 3.力扣刷题水果成篮题目思路代码 一、题目 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组&…...
大话设计模式之装饰模式
装饰模式(Decorator Pattern)是一种结构型设计模式,它允许向现有对象动态地添加新功能,同时又不改变其结构。装饰模式通过将对象放入包装器中来实现,在包装器中可以动态地添加功能。 在装饰模式中,通常会有…...
国赛大纲解读
1. 第一部分,是针对5G基础知识的掌握,第二部分是人工智能基本算法的掌握,就是人工智能的应用,用5G+人工智能(AI算法)进行网络优化的问题,要有网络优化的基础知识,比如说:某个区域的覆盖问题,覆盖特别差,但有数据,覆盖电频,srp值这些数据给你,根据数据来判断是…...
设计模式(5):原型模式
一.原型模式 通过 n e w 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。 \color{red}{通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。} 通过new产生一个对象需要非常繁琐的数据准备或访问权限…...
【React】vite + react 项目,进行配置 eslint
安装与配置 eslint 1 安装 eslint babel/eslint-parser2 初始化配置 eslint3 安装 vite-plugin-eslint4 配置 vite.config.js 文件5 修改 eslint 默认配置 1 安装 eslint babel/eslint-parser npm i -D eslint babel/eslint-parser2 初始化配置 eslint npx eslint --init相关…...
Windows入侵排查
目录 0x00 前言 0x01 入侵排查思路 1.1 检查系统账号安全 1.2 检查异常端口、进程 1.3 检查启动项、计划任务、服务 0x00 前言 当企业发生黑客入侵、系统崩溃或其它影响业务正常运行的安全事件时,急需第一时间进行处理,使企业的网络信息系统在最短时…...
C语言每日一题
1.题目 二.分析 本题有两点需要注意的: do-while循环 :在判断while条件前先执行一次do循环static变量 :程序再次调用时static变量的值不会重新初始化,而是在上一次退出时的基础上继续执行。for( i 1; i < 3; i )将调用两次…...
wordpress 商品采集/专业精准网络营销推广
1:在dos下执行jar,根据入参和引用第三方jar的操作 测试类: 打成jar包:encryptPwdForCIP.jar 测试:目录结构如下 lib文件夹下放入jar: 在lib同路径下执行dos 命令: 解释命令:java -cp…...
知名网络公司有哪些/济宁seo推广
先打出nand表0 nand 011 nand 100 nand 111 nand 01容易发现(!a)a nand a然后(a&b)!(a nand b)然后(a|b)!((!a)&(!b))然后(a^b)(a|b)&(a nand b)所以通过nand我们可以实现任意一种位运算所以每一位我们想得到0/1都是可以的按道理[L,R]中符合位数要求的数都能得到然…...
备案网站/优化网站seo
2705: [SDOI2012]Longge的问题 Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 1959 Solved: 1229[Submit][Status][Discuss]Description Longge的数学成绩非常好,并且他非常乐于挑战高难度的数学问题。现在问题来了:给定一个整数N,你需要…...
网站的做用/网络销售挣钱吗
Android中对sqlite加密--SQLCipher 原文:Android中对sqlite加密--SQLCipherandroid中有些时候会将一些隐私数据存放在sqlite数据库中,在root过的手机中通过RE就能够轻松的打开并查看数据库所有内容,所以对隐私数据的保护就有两个方法:①将隐私…...
广州网站推广奋/百度seo刷排名软件
安装到最后一步出错,求解...
怎么在网站做外部链接/定制网站建设推广服务
微信扫码关注公众号 :前端前端大前端,追求更精致的阅读体验 ,一起来学习啊关注后发送关键资料,免费获取一整套前端系统学习资料和老男孩python系列课程 学习资源推荐 区分进程和线程 进程是cpu资源分配的最小单位,可独立运行线程是cpu调度…...