项目实战 | 如何正确使用 watch/computed/ref

4,600 阅读3分钟

前言

哈喽大家好,我是 嘟老板。最近接了个成本千万级的业务系统运维工作,前端是用 Vue3 写的。看了代码之后,我大为震惊,让我不得不重新思考下使用 Vue3 的正确姿势。

本文设计 Vue3 computed watch 以及 ref 的相关说明及反面案例

这是 千万级项目反思 系列的第 1 篇,其他文章见:

正文

业务场景:

某业务单据管理模块,单据详情页面允许同时打开多个(单据 id 不同的情况下),需要根据不同的操作类型(新增、查看、编辑等)对页面元素进行控制,如:查看操作不允许任何编辑操作,新增/编辑操作则允许编辑。操作类型会在从上级页面跳转时添加到 URL 参数上,即使用 vue-router push 函数参数的 query 属性实现。

项目代码

清楚了业务,我们来看看原项目的小伙伴是如何实现的:

const route = useRoute()

const isView = ref(route.query.opType === 'view')

watch(
   () => route,
   (newVal) => {
       if (newValue.query.opType === 'view') isView.value = true
   },
   {
       deep: true,
       immediate: true
   }
)

以上是精简过的代码,核心思路是通过 isView 来控制页面元素的编辑状态,isView 的值由操作类型 route.query.opType 确定,若其等于 view,则为 true,否则为 false

分析

上面代码看似是实现了业务功能,监听路由 route 变更,以更新 isView 的值,达到控制页面元素编辑状态的目的。

那么哪里让我陷入沉思了呢?主要有以下两点:

  1. isView 的值仅仅由路径参数 opType 决定,是否有必要手动赋值?
  2. 仅为了获取路由参数 opType 的值,是否有必要始终监听 route 对象?

针对以上两个问题,我的答案都是。因为 isView 的值来源单一,仅仅是通过 opType 来决定,没有其他任何需要赋值的逻辑;而且对于 route 对象,很可能 queyr.opType 不会变更,而变更其他属性,如 namepath 等,或 query 的其他属性值,这种情况不需要为 isView 重新赋值,如果可以用缓存的值那就完美了。

鉴于以上思路,我认为针对此业务场景,使用 计算属性 computed 更为适合。

computed 和 watch 的区别

我们来简单回顾一下 computedwatch 的主要区别,两者都可以监听响应式依赖的变化。

  • computed 接收一个 getter 函数作为参数,返回一个计算属性 ref,有缓存,仅响应式依赖变更时才重新计算,否则使用缓存的值。
  • watcher 在响应式依赖发生变化时执行副作用函数,无缓存,返回值是一个停止 watch 的函数。

可见,computed 可用于定义 ref 变量,且可缓存,无需每次 route 对象变更时都重新计算。

ref 与 computed 定义变量的区别

由于 refcomputed 都可创建响应式数据,因此也拉来一起比一比😄

  • ref 比较直接,就是用来创建响应式数据的 API
  • computed 返回的计算属性也是响应式数据,不同之处在于,computed 不建议手动赋值,应始终保持其派生状态。即使可以通过 getter/setter 的定义方式实现该特性。

优化后代码

通过以上分析,可以发现,computed 不仅可以监听 route.query.opType 的变更,还可以创建响应式数据,且有缓存,顺便小小的提升了一波性能。

const route = useRoute()

const isView = computed(() => route.query.opType === 'view')

搞定,不仅同样实现了业务功能,还减少了代码量,更提升了可读性和维护性,perfect!!!

结语

好啦,本文内容就到这里。实践出真知,只有真正动手实践,真正参与其中才能更深刻的体会到技术的魅力,才能总结出更合理的开发方式,爱动手的小伙伴们赶快行动起来吧😁。

若本文内容有错误或遗漏的地方,欢迎评论指出。感谢阅读,愿你我共同进步,谢谢!!!