Vue3 侦听器 watch

上一节我们简单的介绍了一下 vue3 项目中的计算属性,这一节我们继续 vue3 的基础知识讲解。

这一节我们来说 vue3 的侦听器。

学过 vue2 的小伙伴们肯定学习过侦听器,主要是用来监听页面数据或者是路由的变化,来执行相应的操作,在 vue3里面呢,也有侦听器的用法,功能基本一样,换汤不换药的东西。 侦听器是常用的 Vue API 之一,它用于监听一个数据并在数据变动时做一些自定义逻辑,本文将先列举侦听器在 Vue 中的使用方式,然后再分析源码讲述为什么可以这样使用、以及侦听器的实现原理。下面我们稍微说一下侦听器。

watch 侦听器使用。

watch API 使用,至少需要指定两个参数: ​​sourc​​​e 和 ​​callback​​,其中 callback 被明确指定只能为函数,所以不同是用方式的差别其实只在 source 。

接下来我们看一下 vue3 侦听器的基本使用:

<template>
<div>
<h1>watch 侦听器</h1>
<el-input v-model="num" />
<br>
<br>
<el-button type="primary" @click="num++">num + 1</el-button>
</div>
</template>
<script>
import { watch, ref } from 'vue'
export default {
setup() {
const num = ref(1)
watch(num, (newVal, oldVal) => {
console.log("新值:", newVal, " 旧值:", oldVal)
})
return { num, }
}
}
</script>
<style scoped>
.el-input {
width: 100px;
}
</style>

上面的代码是页面上有一个数字,点击按钮一下,数字加一,然后侦听器侦听数字的变化,打印出侦听的最新值和老值。

# Vue3 侦听器 watch_数组

OK。上边的案例就是 vue3 侦听器的简单案例,侦听器和计算属性一样,可以创建多个侦听器,这个是没有问题的,案例就不写了,和上一节讲的声明多个计算属性是一致的。如果有不明白的可以看一下我的上一篇博客。

上边我们说过这么一句话,​​watch API 至少需要指定两个参数: source 和 callback​​。通过上边的案例我们看到了, 确实是两个,source 是监听的数据,callback 是监听回调,那为啥说是至少呢?

对的,因为他还有第三个参数 —— ​​配置对象​​。

在 vue2 里面,我们打开页面就像让侦听器立即执行,而不是在第一次数据改变的时候才开始执行,这时候有一个参数叫 ​​immediate​​ ,设置了这个参数,创建第一次就执行,所以说呢,vue3 同样可以使用。

上面的案例刷新执行的时候发现,在点击按钮之前,也就是 num 创建的时候,侦听器是没有执行的,所以说呢,加上 immediate 参数,就可以让侦听器立即执行操作。

<template>
<div>
<h1>watch 侦听器</h1>
<el-input v-model="num" />
<br>
<br>
<el-button type="primary" @click="num++">num + 1</el-button>
</div>
</template>
<script>
import { watch, ref } from 'vue'
export default {
setup() {
const num = ref(1)
watch(num, (newVal, oldVal) => {
console.log("新值:", newVal, " 旧值:", oldVal)
}, {
immediate: true
})
return { num, }
}
}
</script>
<style scoped>
.el-input {
width: 100px;
}
</style>

# Vue3 侦听器 watch_数据_02


我们看到,刷新完页面,还没有点击按钮让 num 加一的,控制台就有数据打印了,为什么呢?就是因为我们加了 immediate 为 true,让侦听器立即执行。控制台输出最新的值也就是我们初始化的值1,老的值没有,所以输出了 undefined。

侦听器监听 reactive

上面说了侦听器侦听单个数据,他也可以用来侦听对象的变化。

<template>
<div>
<h1>watch 侦听器</h1>
<el-input v-model="num.age" />
<br>
<br>
<el-button type="primary" @click="num.age++">num + 1</el-button>
</div>
</template>
<script>
import { watch, ref, reactive } from 'vue'
export default {
setup() {
const num = reactive({
name: '我是𝒆𝒅.',
age: 10
})
watch(num, (newVal, oldVal) => {
console.log(newVal, oldVal)
})
return { num }
}
}
</script>
<style scoped>
.el-input {
width: 100px;
}
</style>

