[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.mzph.cn/web/37675.shtml

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

相关文章

为什么企业应用开发,c++干不过java?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「c的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; C/C这种东西&#xff0c;根本…

一个 API 客户端和一份 TS 学习手册

第75期&#xff1a; Insomnia&#xff1a;超好看的 API 客户端 项目介绍&#xff1a; 一款适用于 GraphQL、REST、WebSockets 和 gRPC 的开源 API 客户端&#xff0c;颜值超高。 跨平台&#xff0c;支持 Mac、Windows 和 Linux。但不支持网页版&#xff0c;需要下载客户端。…

Supabase 自托管部署实践

Supabase 是 Firebase 的开源替代品。使用 Postgres 数据库、身份验证、即时 API、边缘函数、实时订阅、存储和向量嵌入来启动您的项目。 Supabase介绍 Supabase 是一个开源的后端即服务&#xff08;BaaS&#xff09;平台&#xff0c;提供了一系列工具和服务&#xff0c;帮助…

CrimsonEDR:一款恶意软件模式识别与EDR策略评估工具

关于CrimsonEDR CrimsonEDR是一个功能强大的开源项目&#xff0c;该项目旨在帮助广大研究人员识别特定的恶意软件模式&#xff0c;以此来优化终端检测与响应&#xff08;EDR&#xff09;的策略方案。通过使用各种不同的检测方案&#xff0c;可以加深开发人员与研究人员加深对安…

SpringBoot入门实战:SpringBoot整合WebSocket

1.背景介绍 SpringBoot是一个快速开发的框架&#xff0c;它可以帮助我们快速开发Web应用程序。SpringBoot整合WebSocket是SpringBoot的一个组件&#xff0c;它可以帮助我们快速开发WebSocket应用程序。 WebSocket是一种新的协议&#xff0c;它可以让客户端和服务器之间建立持久…

MSYS2教程(windows环境下使用linux工具)

MSYS2教程(windows环境下使用linux工具) 1.msys2简介 MSYS2&#xff08;Minimal SYStem 2&#xff09;是一个集成了大量的GNU工具链、工具和库的开源软件包集合。它提供了一个类似于Linux的shell环境&#xff0c;可以在Windows系统中编译和运行许多Linux应用程序和工具。 MS…

excel修改批量一列单价的金额并保留1位小数

1.打开表格&#xff0c;要把单价金额变成现在的两倍&#xff0c;数据如下&#xff1a; 2.把单价这一列粘贴到一个新的sheet页面&#xff0c;在B2单元格输入公式&#xff1a;A2*2 然后按enter回车键,这时候吧鼠标放到B2单元格右下角&#xff0c;会出现一个黑色的小加号&#xf…

《信创数据库沙龙上海站:共话发展,智启未来》

2024 年 6 月 29 日周六 14:00&#xff0c;信创数据库沙龙在上海市徐汇区建国西路 285 号科投大厦 13 楼金星厅成功举办。本次活动吸引了众多学术界和产业界的专家、学者以及技术爱好者参与。 活动中&#xff0c;多位嘉宾带来了精彩分享。薛晓刚探讨了 Oracle 在国内的前景&a…

EAGLE-2:一种高效无损的推测性采样方法,提升LLM的推理速度。

欢迎关注我的公众号&#xff1a;Halo咯咯 01。概述 北京大学的研究人员联合微软研究院、滑铁卢大学以及Vector研究所共同推出了EAGLE-2&#xff0c;这是一种利用上下文感知的动态草图树来增强推测性采样的方法。EAGLE-2在先前的EAGLE方法基础上进行了改进&#xff0c;不仅显著…

【源码+文档+调试讲解】基于vue的线上点餐系统

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了线上点餐系统的开发全过程。通过分析线上点餐系统管理的不足&#xff0c;创建了一个计算机管理线上点餐系统的方案。文章介绍了线上点餐系统的系统分析部分&…

电脑提示vcomp140.dll缺失怎么解决?vcomp140.dll是什么文件?

当你的电脑提示vcomp140.dll缺失的时候&#xff0c;你就应该要注意了&#xff0c;因为这个提示的出现&#xff0c;代表你的某个程序开不了&#xff01;想要程序能正常运行&#xff0c;那么只要修复好这个vcomp140.dll文件就可以了&#xff0c;下面我们就来给大家详细的说说说vc…

超详细之IDEA上传项目到Gitee完整步骤

1. 注册gitee 账号密码&#xff0c;gitee官网地址&#xff1a;Gitee官网&#xff0c;注册完成后&#xff0c;登录。 2. 创建仓库&#xff0c;在主页左下角有新建按钮&#xff0c;点击新建后会进入到此页面填写仓库信息。 3. 创建完成后复制仓库地址 4. 打开IntelliJ IDEA新建或…

python自动化之schedule

目录 代码&#xff08;以每5秒1次为例&#xff09;: 每5分钟1次 每2小时1次 每天18:00执行 用到的库&#xff1a;schedule&#xff0c;time 实现的效果&#xff1a;按秒来运行任务&#xff0c;按分钟来运行任务&#xff0c;按小时来运行任务&#xff0c;按天来运行任务 代…

鸿蒙3.0WebView网络错误问题

背景&#xff0c;荣耀9x&#xff0c;混淆才会出这个问题。 [ERROR:ssl_client_socket_impl.cc(981)] handshake failed; returned -1, SSL error code 1, net_error -2 NetError.java int SSLClientSocketImpl::DoHandshake() {crypto::OpenSSLErrStackTracer err_tracer(FRO…

Oracle新特性速递:未来数据库技术的无限可能

文章目录 一、自治数据库&#xff1a;智能化与自动化的革命二、机器学习集成&#xff1a;智能数据分析的新境界三、区块链技术&#xff1a;确保数据完整性与透明性四、云原生数据库&#xff1a;灵活扩展与快速部署五、人工智能优化器&#xff1a;智能查询执行计划《Oracle从入门…

centos7搭建zookeeper 集群 1主2从

centos7搭建zookeeper 集群 准备前提规划防火墙开始搭建集群192.168.83.144上传安装包添加环境变量修改zookeeper 的配置 192.168.83.145 和 192.168.83.146 配置 启动 集群 准备 vm 虚拟机centos7系统zookeeper 安装包FinalShell或者其他shell工具 前提 虚拟机安装好3台cen…

为什么我的Skype点数不见了?如何重新激活 Skype 点数?

您超过180天没有使用过点数打电话功能&#xff0c;点数暂时封存在您的账户里面&#xff0c;需要您手动激活&#xff08;目前必须要登录网页版skype&#xff09; 可再次使用。 如何重新激活 Skype 点数&#xff1f; 登录到你的帐户 . 选择 重新激活信用额度 .注意&#xff1a; …

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

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

预约小程序源码,云开发技术,无需服务器

介绍&#xff1a; 很多企业的业务都需要通过服务预约来完成&#xff0c;比如酒店、美容、家政等等。 但很多商家因缺少合适的服务预订工具&#xff0c;而不知道如何让客户尽快预约。 这种情况下&#xff0c;制作一个自己的预约小程序&#xff0c;客户只需要扫码或者在微信里…

【笔记】通过shell脚本自动部署项目(未完成)

然后将gitee仓库上的代码克隆至linux上 如果不知道gitee仓库怎么上传代码移步【笔记】如何在gitee仓库上传idea代码-CSDN博客 写到一半不想写了自己去复习p138-139吧