浅析在Vue中watch使用的必要性及其优化
作者:ssshooter
场景
代码大概如下,删除了很多无关内容。
<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,今天新功能加了个关键词搜索,又得多 watch
一个 keyword.value
。
于是这里变成了 3 个 watch,而且里面有逻辑,甚至是相互依赖的逻辑。
上面的代码没写完,但是整理一下,最终目标是这样的:
- 请求参数有三个变量:route.params.type、keyword 和 pagination
- route.params.type 改变时需要重置 pagination 和 keyword,然后重新请求
- keyword 改变时需要重制 pagination,然后重新请求
- pagination 改变时需要重新请求
watch 真的好?
如果继续用 watch,因为需要重置 pagination 和 keyword,硬生生把三个 watch 写成了个像是任务委托一样的效果,例如 keyword.value 修改时如果 page 是 1 就直接请求,否则修改 page 再让 page 的 watch 触发请求。
watch( () => keyword.value, async () => { if (pagination.page === 1) fetchList() else { pagination.page = 1 } } )
这么耦合真的好吗?这不好。我劝自己耗子尾汁,好好反思。
得出的结论是:watch
不是好文明,能不用 watch
,就别用 watch
。
这不是我第一次对 watch
有意见,在工作中我就见过很多复杂组件动则 5 个以上的 watch
,有的里面还有复杂逻辑。
重点是啥,还没注释……watch
天然就容易让人不写注释,给人一种“啊,这个值变了,运行下面的逻辑是理所当然的吧。”,那你问问两个月后的自己,是不是真的这样?你自己写的 watch 你自己看得懂吗?一个值变了就触发逻辑,但问题是,它变的原因可多了。
所以 watch
生而在语义上不明确,它只解释了对值的依赖,没有解释依赖的原因。
watchEffect 呢?
上面的例子,假如把 fetchList
写成 watchEffect
,其实还是一样的问题,需要在里面额外加 if else 处理重置逻辑。不过逻辑集中在一个 watchEffect
大概还是比分散在 N 个 watch 里好。
总结
总结一下,watch 或者 watchEffect
有其用武之地,但最好满足以下的条件:
- 变动触发点大于 2 个才考虑
watch
(只有一个触发机会的话,什么时候用,什么时候跑就好了) - 所有场景全都适用同一个处理逻辑
- 与其他 watch 没耦合
不过如果没有事件机制来触发的话,那就只能 watch
了。
优化后
<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
,不会发生冲突,另外两个通过事件触发。至于触发事件也不用额外写 @change
,直接用 @update:xxx
就可以了。
这样只有易读的重置逻辑,没有 if else!清爽!
到此这篇关于浅析在Vue中watch使用的必要性及其优化的文章就介绍到这了,更多相关Vue watch内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!