[vue2/vue3] 详细剖析watch、computed、watchEffect的区别,原理解读

前言:哈喽,大家好,我是前端菜鸟的自我修养!今天给大家分享【深入剖析watch、computed、watchEffect的区别】,并提供具体代码帮助大家深入理解,彻底掌握!原创不易,如果能帮助到带大家,欢迎 收藏+关注 哦 💕

🌈🌈文章目录

一、watch

1.使用语法

2.watch的实现原理 

二、computed

1.简单使用

2.computed的实现原理

三、watchEffect

1.简单使用

2.小结

3.watchEffect的实现原理

4.注意事项

1. 避免过度监听

2. 异步操作需谨慎处理

3. 避免无限循环

四、实际开发当中该怎么去选择

 1.watch

1.1 异步操作触发

1.2 深度监听

2.computed

2.1 派生数据 

2.2 性能优化

3.watchEffect

3.1 自动依赖追踪

3.2 动态数据处理

五、总结

在Vue中,数据响应式是一个核心概念,它使得当数据变化时,相关的视图会自动更新。为了更灵活地处理数据的变化,Vue提供了多种方式,其中包括watch、computed和watchEffect

一句话概括watch ​适用于需要有条件地监听数据变化的场景,computed​ 适用于创建派生数据和性能优化,而watchEffect 适用于自动追踪依赖的场景。在实际应用中,根据具体需求选择合适的API可以更好地发挥Vue的响应式能力。

一、watch

1.使用语法

watch是Vue中一个非常强大的特性,它允许你监听数据的变化并做出相应的反应。它有两种用法:一是监听一个具体的数据变化,二是监听多个数据的变化。

// 监听单个数据
watch('someData', (newVal, oldVal) => {// 做一些事情
});// 监听多个数据
watch(['data1', 'data2'], ([newVal1, newVal2], [oldVal1, oldVal2]) => {// 做一些事情
});

2.watch的实现原理 

Vue中watch的实现主要依赖于Watcher这个核心类。当调用watch时,实际上是创建了一个Watcher实例,并将回调函数和需要监听的数据传递给这个实例。

// 简化版的watch实现
function watch(source, cb) {const watcher = new Watcher(source, cb);
}

Watcher类的构造函数接收两个参数,分别是需要监听的数据(可以是一个字符串,也可以是一个返回值的函数)回调函数。在构造函数中,会对数据进行求值,然后将这个Watcher实例添加到数据的依赖列表中。

class Watcher {constructor(source, cb) {this.getter = typeof source === 'function' ? source : () => this.vm[source];this.cb = cb;this.value = this.get();}get() {pushTarget(this); // 将当前Watcher实例压栈const value = this.getter.call(this.vm); // 触发数据的getter,将当前Watcher实例添加到依赖列表中popTarget(); // 将当前Watcher实例出栈return value;}update() {const oldValue = this.value;this.value = this.get();this.cb(this.value, oldValue);}
}

