打印
[资料干货]

写 Vue 我建议非必要别用 watch

[复制链接]
258|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
科叼|  楼主 | 2024-7-17 18:48 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

顺便吆喝一声,技术大厂,内推捞人,前/后端or测试←感兴趣

要求学历:全日制统招本科

--加班偶尔较多,但周末加班两倍工资。

--综合薪酬15-35K,工资在一线城市属于一般,但二线城市很可以。


场景代码大概如下,删除了很多无关内容。
<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!清爽!

原文传送门:ssshooter.com/vue-watch/


使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

64

主题

72

帖子

0

粉丝