比如说上面代码,我们侦听 num 这个对象的变化。

# Vue3 侦听器 watch_数组_03


看效果我们发下,在监听整个 reactive 响应式对象的时候,确实当里面的属性值发生改变了之后可以被侦听器检测到,但是 newVal 和 oldVal 的值都是新的,默认是10,点击之后,新值是 11 很正常,但是老值不应该是 10 吗?为什么这里老值和新值一样也是 11 呢?

这个不需要疑问哈,如果监听整个 reactive 数据的话,只能回调到最新的值,获取不到老的值。

那问题来喽,我就修改 age 属性,我就要获取 age 老的值怎么办?其实我们只需要监听 num 下面的 age 就可以了,先看下面的代码哈。

<template>
<div>
<h1>watch 侦听器</h1>
<el-input v-model="num.age" />
<br>
<br>
<el-button type="primary" @click="num.age++">num + 1</el-button>
</div>
</template>
<script>
import { watch, ref, reactive } from 'vue'
export default {
setup() {
const num = reactive({
name: '我是𝒆𝒅.',
age: 10
})
watch(num.age, (newVal, oldVal) => {
console.log(newVal, oldVal)
})
return { num }
}
}
</script>
<style scoped>
.el-input {
width: 100px;
}
</style>

我们监听对象直接是 num.age, 监听年龄属性值,保存看一下效果。

# Vue3 侦听器 watch_数据_04


刷新结果我们可以看到哈,我们啥都没干,侦听器直接报了一个警告给我们,啥意思呢,其实不能直接这样监听。

当我们需要监听某个对象属性的时候,我们不能直接对象点属性的方式进行监听,需要传入一个 getter 方法,也就是箭头函数进行监听,下面的代码是正确方式哈。

<template>
<div>
<h1>watch 侦听器</h1>
<el-input v-model="num.age" />
<br>
<br>
<el-button type="primary" @click="num.age++">num + 1</el-button>
</div>
</template>
<script>
import { watch, ref, reactive } from 'vue'
export default {
setup() {
const num = reactive({
name: '我是𝒆𝒅.',
age: 10
})
watch(() => num.age, (newVal, oldVal) => {
console.log(newVal, oldVal)
})
return { num }
}
}
</script>
<style scoped>
.el-input {
width: 100px;
}
</style>

OK,保存刷新,我们发现,侦听器已经不报错了,而且我们点击按钮让 age 加一的时候,可以顺利的监听到 age 的变化,并且回调出最新值和上一次的值。

# Vue3 侦听器 watch_嵌套_05

通过箭头函数,我们就可以实现对象属性的监听。

很多人说,vue2 在监听对象的时候需要对侦听器设置深度侦听,为什么 vue3 这个不需要呢?因为他监听响应式对象,默认就是深度监听。但是,如果监听的是深度嵌套对象或数组中的 property 变化时,仍然需要 deep 选项设置为 true。

看下面的案例,我们监听深层嵌套的 time 属性值。其实我觉得没大必要,不使用箭头函数其实可以。但是还是写一下吧。

<template>
<div>
<h1>watch 侦听器</h1>
<el-input v-model="num.todo.time" />
<br>
<br>
<el-button type="primary" @click="num.todo.time ++">num.todo.time + 1</el-button>
</div>
</template>
<script>
import { watch, ref, reactive, computed, } from 'vue'
export default {
setup() {
const num = reactive({
name: '我是𝒆𝒅.',
age: 10,
todo: {
name: '弹吉他',
time: 1
}
})
watch(() => num, (newVal, oldVal) => {
console.log(newVal.todo.time, oldVal.todo.time)
})
return { num }
}
}
</script>
<style scoped>
.el-input {
width: 100px;
}
</style>

保存代码刷新,发现点击之后没有监听到。

# Vue3 侦听器 watch_嵌套_06

这个时候就可以加上 deep 深度监听。