 简单来说,watch的实现原理就是通过Watcher实例来监听数据的变化,当数据变化时,触发update方法执行回调函数

二、computed

1.简单使用

computed用于计算派生数据。它依赖于其他响应式数据,并且只有在相关数据发生变化时才会重新计算

computed(() => {return someData * 2;
});

2.computed的实现原理

computed的实现原理相对于watch更为复杂,它依赖于gettersetter的机制。在Vue中,computed的定义是一个包含get和set方法的对象

const computed = {get() {return someData * 2;},set(value) {someData = value / 2;}
};

在computed的实现中,当访问计算属性时,实际上是执行了get方法,而在数据变化时,会执行set方法。这里主要使用了Object.defineProperty这个JavaScript的特性。

function createComputedGetter() {return function computedGetter() {const value = getter.call(this); // 执行计算属性的get方法track(target, TrackOpTypes.GET, 'value'); // 添加依赖return value;};
}function createComputedSetter() {return function computedSetter(newValue) {setter.call(this, newValue); // 执行计算属性的set方法trigger(target, TriggerOpTypes.SET, 'value'); // 触发更新};
}function computed(getterOrOptions) {const getter = typeof getterOrOptions === 'function'? getterOrOptions: getterOrOptions.get;const setter = getterOrOptions.set;const cRef = new ComputedRefImpl(getter,setter,isFunction(getterOrOptions) || !getterOrOptions.get);return cRef;
}class ComputedRefImpl {// 构造函数constructor(getter, setter, isReadonly) {// ...this.effect = effect(getter, {lazy: true,scheduler: () => {if (!this._dirty) {this._dirty = true;triggerRef(this);}},});}// ...
}

在上述代码中,createComputedGetter和createComputedSetter用于创建计算属性的getter和setter。computed函数接收一个getter函数,并通过Object.defineProperty将getter和setter添加到计算属性的引用对象中。

当计算属性被访问时,会触发getter,此时会将当前计算属性添加到依赖列表中。当计算属性的依赖数据发生变化时,会触发setter,并通过triggerRef触发计算属性的更新。

三、watchEffect

1.简单使用

watchEffect()函数用于创建一个自动追踪依赖的响应式副作用。它会在初始化时会立即执行一次,并自动追踪在回调函数中使用的所有响应式数据,在这些数据发生变化时重新运行回调函数。

例如,在每当 todoId 的引用发生变化时使用侦听器来加载一个远程资源,如果用watch,是这么写:

<template><div>Test</div>
</template><script setup>import { ref, watch } from 'vue'const todoId = ref(1)const data = ref(null)watch(todoId,async () => {const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)data.value = await response.json()console.log(data.value)},{ immediate: true })
</script>

打印:

但是用了watchEffect()就可以简化为:

<template><div>Test</div>
</template><script setup>import { ref, watchEffect } from 'vue'const todoId = ref(1)const data = ref(null)watchEffect(async () => {const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)data.value = await response.json()console.log(data.value)})
</script>

打印:

2.小结

两者都立即打印了data。但下面的例子中,回调会立即执行,不需要指定 immediate: true。在执行期间,它会自动追踪 todoId.value 作为依赖(和计算属性类似)。每当 todoId.value 变化时,回调会再次执行。有了 watchEffect(),我们不再需要明确传递 todoId 作为源值。

从这个角度来看,watchEffect()的作用类似于Vue2中的computed,即依赖项发生变化,自己也跟着改变。但与computed不同,watchEffect()没有返回值,而是直接执行回调函数

3.watchEffect的实现原理

watchEffect是Vue3中引入的响应式API,它用于执行一个响应式函数,并在函数中响应式地追踪其依赖。与watch不同,watchEffect不需要显式地指定依赖,它会自动追踪函数内部的响应式数据,并在这些数据变化时触发函数重新执行

首先,watchEffect的核心是依赖追踪和触发。Vue3中的响应式系统使用ReactiveEffect类来表示一个响应式的函数。

class ReactiveEffect {constructor(fn, scheduler = null) {// ...this.deps = [];this.scheduler = scheduler;}run() {// 执行响应式函数this.active && this.getter();}stop() {// 停止追踪cleanupEffect(this);}
}export function watchEffect(effect, options = {}) {// 创建ReactiveEffect实例const runner = effect;const job = () => {if (!runner.active) {return;}if (cleanup) {cleanup();}// 执行响应式函数return runner.run();};// 执行响应式函数job();// 返回停止函数return () => {stop(runner);};
}

在上述代码中,ReactiveEffect类表示一个响应式的函数。watchEffect函数接收一个响应式函数,并创建一个ReactiveEffect实例。在执行时,该实例会追踪函数内部的响应式数据,并在这些数据变化时触发函数重新执行。

watchEffect返回一个停止函数,用于停止对响应式数据的追踪

4.注意事项

1. 避免过度监听

由于watchEffect()会追踪使用到的所有响应式数据,因此要确保在回调函数中只使用必要的响应式数据,避免造成不必要的渲染开销。

2. 异步操作需谨慎处理

由于watchEffect()会立即执行回调函数,如果在回调函数中进行异步操作,需要谨慎处理,以免导致意外行为或副作用。

<script setup>
import { watchEffect } from 'vue'// 它会自动停止
watchEffect(() => {})// ...这个则不会!
setTimeout(() => {watchEffect(() => {})
}, 100)
</script>
3. 避免无限循环

当在watchEffect()的回调中修改响应式数据时,可能会导致无限循环。要避免此问题,可以使用watch()函数并设置 immediate: true选项,或者使用ref来存储临时数据。

四、实际开发当中该怎么去选择

