詳解Vue中如何避免濫用watch
前言
上周五晚上8點,開開心心的等著產(chǎn)品驗收完畢后就可以順利上線。結(jié)果產(chǎn)品突然找到我說要加需求,并且維護(hù)這一塊業(yè)務(wù)的同事已經(jīng)下班走了,所以只有我來做。雖然內(nèi)心一萬頭草泥馬在狂奔,但是嘴里還是一口答應(yīng)沒問題。由于這一塊業(yè)務(wù)很復(fù)雜并且我也不熟悉,加上還餓著肚子,在梳理代碼邏輯的時候我差點崩潰了。需要修改的那個vue
文件有幾千行代碼,迭代業(yè)務(wù)對應(yīng)的ref
變量有10多個watch
。我光是梳理這些watch
的邏輯就搞了很久,然后小心翼翼的在原有代碼上面加上新的業(yè)務(wù)邏輯,不敢去修改原有邏輯(擔(dān)心搞出線上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, () => { // 根據(jù)disableList邏輯很復(fù)雜同步計算出新list const newList = getListFromDisabledList(dataList.value); dataList.value = newList; }, { deep: true } ); watch( () => props.type, () => { // 根據(jù)type邏輯很復(fù)雜同步計算出新list const newList = getListFromType(dataList.value); dataList.value = newList; } ); watch( () => props.id, () => { // 從服務(wù)端獲取dataList fetchDataList(); }, { immediate: true } ); </script>
上面這個例子在template
中渲染了dataList
,當(dāng)props.id
更新時和初始化時從服務(wù)端異步獲取dataList
。當(dāng)props.disableList
和props.type
更新時,同步的計算出新的dataList。
代碼邏輯流程圖是這樣的:
乍一看上面的代碼沒什么問題,但是當(dāng)一個不熟悉這一塊業(yè)務(wù)的新同學(xué)接手這一塊代碼時問題就出來了。
我們平時接手一個不熟悉的業(yè)務(wù)首先要找一個切入點,對于前端業(yè)務(wù),切入點肯定是瀏覽器渲染的頁面。在 Vue 中,頁面由模板渲染而來,找到模板中使用的響應(yīng)式變量和他的來源,就能理解業(yè)務(wù)邏輯。以 dataList
變量為例,梳理dataList
的來源基本就可以理清業(yè)務(wù)邏輯。
在我們上面的這個例子dataList
的來源就是發(fā)散的,有很多個來源。首先是watch
了props.id
從服務(wù)端異步獲取。然后是watch
了props.disableList
和props.type
,同步更新了dataList
。這個時候一個不熟悉業(yè)務(wù)的同學(xué)接到產(chǎn)品需求要更新dataList
的取值邏輯,他需要先熟悉dataList
多個來源的取值邏輯,熟悉完邏輯后再分析我到底應(yīng)該是在哪個watch上面去修改業(yè)務(wù)邏輯完成產(chǎn)品需求。
但是實際上我們維護(hù)別人的代碼時(特別是很復(fù)雜的代碼)一般都不愿意去改代碼,而是在原有代碼的基礎(chǔ)上再去加上我們的代碼。因為去改別人的復(fù)雜代碼很容易搞出線上bug,然后背鍋。所以在這里我們的做法一般都是再加一個watch
,然后在這個watch
中去實現(xiàn)產(chǎn)品最新的dataList
業(yè)務(wù)邏輯。
watch( () => props.xxx, () => { // 加上產(chǎn)品最新的業(yè)務(wù)邏輯 const newList = getListFromXxx(dataList.value); dataList.value = newList; } );
迭代幾次業(yè)務(wù)后這個vue
文件里面就變成了一堆watch
,屎山代碼就是這樣形成的。當(dāng)然不排除有的情況是故意這樣寫的,為的就是穩(wěn)定自己在團(tuán)隊里面的地位,因為離開了你這坨代碼沒人敢動。
使用computed解決問題
我們看了上面的反例,那么一個易維護(hù)的代碼是怎么樣的呢?我認(rèn)為應(yīng)該是下面這樣的:
dataList
在template
中渲染,然后同步更新dataList
,最后異步從服務(wù)端異步獲取dataList
,整個過程能夠被穿成一條線。此時新來一位同學(xué)要去迭代dataList
相關(guān)的業(yè)務(wù),那么他只需要搞清楚產(chǎn)品的最新需求是應(yīng)該在同步階段去修改代碼還是異步階段去修改代碼,然后在對應(yīng)的階段去加上對應(yīng)的最新代碼即可。
我們來看看上面的例子應(yīng)該怎么優(yōu)化成易維護(hù)的代碼,上面的代碼中dataList
來源主要分為同步來源和異步來源。異步來源這一塊我們沒法改,因為從業(yè)務(wù)上來看props.id
更新后必須要從服務(wù)端獲取最新的dataList
。我們可以將同步來源的代碼全部摞到computed
中。優(yōu)化后的代碼如下:
<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(() => { // 根據(jù)disableList計算出list const newDataList = getListFromDisabledList(dataList.value); // 根據(jù)type計算出list return getListFromType(newDataList); }); watch( () => props.id, () => { // 從服務(wù)端獲取dataList fetchDataList(); }, { immediate: true, } ); </script>
我們在template
中渲染的不再是dataList
變量,而是renderDataList
。renderDataList
是一個computed
,在這個computed
中包含了所有dataList
同步相關(guān)的邏輯。代碼邏輯流程圖是這樣的:
此時一位新同學(xué)接到產(chǎn)品需求要迭代dataList
相關(guān)的業(yè)務(wù),因為我們的整個業(yè)務(wù)邏輯已經(jīng)變成了一條線,新同學(xué)就可以很快的梳理清楚業(yè)務(wù)邏輯。再根據(jù)產(chǎn)品的需求看到底應(yīng)該是修改同步相關(guān)的邏輯還是異步相關(guān)的邏輯。下面這個是修改同步邏輯的demo:
const renderDataList = computed(() => { // 加上產(chǎn)品最新的業(yè)務(wù)邏輯 const xxxList = getListFromXxx(dataList.value); // 根據(jù)disableList計算出list const newDataList = getListFromDisabledList(xxxList); // 根據(jù)type計算出list return getListFromType(newDataList); });
總結(jié)
這篇文章介紹了watch
主要分為兩種使用場景,一種是當(dāng)watch
的值改變后需要同步更新渲染的dataList
,另外一種是當(dāng)watch
的值改變后需要異步從服務(wù)端獲取要渲染的dataList
。如果不管同步還是異步都一股腦的將所有代碼都寫在watch
中,那么后續(xù)接手的維護(hù)者要梳理dataList
相關(guān)的邏輯就會非常痛苦。因為到處都是watch
在更新dataList
的值,完全不知道應(yīng)該在哪個watch
中去加上最新的業(yè)務(wù)邏輯,這種時候我們一般就會再新加一個watch
然后在新的watch
中去實現(xiàn)最新的業(yè)務(wù)邏輯,時間久了代碼中就變成了一堆watch
,維護(hù)性就變得越來越差。我們給出的優(yōu)化方案是將那些同步更新dataList
的watch
代碼全部摞到一個名為renderDataList
的computed
,后續(xù)維護(hù)者只需要判斷新的業(yè)務(wù)如果是同步更新dataList
,那么就將新的業(yè)務(wù)邏輯寫在computed
中。如果是要異步更新dataList
,那么就將新的業(yè)務(wù)邏輯寫在watch
中。
到此這篇關(guān)于詳解Vue中如何避免濫用watch的文章就介紹到這了,更多相關(guān)Vue避免濫用watch內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Vue中數(shù)據(jù)可視化詞云展示與詞云的生成
數(shù)據(jù)可視化是現(xiàn)代Web應(yīng)用程序中的一個重要組成部分,詞云是一種非常流行的數(shù)據(jù)可視化形式,可以用來展示文本數(shù)據(jù)中的主題和關(guān)鍵字,本文我們將介紹如何在Vue中使用詞云庫進(jìn)行數(shù)據(jù)可視化詞云展示和詞云生成,需要的可以參考一下2023-06-06VUE?項目如何使用?Docker+Nginx進(jìn)行打包部署
使用?Docker,你可以創(chuàng)建一個包含?Vue.js?應(yīng)用程序的容器鏡像,并在任何支持?Docker?的環(huán)境中運行該鏡像,這篇文章主要介紹了VUE?項目用?Docker+Nginx進(jìn)行打包部署,需要的朋友可以參考下2024-06-06Vue網(wǎng)頁html轉(zhuǎn)換PDF(最低兼容ie10)的思路詳解
這篇文章主要介紹了Vue網(wǎng)頁html轉(zhuǎn)換PDF(最低兼容ie10)的思路詳解,實現(xiàn)此功能需要引入兩個插件,需要的朋友可以參考下2017-08-08解決Mint-ui 框架Popup和Datetime Picker組件滾動穿透的問題
這篇文章主要介紹了解決Mint-ui 框架Popup和Datetime Picker組件滾動穿透的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11Vue使用axios進(jìn)行數(shù)據(jù)異步交互的方法
大家都知道在Vue里面有兩種出名的插件能夠支持發(fā)起異步數(shù)據(jù)傳輸和接口交互,分別是axios和vue-resource,同時vue更新到2.0之后,宣告不再對vue-resource更新,而是推薦的axios,今天就講一下怎么引入axios,需要的朋友可以參考下2024-01-01