<template>
<div>
<h1>watch 侦听器</h1>
<el-input v-model="num.todo.time" />
<br>
<br>
<el-button type="primary" @click="num.todo.time ++">num.todo.time + 1</el-button>
</div>
</template>
<script>
import { watch, ref, reactive, computed, } from 'vue'
export default {
setup() {
const num = reactive({
name: '我是𝒆𝒅.',
age: 10,
todo: {
name: '弹吉他',
time: 1
}
})
watch(() => num, (newVal, oldVal) => {
console.log(newVal.todo.time, oldVal.todo.time)
}, { deep: true })
return { num }
}
}
</script>
<style scoped>
.el-input {
width: 100px;
}
</style>

加上深度监听 ​​{ deep:true }​

# Vue3 侦听器 watch_数组_07


我们可以看到打印出信息来了,其实我觉得这个方法有点多余,但是万一用到呢是吧?啊哈哈哈哈,自己根据情况选择使用吧。

但是有一点要注意哈!深度侦听需要遍历被侦听对象中的所有嵌套的 属性,当用于大型数据结构时,开销很大。因此请只在必要时才使用它,并且要留意性能。

监听多个参数执行各自逻辑

本来不打算说了,但是逼逼赖赖这么久了,稍微简单提一下吧。

比如说我们需要监听多个参数,假设两个哈,然后每个参数监听到之后,执行的逻辑是不一样的,我们可以创建多个侦听器来分别监听,不写全部代码了,只写关键代码了哈。

// 第一个
watch(num, (newVal, oldVal) => {
console.log(newVal, oldVal)
})

// 第二个
watch(()=> boy.age, (newVal, oldVal) => {
console.log(newVal, oldVal)
})

监听多个参数执行相同逻辑

这个的意思就是无论是 num 改变还是 boy.age 改变,我执行的代码都是一样的。看下面案例:

<template>
<div>
<h1>watch 侦听器</h1>
<el-input v-model="num.name" />
<el-input v-model="num.age" />
<br>
<br>
<el-button type="primary" @click="num.todo.time ++">num.todo.time + 1</el-button>
</div>
</template>
<script>
import { watch, ref, reactive, computed, } from 'vue'
export default {
setup() {
const num = reactive({
name: '我是𝒆𝒅.',
age: 10,
todo: {
name: '弹吉他',
time: 1
}
})

watch([() => num.name, () => num.age], (newVal, oldVal) => {
console.log(newVal, oldVal)
})
return { num }
}
}
</script>
<style scoped>
.el-input {
width: 100px;
}
</style>

保存刷新页面,修改 name 和 age 的值。

# Vue3 侦听器 watch_数组_08


上面我们把数据源以数组的方式传入,返回的回调参数,新值和旧值都是以数组的方式返回,新值旧值的数组内顺序就是我们数据源传入的顺序,都能看出来哈。

如果你不想让他返回数组你可以这样改一下,其实都差不多,了解一下,根据实际情况选择性使用就行。

<template>
<div>
<h1>watch 侦听器</h1>
<el-input v-model="num.name" />
<el-input v-model="num.age" />
<br>
<br>
<el-button type="primary" @click="num.todo.time ++">num.todo.time + 1</el-button>
</div>
</template>
<script>
import { watch, ref, reactive, computed, } from 'vue'
export default {
setup() {
const num = reactive({
name: '我是𝒆𝒅.',
age: 10,
todo: {
name: '弹吉他',
time: 1
}
})

watch([() => num.name, () => num.age], ([newName, newAge], [oldName, oldAge]) => {
console.log(newName, newAge, oldName, oldAge)
})
return { num }
}
}
</script>
<style scoped>
.el-input {
width: 100px;
}
</style>

保存刷新查看一下效果。

# Vue3 侦听器 watch_数据_09

好了,今天侦听器 watch 的内容主要就是这些,晚安啦宝子们,我们明天见!

【版权声明】本博文著作权归作者所有,任何形式的转载都请联系作者获取授权并注明出处!
【重要说明】本文为本人的学习记录,论点和观点仅代表个人而不代表当时技术的真理,目的是自我学习和有幸成为可以向他人分享的经验,因此有错误会虚心接受改正,但不代表此刻博文无误!
【Gitee地址】秦浩铖:​​https://gitee.com/wjw1014​