 1.watch

watch主要用于监听特定的数据变化并执行回调函数。它可以监听数据的变化,并在满足一定条件时执行相应的操作。常见的使用场景包括:

1.1 异步操作触发

当某个数据发生变化后,需要进行异步操作,比如发起一个网络请求或执行一段耗时的操作。

watch(() => state.data, async (newData, oldData) => {// 异步操作await fetchData(newData);
});

1.2 深度监听

监听对象或数组的变化,并在深层次的数据变化时执行回调

watch(() => state.user.address.city, (newCity, oldCity) => {console.log(`City changed from ${oldCity} to ${newCity}`);
});

2.computed

computed用于创建一个计算属性,它依赖于其他响应式数据,并且只有在依赖数据发生变化时才重新计算。常见的使用场景包括:

2.1 派生数据 

根据现有的数据计算出一些派生的数据,而不必每次都重新计算

const fullName = computed(() => `${state.firstName} ${state.lastName}`);

2.2 性能优化

避免不必要的重复计算,提高性能。

const result = computed(() => {// 避免重复计算if (someCondition) {return heavyCalculation();} else {return defaultResult;}
});

3.watchEffect

watchEffect用于执行一个响应式函数,并在函数内部自动追踪依赖。它适用于不需要显式指定依赖,而是在函数内部自动追踪所有响应式数据变化的场景。常见的使用场景包括:

3.1 自动依赖追踪

函数内部的所有响应式数据都被自动追踪,无需显式指定

watchEffect(() => {console.log(`Count changed to ${state.count}`);
});

3.2 动态数据处理

处理动态变化的数据,无需手动管理依赖

watchEffect(() => {// 处理动态变化的数据handleDynamicData();
})

五、总结

watch ​适用于需要有条件地监听数据变化的场景,computed​ 适用于创建派生数据和性能优化,而watchEffect 适用于自动追踪依赖的场景。在实际应用中,根据具体需求选择合适的API可以更好地发挥Vue的响应式能力。

  好了,本文就到这里吧,点个关注再走嘛~ 

🚀 个人简介:7年开发经验,某大型国企前端负责人,信息系统项目管理师、CSDN优质创作者、阿里云专家博主,分享前端相关技术与工作常见问题~
💟 作    者:前端菜鸟的自我修养❣️
📝 专    栏:vue从基础到起飞
🌈 若有帮助,还请 关注➕点赞➕收藏  ,不行的话我再努努力💪💪💪  

