开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 8 天,点击查看活动详情
说点题外话
上一篇介绍了组合式composition API的写法:
- setup是一个函数,需要将template模板展示的内容return出去
- 如果是setup语法糖,则直接定义需要展示的变量或者方法即可
组合式定义响应式变量的两种方式:
- ref(常用):更适合定义String、Number基本数据类型
- reactive:适合定义Object等引用数据类型
今天这篇介绍侦听器watch在组合式 API和setup语法糖下面的写法
说正文
一、watchEffect
watchEffect用于自动收集响应式数据的依赖
1.1 watchEffect基本使用
<template>
<div>
<h2>{{ counter }}</h2>
<button @click="addCounter">+1</button>
</div>
</template>
<script >
import { ref, watchEffect } from 'vue';
export default{
setup() {
const counter = ref(100)
const addCounter = () => counter.value++
watchEffect(() => {
console.log("counter:", counter.value);
})
return {
counter,
addCounter
}
}
}
</script>
从上面的代码和输出结果能看出来,watchEffect会默认执行一次。检测到有使用的变量,就会将变量添加到依赖中,只要有变量发生变化,就会执行侦听
1.2 watchEffect的其他参数
1.2.1 停止侦听
watchEffect会返回一个返回值,该返回值是一个函数,在满足条件的时候触发返回值函数。我们可以利用这个返回值做一些操作
这里我们想要实现计数器增加到15的时候停止侦听
<template>
<div>
<h2>{{ counter }}</h2>
<button @click="addCounter">+1</button>
</div>
</template>
<script >
import { ref, watchEffect } from 'vue';
export default{
setup() {
const counter = ref(10)
const addCounter = () => {
counter.value++
if(counter.value >= 15) stop()
}
const stop = watchEffect(() => {
console.log("counter:", counter.value);
})
return {
counter,
addCounter
}
}
}
</script>
自行定义了一个stop接收watchEffect的返回值,在手动触发计数器到15之后不再侦听变量的改变。
1.2.2 消除副作用
我们做一个假设,假设我们需要根据counter的值去触发对应的网络请求,但是在请求数据的过程中,我们再次触发方法改变了counter的值,我们该如何做才能让每次请求回来的都是正确的数据呢?
我们也可以这么想,也就是我们只要改变了age的值,就需要清除上一次的请求,重新执行一次请求。
watchEffect中有一个参数可以作为消除副作用,它会返回一个函数
<template>
<div>
<h2>{{ counter }}</h2>
<button @click="addCounter">+1</button>
</div>
</template>
<script >
import { ref, watchEffect } from 'vue';
export default{
setup() {
const counter = ref(10)
const addCounter = () => {
counter.value++
}
watchEffect( onInvalidate => {
onInvalidate(() => {
console.log("onInvalidate");
})
console.log("counter:", counter.value);
})
return {
counter,
addCounter
}
}
}
</script>
这里可以发现onInvalidate这个函数不会默认执行一次,我们可以利用这个函数的特性完成我们上面提过的网络请求案例
<template>
<div>
<h2>{{ counter }}</h2>
<button @click="addCounter">+1</button>
</div>
</template>
<script >
import { ref, watchEffect } from 'vue';
export default {
setup() {
const counter = ref(10)
const addCounter = () => {
counter.value++
}
watchEffect(onInvalidate => {
const timer = setTimeout(() => {
// 定时器模拟网络请求
console.log("setTimeout");
}, 2000)
onInvalidate(() => {
clearTimeout(timer)
})
console.log("counter:", counter.value);
})
return {
counter,
addCounter
}
}
}
</script>
从状态栏的输出我们也可以发现,当触发了button改变counter的值之后,只要我们再次改变counter的值,定时器模拟的网路请求并不会执行。
二、watch
watch的API完全等同于组件watch选项中的property。watch需要侦听特定的数据源,并在回调中执行副作用;默认情况下它是惰性的,只有当被侦听的源发生变化时才会 执行回调。
watch的三个参数:
-
第一个参数:侦听的源
- 一个getter函数,返回一个值
- 一个ref对象或者一个reactive对象
- 或者是以上类型的值组成的数组
-
第二个参数:发生变化的时候要调用的回调函数,该回调函数接收三个值(按照顺序):
- 新值
- 旧值
- 用于注册副作用的回调函数
-
第三个参数:
immediate
:在侦听器创建时立即触发回调。第一次调用时旧值是undefined
。deep
:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。参考深层侦听器。flush
:调整回调函数的刷新时机。pre | post | synconTrack / onTrigger
:调试侦听器的依赖。
我们一个一个参数的详细介绍一下
2.1侦听一个getter函数
<template>
<div>
<h2>{{ counter }}</h2>
<button @click="addCounter">+1</button>
</div>
</template>
<script >
import { ref, watch } from 'vue';
export default {
setup() {
const counter = ref(10)
const addCounter = () => {
counter.value++
}
watch(() => counter, (newValue, oldValue) => {
console.log("newValue:", newValue, "oldValue:", oldValue);
})
return {
counter,
addCounter
}
}
}
</script>
使用getter函数直接侦听一个ref对象,无法触发侦听器
<template>
<div>
<h2>{{ info.name }}</h2>
<button @click="changeName">+1</button>
</div>
</template>
<script >
import { watch,reactive } from 'vue';
export default {
setup() {
const info = reactive({
name: "小白"
})
const changeName = () => {
info.name = "小刚"
}
watch(() => info.name, (newValue, oldValue) => {
console.log("newValue:", newValue, "oldValue:", oldValue);
})
return {
info,
changeName
}
}
}
</script>
当getter函数接收的是一个对象的时候,可以触发侦听器
说再见
关于watch的侦听有很多细节,贪多嚼不烂,还是老规矩放在下一篇在继续说侦听器watch
难忘今宵
十恶不赦”指的是哪“十恶”?
“十恶”是在中国封建社会十类重罪的总称。包含谋反、谋大逆、谋叛、恶逆、不道、大不敬、不孝、不睦、不义、内乱。