写 Vue 我建议非必要别用 watch

场景

代码大概如下,删除了很多无关内容。

html 复制代码
<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 触发请求。

js 复制代码
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 了。

优化后

html 复制代码
<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.typewatch,不会发生冲突,另外两个通过事件触发。至于触发事件也不用额外写 @change,直接用 @update:xxx 就可以了。

这样只有易读的重置逻辑,没有 if else!清爽!

相关推荐
北京_宏哥几秒前
🔥Jmeter(二十五) - 从入门到精通 - JMeter函数 - 下篇(详解教程)
前端·jmeter·面试
天生我材必有用_吴用1 分钟前
鸿蒙开发入门到进阶:从布局基础到组件实战
前端·harmonyos·arkts
zhangxiao3 分钟前
自定义指令 - 去除所有空格和换行
前端
三思而后行,慎承诺3 分钟前
详解React Fiber架构中,reconcile阶段的具体工作流程
javascript·react.js·ecmascript
前端太佬4 分钟前
微信公众号网页登录:前端视角下的技术实现精要
前端·javascript·微信
Ryan今天学习了吗5 分钟前
如何在浏览器中渲染100万个元素,并且保证页面不卡顿?超详细底层原理图文分享
前端
二进制独立开发5 分钟前
[Trae 04.22+]适用于Vue开发的智能体提示词
vue.js·trae
前端太佬6 分钟前
微信小程序支付全流程实战指南(Node.js后端篇)
前端·javascript·微信小程序
_十六6 分钟前
面试官最爱问的 TypeScript 装饰器:核心原理与实战技巧全解析.md
前端·typescript
代码搬运媛6 分钟前
mitt 事件发布-订阅库在 react 中的使用
前端