答应我,在vue中不要滥用watch好吗?
前言
上周五晚上8点,开开心心的等着产品验收完毕后就可以顺利上线。结果产品突然找到我说要加需求,并且维护这一块业务的同事已经下班走了,所以只有我来做。虽然内心一万头草泥马在狂奔,但是嘴里还是一口答应没问题。由于这一块业务很复杂并且我也不熟悉,加上还饿着肚子,在梳理代码逻辑的时候我差点崩溃了。需要修改的那个vue文件有几千行代码,迭代业务对应的ref变量有10多个watch。我光是梳理这些watch的逻辑就搞了很久,然后小心翼翼的在原有代码上面加上新的业务逻辑,不敢去修改原有逻辑(担心搞出线上bug背锅)。
滥用watch带来的问题
首先我们来看一个例子:
<template>
{{ dataList }}
</template>
<script setup lang="ts">
import { ref, watch } from "vue";
const dataList = ref([]);
const props = defineProps(["disableList", "type", "id"]);
watch(
() => props.disableList,
() => {
// 根据disableList逻辑很复杂同步计算出新list
const newList = getListFromDisabledList(dataList.value);
dataList.value = newList;
},
{ deep: true }
);
watch(
() => props.type,
() => {
// 根据type逻辑很复杂同步计算出新list
const newList = getListFromType(dataList.value);
dataList.value = newList;
}
);
watch(
() => props.id,
() => {
// 从服务端获取dataList
fetchDataList();
},
{ immediate: true }
);
</script>
上面这个例子在template中渲染了dataList,当props.id更新时和初始化时从服务端异步获取dataList。当props.disableList和props.type更新时,同步的计算出新的dataList。
代码逻辑流程图是这样的:

乍一看上面的代码没什么问题,但是当一个不熟悉这一块业务的新同学接手这一块代码时问题就出来了。
我们平时接手一个不熟悉的业务首先要找一个切入点,对于前端业务,切入点肯定是浏览器渲染的页面。在 Vue 中,页面由模板渲染而来,找到模板中使用的响应式变量和他的来源,就能理解业务逻辑。以 dataList 变量为例,梳理dataList的来源基本就可以理清业务逻辑。
在我们上面的这个例子dataList的来源就是发散的,有很多个来源。首先是watch了props.id从服务端异步获取。然后是watch了props.disableList和props.type,同步更新了dataList。这个时候一个不熟悉业务的同学接到产品需求要更新dataList的取值逻辑,他需要先熟悉dataList多个来源的取值逻辑,熟悉完逻辑后再分析我到底应该是在哪个watch上面去修改业务逻辑完成产品需求。
但是实际上我们维护别人的代码时(特别是很复杂的代码)一般都不愿意去改代码,而是在原有代码的基础上再去加上我们的代码。因为去改别人的复杂代码很容易搞出线上bug,然后背锅。所以在这里我们的做法一般都是再加一个watch,然后在这个watch中去实现产品最新的dataList业务逻辑。
watch(
() => props.xxx,
() => {
// 加上产品最新的业务逻辑
const newList = getListFromXxx(dataList.value);
dataList.value = newList;
}
);
迭代几次业务后这个vue文件里面就变成了一堆watch,屎山代码就是这样形成的。当然不排除有的情况是故意这样写的,为的就是稳定自己在团队里面的地位,因为离开了你这坨代码没人敢动。
使用computed解决问题
我们看了上面的反例,那么一个易维护的代码是怎么样的呢?我认为应该是下面这样的:

dataList在template中渲染,然后同步更新dataList,最后异步从服务端异步获取dataList,整个过程能够被穿成一条线。此时新来一位同学要去迭代dataList相关的业务,那么他只需要搞清楚产品的最新需求是应该在同步阶段去修改代码还是异步阶段去修改代码,然后在对应的阶段去加上对应的最新代码即可。
我们来看看上面的例子应该怎么优化成易维护的代码,上面的代码中dataList来源主要分为同步来源和异步来源。异步来源这一块我们没法改,因为从业务上来看props.id更新后必须要从服务端获取最新的dataList。我们可以将同步来源的代码全部摞到computed中。优化后的代码如下:
<template>
{{ renderDataList }}
</template>
<script setup lang="ts">
import { ref, computed, watch } from "vue";
const props = defineProps(["disableList", "type", "id"]);
const dataList = ref([]);
const renderDataList = computed(() => {
// 根据disableList计算出list
const newDataList = getListFromDisabledList(dataList.value);
// 根据type计算出list
return getListFromType(newDataList);
});
watch(
() => props.id,
() => {
// 从服务端获取dataList
fetchDataList();
},
{
immediate: true,
}
);
</script>
我们在template中渲染的不再是dataList变量,而是renderDataList。renderDataList是一个computed,在这个computed中包含了所有dataList同步相关的逻辑。代码逻辑流程图是这样的:

