淺析在Vue中watch使用的必要性及其優(yōu)化
場景
代碼大概如下,刪除了很多無關(guān)內(nèi)容。
<template>
<div>
<SearchBar @search="handleSearch" />
<Pagination
v-model:page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
/>
</div>
</template>
<script setup lang="ts">
import { reactive, ref, watch, inject, computed } from 'vue'
import SearchBar from '@/components/SearchBar.vue'
const route = useRoute()
const pagination = reactive({
page: 1,
pageSize: isPublic.value ? 10 : 9,
total: 0,
})
const keyword = ref('')
const fetchList = async () => {
loading.value = true
const res = await connect.get(`/api/${route.params.type}`, {
params: {
pageSize: pagination.pageSize,
page: pagination.page,
name: keyword.value,
},
})
pagination.total = res.total
loading.value = false
}
watch(
() => route.params.type,
async () => {
pagination.page = 1
fetchList()
},
{ immediate: true }
)
watch(
() => pagination.page,
async () => {
fetchList()
}
)
watch(
() => keyword.value,
async () => {
if (pagination.page === 1) fetchList()
else {
pagination.page = 1
}
}
)
const handleSearch = (val: string) => {
keyword.value = val
}
const handleDelete = async (item: MindMapItem) => {
await confirmModal.value?.confirm()
await connect.delete('/api/map/' + item._id)
fetchList()
}
</script>
本來只有 2 個 watch,今天新功能加了個關(guān)鍵詞搜索,又得多 watch 一個 keyword.value。
于是這里變成了 3 個 watch,而且里面有邏輯,甚至是相互依賴的邏輯。
上面的代碼沒寫完,但是整理一下,最終目標(biāo)是這樣的:
- 請求參數(shù)有三個變量:route.params.type、keyword 和 pagination
- route.params.type 改變時需要重置 pagination 和 keyword,然后重新請求
- keyword 改變時需要重制 pagination,然后重新請求
- pagination 改變時需要重新請求
watch 真的好?
如果繼續(xù)用 watch,因為需要重置 pagination 和 keyword,硬生生把三個 watch 寫成了個像是任務(wù)委托一樣的效果,例如 keyword.value 修改時如果 page 是 1 就直接請求,否則修改 page 再讓 page 的 watch 觸發(fā)請求。
watch(
() => keyword.value,
async () => {
if (pagination.page === 1) fetchList()
else {
pagination.page = 1
}
}
)
這么耦合真的好嗎?這不好。我勸自己耗子尾汁,好好反思。
得出的結(jié)論是:watch 不是好文明,能不用 watch,就別用 watch。
這不是我第一次對 watch 有意見,在工作中我就見過很多復(fù)雜組件動則 5 個以上的 watch,有的里面還有復(fù)雜邏輯。
重點是啥,還沒注釋……watch 天然就容易讓人不寫注釋,給人一種“啊,這個值變了,運行下面的邏輯是理所當(dāng)然的吧。”,那你問問兩個月后的自己,是不是真的這樣?你自己寫的 watch 你自己看得懂嗎?一個值變了就觸發(fā)邏輯,但問題是,它變的原因可多了。
所以 watch 生而在語義上不明確,它只解釋了對值的依賴,沒有解釋依賴的原因。
watchEffect 呢?
上面的例子,假如把 fetchList 寫成 watchEffect,其實還是一樣的問題,需要在里面額外加 if else 處理重置邏輯。不過邏輯集中在一個 watchEffect 大概還是比分散在 N 個 watch 里好。
總結(jié)
總結(jié)一下,watch 或者 watchEffect 有其用武之地,但最好滿足以下的條件:
- 變動觸發(fā)點大于 2 個才考慮
watch(只有一個觸發(fā)機會的話,什么時候用,什么時候跑就好了) - 所有場景全都適用同一個處理邏輯
- 與其他 watch 沒耦合
不過如果沒有事件機制來觸發(fā)的話,那就只能 watch 了。
優(yōu)化后
<template>
<div>
<SearchBar @search="handleSearch" />
<Pagination
v-model:page="pagination.page"
@update:page="fetchList"
:page-size="pagination.pageSize"
:total="pagination.total"
/>
</div>
</template>
<script setup lang="ts">
import { reactive, ref, watch, inject, computed } from 'vue'
import SearchBar from '@/components/SearchBar.vue'
const route = useRoute()
const pagination = reactive({
page: 1,
pageSize: isPublic.value ? 10 : 9,
total: 0,
})
const keyword = ref('')
const fetchList = async () => {
// 省略
}
watch(
() => route.params.type,
async () => {
keyword.value = ''
pagination.page = 1
fetchList()
},
{ immediate: true }
)
const handleSearch = (val: string) => {
keyword.value = val
pagination.page = 1
fetchList()
}
const handleDelete = async (item: MindMapItem) => {
await confirmModal.value?.confirm()
await connect.delete('/api/map/' + item._id)
fetchList()
}
</script>
修改后,只保留 route.params.type 的 watch,不會發(fā)生沖突,另外兩個通過事件觸發(fā)。至于觸發(fā)事件也不用額外寫 @change,直接用 @update:xxx 就可以了。
這樣只有易讀的重置邏輯,沒有 if else!清爽!
到此這篇關(guān)于淺析在Vue中watch使用的必要性及其優(yōu)化的文章就介紹到這了,更多相關(guān)Vue watch內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue實現(xiàn)給當(dāng)前元素添加樣式,其他元素?zé)o樣式問題
這篇文章主要介紹了vue實現(xiàn)給當(dāng)前元素添加樣式,其他元素?zé)o樣式問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05