  更多专栏订阅推荐:

👍 前端工程搭建
💕 前端常见问题汇总,避坑大全

📝 javascript深入研究

✍️ GIS地图与大数据可视化

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

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

相关文章

Rust 跨平台-Android 和鸿蒙 OS

1. 安装 rustup rustup 是 Rust 的安装和版本管理工具 $ curl --proto https --tlsv1.2 https://sh.rustup.rs -sSf | sh 该命令会安装 rusup 和最新的稳定版本的 Rust&#xff1b;包括&#xff1a; rustc Rust 编译器&#xff0c;用于将 Rust 代码编译成可执行文件或库。 ca…

网络爬虫(一)

1. 深度优先爬虫&#xff1a;深度优先爬虫是一种以深度为优先的爬虫算法。它从一个起始点开始&#xff0c;先访问一个链接&#xff0c;然后再访问该链接下的链接&#xff0c;一直深入地访问直到无法再继续深入为止。然后回溯到上一个链接&#xff0c;再继续深入访问下一个未被访…

redis,memcached,nginx网络组件

课程目标&#xff1a; 1.网络模块要处理哪些事情 2.reactor是怎么处理这些事情的 3.reactor怎么封装 4.网络模块与业务逻辑的关系 5.怎么优化reactor? io函数 函数调用 都有两个作用&#xff1a;io检测 是否就绪 io操作 1. int clientfd accept(listenfd, &addr, &l…

猫头虎 2024年上半年个人总结:科技自媒体的进击与突破

猫头虎 &#x1f42f; 2024年上半年个人总结&#xff1a;科技自媒体的进击与突破 &#x1f680; 大家好&#xff0c;我是猫头虎&#xff0c;实名李彦斌&#xff0c;陕西商洛人&#xff0c;今年26岁&#xff0c;目前在北京工作&#xff0c;全栈软件工程师、科技自媒体博主、某科…

CSS基础学习记录(6)

目录 1、从最基本的页面开始 2、添加图像/浮层部分 3、位置调整 4、添加动效 4.1、添加浮层动效 4.2、添加背景动画 目录 1、从最基本的页面开始 2、添加图像/浮层部分 3、位置调整 4、添加动效 4.1、添加浮层动效 4.2、添加背景动画 5、总结 根据前面css的学习&a…

喜讯!安全狗荣获“2023年网络安全技术支撑优秀单位”称号

6月6日&#xff0c;由中共厦门市委网络安全和信息化委员会办公室&#xff08;以下简称“厦门市委网信办”&#xff09;主办的2023年网络安全技术支撑优秀单位颁奖仪式在厦门成功举行。 作为国内云原生安全领导厂商&#xff0c;安全狗受邀出席此次活动。 会上&#xff0c;安全狗…

Spring两大核心思想 IoC和AoP

目录 ✨ 一、什么是IoC 1、定义 &#x1f38a; 2、IoC思想 &#x1f38a; 3、优势 &#x1f38a; 4、对象的管理 &#x1f38a; 存对象&#xff1a;Component 取对象&#xff1a;AutoWired ✨二、什么是DI 1、定义 &#x1f38a; 2、IoC和DI的关系&#x1f38a; 可…

【笔记】echarts图表的缩放和鼠标滚动冲突的处理解决方案

解决方案不是很好&#xff0c;来源于github的issue&#xff0c;官方提供了&#xff0c;组合键触发缩放的功能。 https://github.com/apache/echarts/issues/5769 https://echarts.apache.org/zh/option.html#dataZoom-inside.zoomOnMouseWheel dataZoom-inside.zoomOnMouseWhe…

【正点原子K210连载】第十四章 按键输入实验 摘自【正点原子】DNK210使用指南-CanMV版指南

1&#xff09;实验平台&#xff1a;正点原子ATK-DNK210开发板 2&#xff09;平台购买地址https://detail.tmall.com/item.htm?id731866264428 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/docs/boards/xiaoxitongban 第十四章 按键输入实…

数据结构与算法笔记:实战篇 - 剖析Redis常用数据类型对应的数据结构

概述 从本章开始&#xff0c;就进入实战篇的部分。这部分主要通过一些开源醒目、经典系统&#xff0c;真枪实弹地教你&#xff0c;如何将数据结构和算法应用到项目中。所以这部分的内容&#xff0c;更多的是知识点的回顾&#xff0c;相对于基础篇和高级篇&#xff0c;其实这部…

短视频电商源码如何选择

在数字时代的浪潮下&#xff0c;短视频电商以其直观、生动、互动性强的特点&#xff0c;迅速崛起成为电商行业的一股新势力。对于有志于进军短视频电商领域的创业者来说&#xff0c;选择一款合适的短视频电商源码至关重要。本文将从多个角度探讨如何选择短视频电商源码&#xf…

【每日刷题】Day78

【每日刷题】Day78 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 1608. 特殊数组的特征值 - 力扣&#xff08;LeetCode&#xff09; 2. 1385. 两个数组间的距离值 - …

我的世界服务器-高版本服务器-MC服务器-生存服务器-RPG服务器-幻世星辰

生存为主&#xff0c;RPG乐趣为辅&#xff0c;重视每位玩家的建议&#xff0c;一起打造心目中的服务器&#xff0c;与小伙伴一起探险我的世界&#xff01; 服务器版本: 1.18.2 ~ 1.20.4 Q群&#xff1a; 338238381 服务器官网: 星辰毛毛雨-Minecraft高版本生存服务器我的世界…

JVM原理(八):JVM虚拟机工具之基础故障工具

这里主要介绍监视虚拟机运行状态和进行故障处理的工具 1. jsp:虚拟机进程状况工具 jsp命令格式&#xff1a; jsp [options] [hostid] jps远程查询虚拟机进程状态 2. jstat:虚拟机统计信息监视工具 jstat命令格式&#xff1a; jstat [option vmid [interval [s|ms] [count]…

计算机专业课面试常见问题-计算机网络篇

目录 1. 计算机网络分为哪 5 层&#xff1f; 2. TCP 协议简述&#xff1f; 3. TCP 和 UDP 的区别&#xff1f;->不同的应用场景&#xff1f; 4. 从浏览器输入网址到显示页…

Wireshark - tshark支持iptables提供数据包

tshark现在的数据包获取方式有两种&#xff0c;分别是读文件、网口监听&#xff08;af-packet原始套接字&#xff09;。两种方式在包获取上&#xff0c;都是通过读文件的形式&#xff1b;存在文件io操作&#xff0c;在专门处理大流量的情境下&#xff0c; 我们复用wireshark去做…

【mysql死锁】示例 和讨论 “SHOW ENGINE INNODB STATUS“

文章目录 mysql 死锁死锁演示表结构如下 死锁查询mysql 详情命令行 SHOW ENGINE INNODB STATUS 如果 两个事务都是按照先更新1 再更新2的顺序去做更新 会发生死锁么&#xff1f;验证一下所以 如果顺序是一致的 不会产生死锁 只会进行等待 防止mysql 死锁的方式优化sql 自行顺序…

2024超全盘点:5大文档加密系统,文档加密系统都有哪些功能

在众多文档加密系统中&#xff0c;以下是五款备受推崇的软件&#xff0c;其中包括安企神软件、加密精灵等&#xff0c;它们各自具备独特的优势和特点&#xff0c;以满足不同企业对数据安全的需求。 1. 安企神软件 特点与优势&#xff1a;安企神软件以其全面的数据保护功能见长…

C语言从入门到进阶(15万字总结)

前言&#xff1a; 《C语言从入门到进阶》这本书可是作者呕心沥血之作&#xff0c;建议零售价1元&#xff0c;当然这里开个玩笑。 本篇博客可是作者之前写的所有C语言笔记博客的集结&#xff0c;本篇博客不止有知识点&#xff0c;还有一部分代码练习。 有人可能会问&#xff…

基于STM32的简易智能家居设计

一、项目功能概述 1、OLED显示温湿度、空气质量&#xff0c;并可以设置报警阈值 2、设置4个继电器开关&#xff0c;分别控制灯、空调、开关、风扇 3、设计一个离线语音识别系统&#xff0c;可以语音控制打开指定开关、并且可以显示识别命令词到OLED屏上 4、OLED实时显示&#…