此时一位新同学接到产品需求要迭代dataList相关的业务,因为我们的整个业务逻辑已经变成了一条线,新同学就可以很快的梳理清楚业务逻辑。再根据产品的需求看到底应该是修改同步相关的逻辑还是异步相关的逻辑。下面这个是修改同步逻辑的demo:
const renderDataList = computed(() => {
// 加上产品最新的业务逻辑
const xxxList = getListFromXxx(dataList.value);
// 根据disableList计算出list
const newDataList = getListFromDisabledList(xxxList);
// 根据type计算出list
return getListFromType(newDataList);
});
总结
这篇文章介绍了watch主要分为两种使用场景,一种是当watch的值改变后需要同步更新渲染的dataList,另外一种是当watch的值改变后需要异步从服务端获取要渲染的dataList。如果不管同步还是异步都一股脑的将所有代码都写在watch中,那么后续接手的维护者要梳理dataList相关的逻辑就会非常痛苦。因为到处都是watch在更新dataList的值,完全不知道应该在哪个watch中去加上最新的业务逻辑,这种时候我们一般就会再新加一个watch然后在新的watch中去实现最新的业务逻辑,时间久了代码中就变成了一堆watch,维护性就变得越来越差。我们给出的优化方案是将那些同步更新dataList的watch代码全部摞到一个名为renderDataList的computed,后续维护者只需要判断新的业务如果是同步更新dataList,那么就将新的业务逻辑写在computed中。如果是要异步更新dataList,那么就将新的业务逻辑写在watch中。
如果我的文章对你有点帮助,欢迎关注公众号:【前端欧阳】,文章在公众号首发。你的支持就是我创作的最大动力,感谢感谢!
答应我,在vue中不要滥用watch好吗?的更多相关文章
- Vue中的computed属性
阅读Vue官网的过程中,对于计算属于与监听器章节的内容有点理解的不清晰:https://cn.vuejs.org/v2/guide/computed.html. 后来上网查询了资料,结合官网的说明,总 ...
- 八、Vue中的computed属性
看了网上很多资料,对vue的computed讲解自己看的都不是很清晰,今天忙里抽闲,和同事们又闲聊起来,对computed这个属性才有了一个稍微比较清晰的认识,下面的文章有一部分是转自: https: ...
- vue中computed与watch的异同
一.computed 和 watch 都可以观察页面的数据变化.当处理页面的数据变化时,我们有时候很容易滥用watch. 而通常更好的办法是使用computed属性,而不是命令是的watch回调. ...
- 十三、Vue中的computed属性
以下抄自https://www.cnblogs.com/gunelark/p/8492468.html 看了网上很多资料,对vue的computed讲解自己看的都不是很清晰,今天忙里抽闲,和同事们又闲 ...
- Vue中的computed和watch
看了网上很多资料,对vue的computed讲解自己看的都不是很清晰,今天忙里抽闲,和同事们又闲聊起来,对computed这个属性才有了一个稍微比较清晰的认识,下面的文章有一部分是转自: https: ...
- 详解Vue中的computed和watch
作者:小土豆 博客园:https://www.cnblogs.com/HouJiao/ 掘金:https://juejin.cn/user/2436173500265335 1. 前言 作为一名Vue ...
- vue中如何不通过路由直接获取url中的参数
前言:为什么要不通过路由直接获取url中的参数? vue中使用路由的方式设置url参数,但是这种方式必须要在路径中附带参数,而且这个参数是需要在vue的路由中提前设置好的. 相对来说,在某些情况下直接 ...
- vue中的重要特性
一.vue中的自定义组件 html的代码: <!DOCTYPE html> <html lang="en"> <head> <meta c ...
- Vue中comoputed中的数据绑定
Vue中的数据实现响应式绑定是在初始化的时候利用definePrototype的定义set和get过滤器,在进行组件模板编译时实现water的监听搜集依赖项,当数据发生变化时在set中通过调用dep. ...
- vue中使用stompjs实现mqtt消息推送通知
最近在研究vue+webAPI进行前后端分离,在一些如前端定时循环请求后台接口判断状态等应用场景用使用mqtt进行主动的消息推送能够很大程度的减小服务端接口的压力,提高系统的效率,而且可以利用mqtt ...
随机推荐
- JVM内存初步学习
JVM内存初步学习 最近在学习容器内的JVM运行, 简单总结了下学习结果, 但是感觉还是分不清楚很多地方: 同事帮忙进行了 native memory的监控, 主要信息简要如下: jvm刚运行起来 ...
- linux 53端口占用的原因
解析 Linux 下 53 端口占用的现象 在 Linux 系统中,端口 53 往往是与域名解析服务(DNS)相关的.本文将详细介绍一个与端口 53 相关的情景,以及如何使用命令行工具来解析和理解这一 ...
- VUe2.0 和 Vue3.0 的生命周期作对比
VUe2.0 和 Vue3.0 的生命周期作对比 beforeCreate -> 请使用 setup() created -> 请使用 setup() beforeMount -> ...
- 超级好用的elementui动态循环菜单
<template> <div> <el-menu @select="selectMenu" :default-active="curren ...
- 【一个经典BUG】gin框架中,异步协程使用context导致取不到数据
bug的内容大致如下: func httpHandle(c *gin.Context) { go func(ctx Context){ v := ctx.Value("xxx") ...
- 【K哥爬虫普法】老铁需要车牌靓号吗?判刑的那种
我国目前并未出台专门针对网络爬虫技术的法律规范,但在司法实践中,相关判决已屡见不鲜,K 哥特设了"K哥爬虫普法"专栏,本栏目通过对真实案例的分析,旨在提高广大爬虫工程师的法律意识, ...
- go中异常处理流程
在 Go 语言中,panic.recover 和 defer 是用于处理异常情况的关键字.它们通常一起使用来实现对程序错误的处理和恢复. 1. defer 语句 defer 用于在函数返回之前执行一段 ...
- ASP.NET MVC 通过ActionFilter获取请求的参数
using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using Syst ...
- 大语言模型的预训练[3]之Prompt Learning:Prompt Engineering、Answer engineering、Multi-prompt learning、Training strategy详解
大语言模型的预训练[3]之Prompt Learning:Prompt Engineering.Answer engineering.Multi-prompt learning.Training st ...
- 【二】最新多智能体强化学习文章如何查阅{顶会:AAAI、 ICML }
相关文章: [一]最新多智能体强化学习方法[总结] [二]最新多智能体强化学习文章如何查阅{顶会:AAAI. ICML } [三]多智能体强化学习(MARL)近年研究概览 {Analysis of e ...