watch 全家桶
# 题目:
这个题目里面有 3 个挑战,我们一个个来看。
# watch 一次:
这里我们看到 vue 官网中侦听器一节对于停止侦听器的介绍,可以看到停止侦听器可以手动去调用 watch 或 watchEffect 返回的函数。
const unWatch = watch(count, () => {
console.log("Only triggered once");
unWatch();
});
那么这里我们解决问题的核心就是在回调中去手动调用它自身,就确保了调用一次。
# watch 对象:
这里我们看到 vue 官网中侦听器一节对于深层侦听器的介绍,可以看到深层侦听器可以深层侦听 state 对象内部属性的变化。
const state = ref({
count: 0,
});
watch(
state,
() => {
console.log("The state.count updated");
},
{ deep: true }
);
state.value.count = 2;
在这个挑战中,我们监听的是 state 这个变量,但是改变的值是 state 这个响应式对象里面 count 的值,那么为了确保 watch 的副作用函数被正常触发我们需要加一个属性{ deep:true },这样我们就可以深层侦听 state 对象内部属性的变化。
# 副作用函数刷新时机:
这里我们看到 vue 官网中对于 nextTick 这个 api 的介绍,当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。这是一个等待下一次 DOM 更新刷新的工具方法。
const eleRef = ref();
const age = ref(2);
watch(age, async () => {
await nextTick();
console.log(eleRef.value);
});
age.value = 18;
那么在这个挑战中,我们使用 nextTick 后,可以等待下一次 DOM 更新刷新,就可以确保访问到的是更新后的eleRef
值。
# 思考
什么时候用watch,什么时候用computed?
我们在开发过程中,可能经常会遇到这样一个问题,不知道什么场景下该用什么api
# computed:
computed看上去是方法,但是实际上是计算属性,它会根据你所依赖的数据动态显示新的计算结果。计算结果会被缓存,computed的值在getter执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取computed的值时才会重新调用对应的getter来计算。
# 示例
<script setup>
import { computed, ref } from "vue"
const name = ref({
firstName: 'aaa',
secondName: 'bbb'
})
const fullName = computed(() => name.value.firstName + name.value.secondName)
</script>
<template>
<div>
<h1>{{ fullName }}</h1>
</div>
</template>
这里,直接在Vue的 template模板内使用插值表达式{{}}
是可以写一些简单的js表达式的很便利,如上我们也可以在插值表达式直接计算 name.firstName + name.secondName
,但是在模版中放入太多声明式的逻辑会让模板本身过重,尤其当在页面中使用大量复杂的逻辑表达式处理数据时,会对页面的可维护性造成很大的影响,而 computed 的设计初衷也正是用于解决此类问题。
# 避免直接修改计算属性值
注意
Vue官方文档有一句原话说的是,从计算属性返回的值是派生状态。可以把它看作是一个“临时快照”,每当源状态发生变化时,就会创建一个新的快照。更改快照是没有意义的,因此计算属性的返回值应该被视为只读的,并且永远不应该被更改——应该更新它所依赖的源状态以触发新的计算。
# 应用场景
提示
适用于重新计算比较费时不用重复数据计算的环境。所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例。如果一个数据依赖于其他数据,那么把这个数据设计为computed
# watch:
当需要在数据变化时执行异步或开销较大的操作时,computed是无法操作异步数据的,所以需要使用watch进行侦听。侦听器watch作用是侦听一个或多个数据的变化,数据变化时执行的回调函数,两个额外参数:immediate(立即执行)和deep(深度侦听)。
# 示例
<script setup>
import { ref, watch } from "vue"
const count = ref(0)
const counter = ref(null)
const increment = () => {
count.value += 1
}
watch(() => count.value, (newVal, oldVal) => {
console.log(newVal, '新值')
console.log(oldVal, '老值')
})
</script>
<template>
<button ref="counter" @click="increment">
{{ count }}
</button>
</template>
例如这里点击按钮,count的值就会+1,我们希望当count的值改变时,去做一些逻辑就可以使用watch。
# 应用场景
提示
如果你需要在某个数据变化时做一些事情,就可以使用watch。
# 总结
- 计算属性computed常用场景是简化在template模板中的复杂表达式,模板中出现太多逻辑判断会造成模板不易维护。
- 侦听器watch常用场景是状态变化之后做一些异步操作。
- 至于选择哪种方案时,个人觉得有异步请求的用watch,其他情况能用计算属性就首选计算属性。