答应我,在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逻辑很复杂同步计算出新listconst newList = getListFromDisabledList(dataList.value);dataList.value = newList;},{ deep: true }
);
watch(() => props.type,() => {// 根据type逻辑很复杂同步计算出新listconst newList = getListFromType(dataList.value);dataList.value = newList;}
);
watch(() => props.id,() => {// 从服务端获取dataListfetchDataList();},{ immediate: true }
);
</script>

上面这个例子在template中渲染了dataList,当props.id更新时和初始化时从服务端异步获取dataList。当props.disableListprops.type更新时,同步的计算出新的dataList。

代码逻辑流程图是这样的:
在这里插入图片描述

乍一看上面的代码没什么问题,但是当一个不熟悉这一块业务的新同学接手这一块代码时问题就出来了。

我们平时接手一个不熟悉的业务首先要找一个切入点,对于前端业务,切入点肯定是浏览器渲染的页面。在 Vue 中,页面由模板渲染而来,找到模板中使用的响应式变量和他的来源,就能理解业务逻辑。以 dataList 变量为例,梳理dataList的来源基本就可以理清业务逻辑。

在我们上面的这个例子dataList的来源就是发散的,有很多个来源。首先是watchprops.id从服务端异步获取。然后是watchprops.disableListprops.type,同步更新了dataList。这个时候一个不熟悉业务的同学接到产品需求要更新dataList的取值逻辑,他需要先熟悉dataList多个来源的取值逻辑,熟悉完逻辑后再分析我到底应该是在哪个watch上面去修改业务逻辑完成产品需求。

但是实际上我们维护别人的代码时(特别是很复杂的代码)一般都不愿意去改代码,而是在原有代码的基础上再去加上我们的代码。因为去改别人的复杂代码很容易搞出线上bug,然后背锅。所以在这里我们的做法一般都是再加一个watch,然后在这个watch中去实现产品最新的dataList业务逻辑。

watch(() => props.xxx,() => {// 加上产品最新的业务逻辑const newList = getListFromXxx(dataList.value);dataList.value = newList;}
);

迭代几次业务后这个vue文件里面就变成了一堆watch,屎山代码就是这样形成的。当然不排除有的情况是故意这样写的,为的就是稳定自己在团队里面的地位,因为离开了你这坨代码没人敢动。

使用computed解决问题

我们看了上面的反例,那么一个易维护的代码是怎么样的呢?我认为应该是下面这样的:
在这里插入图片描述

dataListtemplate中渲染,然后同步更新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计算出listconst newDataList = getListFromDisabledList(dataList.value);// 根据type计算出listreturn getListFromType(newDataList);
});watch(() => props.id,() => {// 从服务端获取dataListfetchDataList();},{immediate: true,}
);
</script>

我们在template中渲染的不再是dataList变量,而是renderDataListrenderDataList是一个computed,在这个computed中包含了所有dataList同步相关的逻辑。代码逻辑流程图是这样的:
在这里插入图片描述

此时一位新同学接到产品需求要迭代dataList相关的业务,因为我们的整个业务逻辑已经变成了一条线,新同学就可以很快的梳理清楚业务逻辑。再根据产品的需求看到底应该是修改同步相关的逻辑还是异步相关的逻辑。下面这个是修改同步逻辑的demo:

const renderDataList = computed(() => {// 加上产品最新的业务逻辑const xxxList = getListFromXxx(dataList.value);// 根据disableList计算出listconst newDataList = getListFromDisabledList(xxxList);// 根据type计算出listreturn getListFromType(newDataList);
});

总结

这篇文章介绍了watch主要分为两种使用场景,一种是当watch的值改变后需要同步更新渲染的dataList,另外一种是当watch的值改变后需要异步从服务端获取要渲染的dataList。如果不管同步还是异步都一股脑的将所有代码都写在watch中,那么后续接手的维护者要梳理dataList相关的逻辑就会非常痛苦。因为到处都是watch在更新dataList的值,完全不知道应该在哪个watch中去加上最新的业务逻辑,这种时候我们一般就会再新加一个watch然后在新的watch中去实现最新的业务逻辑,时间久了代码中就变成了一堆watch,维护性就变得越来越差。我们给出的优化方案是将那些同步更新dataListwatch代码全部摞到一个名为renderDataListcomputed,后续维护者只需要判断新的业务如果是同步更新dataList,那么就将新的业务逻辑写在computed中。如果是要异步更新dataList,那么就将新的业务逻辑写在watch中。

如果我的文章对你有点帮助,欢迎关注公众号:【前端欧阳】,文章在公众号首发。你的支持就是我创作的最大动力,感谢感谢!

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

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

相关文章

web前端-html表单元素

文章目录 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>周鑫的</title></head> <body> <form action"06、内联框架.html" method"get"><h1>登录…

江科大stm32学习笔记——【4-1】OLED

一.原理 1.调试方式 串口调试&#xff1a;通过串口通信&#xff0c;将调试信息发送到电脑端&#xff0c;电脑使用串口助手显示调试信息。 显示屏调试&#xff1a;直接将显示屏连接到单片机&#xff0c;将调试信息打印在显示屏上。 Keil调试模式&#xff1a;借助Keil软件的调…

Vite 构建的 Vue3 项目如何整合 Monaco Editor 代码编辑器

目录 &#x1f981; 一. 前言&#x1f981; 二. 探索过程2.1 安装2.2 配置 Monaco Editor2.3 编写 Monaco Editor 代码编辑器2.3.1 创建 Coding Editor 组件2.3.2 父组件使用 CodingEditor 组件 2.4 效果展示 三. 总结 &#x1f981; 一. 前言 各位好&#xff01;我是&#x1…

Unity绘制六边形体

现在steam上面有很多下棋类/经营类的游戏都是用六边形的地形&#xff0c;比较美观而且实用&#xff0c;去年在版本末期我也自己尝试做了一个绘制六边体的demo&#xff0c;一年没接触unity竟然都要忘光了&#xff0c;赶紧在这边记录一下。 想cv代码可以直接拉到代码章节 功能 …

面试官:谈一谈Cookie和Session的区别?

我先解释一下Cookie&#xff0c;它是客户端浏览器用来保存服务端数据的一种机制&#xff0c;当我们通过浏览器去进行网页访问的时候&#xff0c;服务器可以把一些状态数据以key-value的形式写入到Cookie里面&#xff0c;存储到客户端浏览器。下一次这个浏览器再访问服务器的时候…

JavaEE——简单认识JavaScript

文章目录 一、简单认识 JavaScript 的组成二、基本的输入输出和简单语法三、变量的使用四、JS 中的动态类型图示解释常见语言的类型形式 五、JS中的数组六、JS 中的函数七、JS 中的对象 一、简单认识 JavaScript 的组成 对于 JavaScript &#xff0c;其中的组成大致分为下面的…

【嵌入式——QT】日期与定时器

日期 QTime&#xff1a;时间数据类型&#xff0c;仅表示时间&#xff0c;如 16:16:16&#xff1b;QDate&#xff1a;日期数据类型&#xff0c;仅表示日期&#xff0c;如2024-1-22&#xff1b;QDateTime&#xff1a;日期时间数据类型&#xff0c;表示日期和时间&#xff0c;如2…

软件测试有哪些常用的测试方法?

软件测试是软件开发过程中重要组成部分&#xff0c;是用来确认一个程序的质量或者性能是否符合开发之前提出的一些要求。软件测试的目的有两方面&#xff0c;一方面是确认软件的质量&#xff0c;另一方面是提供信息&#xff0c;例如&#xff0c;给开发人员或者程序经理反馈意见…

MATLAB环境下脑电信号EEG的谱分析

脑电信号一直伴随着人类的生命&#xff0c;脑电波是脑神经细胞发生新陈代谢、离子交换时细胞群兴奋突触电位总和&#xff0c;脑电信号的节律性则和丘脑相关&#xff0c;含有丰富的大脑活动信息。通常我们所接触的脑电图都是头皮脑电图&#xff0c;在有些特殊场合还需要皮下部位…

(正规api接口代发布权限)短视频账号矩阵系统实现开发--技术全自动化saas营销链路生态

短视频账号矩阵系统实现开发--技术全自动化saas营销链路生态源头开发&#xff08;本篇禁止抄袭复刻&#xff09; 一、短视频矩阵系统开发者架构 云罗短视频矩阵系统saas化系统&#xff0c;开发层将在CAP原则基础上使用分布式架构,对此网站的整体架构采用了基于B/S三层架构模式…

Python算法题集_组合总和

Python算法题集_组合总和 题39&#xff1a;组合总和1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【值传递回溯】2) 改进版一【引用传递堆栈回溯】3) 改进版二【过程值列表缓存遍历后检索】 4. 最优算法5. 相关资源 本文为Python算法题集之一的…

鸿蒙OS应用编程实战:构建未来应用的基石

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 引言 鸿蒙OS&#xff08;HarmonyOS&#xff0…