跳到主要内容位置

Vue 3 使用 watch 监听数据的变化

峰华前端工程师
软件工程师 / B站UP主

我们知道在 Vue 应用中,当 data 里边的属性变化的时候,HTML 模板中使用到这个属性的地方,会自动更新,但是如果我们想在 JavaScript 中,监听某个属性的变化,并做一些操作,该怎么办呢?这个时候可以使用 Vue 提供的 watch 选项,为属性添加一个监听器。

Watch 的应用场景​

watch 适合根据属性的变化,做一些开销比较大的操作,或者异步操作,例如设置定时器,请求远程 API 等,它不会返回任何值,而是根据属性的变化,直接修改其它属性的值,这样其它属性修改之后,就能够在 Vue 的响应性机制下,自动更新依赖这个属性的 HTML 视图部分。

Watch 的使用方法​

watch 的值也是一个对象,对象里边是一系列的方法,每个方法名跟要监听的属性名保持一致,这里要记住,不能像 computed 计算属性或者 methods 里边的方法那样,自定义方法的名字。

  data() {
return {
showAnswer: false
}
},
watch: {
showAnswer() {
},
},

在监听某个属性的方法中,它接收两个参数,一个是属性变化后的值,一个是属性变化前的值:

watch: {
showAnswer(newVal, oldVal) {
},
},

在方法体里,我们可以进行一些计算量比较大的操作,或者异步操作,例如设置一个间隔定时器:

watch: {
showAnswer(newVal, oldVal) {
if (newVal) {
this.countDown = 5;
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
// 5秒后关闭答案
this.timer = setInterval(() => {
this.countDown -= 1;
if (this.countDown === 0) {
this.showAnswer = false;
clearInterval(this.timer);
this.timer = null;
}
}, 1000);
}
},
},

当方法名对应的属性值发生变化时,这个监听器就会自动执行,并且每发生一次变化都会执行一次。

示例​

我们来继续看上一节课的示例,在上节课,我们使用 methods 配置,给按钮加上了点击事件处理函数,这节课我们假设在显示题目的答案之后,程序会自动添加一个倒数计时器,5 秒之后自动隐藏答案,并把倒数的秒数显示在按钮的文本标签的后面,我们来看看如何利用 watch 来实现。 首先打开我们项目的代码,打开 index.js 文件,我们添加一个新的属性,countDown,来保存当前的倒数秒数,初始值为 5:

  data() {
return {
showAnswer: false,
countDown: 5,
};
},

之后把 countDown 加到按钮的文本标签中,在 computed 里的 label() 中,如果此时是显示答案的,那么就把倒计时秒数添加到按钮文本的后面,也就是当按钮显示为『隐藏答案』的时候:

computed: {
label() {
return this.showAnswer ? "隐藏答案 " + this.countDown : "显示答案";
},
},

接着我们再添加一个 timer 属性,保存间隔定时器的 id,用于后续清除定时器,它的默认值为 null,也就是说目前还没有创建定时器:

data() {
return {
showAnswer: false,
countDown: 5,
timer: null,
};
},

之后我们在 methods 下边新添加一个 watch 配置项,它的值是一个对象:

  watch: {

},

在它的里边,我们定义一个名字为showAnswer的方法,这个方法名跟 data 中的 showAnswer 属性保持一致,这个是必须的,这样每次 showAnswer 属性值发生变化时,都会调用 watch 中的 showAnswer 方法:

watch: {
showAnswer() {
},
},

showAnswer 方法接收两个参数,第一个是变化后的值,第二个是变化前的值,我们给变化后的值命名为 newVal,这个可以自定义,变化前的值我们不需要,可以不用管它:

watch: {
showAnswer(newVal, oldVal) {
},
},

接下来,在方法体中判断,如果 newVal 为 true,也就是说当前 showAnswer 属性的值变为了 true,显示了答案,那么我们把 this.countDown 的值先还原为 5,因为上一次的计时器可能已经把它改为 0 了:

watch: {
showAnswer(newVal, oldVal) {
if (newVal) {
this.countDown = 5;
}
},
},

(这里可以先展示一下错误示范,再回过来加上这段代码)接着判断如果 timer 有值的话,先把它清除,这里是因为有可能倒计时还没有结束,用户就手动点击了隐藏答案,然后又点击了显示答案,这个时候定时器不会自己清除,反复点击的话会注册多个定时器,多个定时器会同时修改 countDown 的值,这样会导致倒计时出现数值减的过快,或数值减的过多的问题:

watch: {
showAnswer(newVal, oldVal) {
if (newVal) {
this.countDown = 5;
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
}
},
},

接下来是正常的情况下,我们使用 setInterval() 创建一个间隔定时器,间隔时间为 1 秒:

watch: {
showAnswer(newVal, oldVal) {
if (newVal) {
this.countDown = 5;
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
// 5秒后关闭答案
this.timer = setInterval(() => {

}, 1000);
}
},
},

在间隔定时器的回调函数中,我们把倒计时秒数 countDown 属性进行减 1 操作,然后判断如果倒计时秒数等于 0,那么就把显示答案的属性 showAnswer 设置为 false,隐藏答案,然后清除掉当前的计时器,把 timer 属性还原成 null:

watch: {
showAnswer(newVal, oldVal) {
if (newVal) {
this.countDown = 5;
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
// 5秒后关闭答案
this.timer = setInterval(() => {
this.countDown -= 1;
if (this.countDown === 0) {
this.showAnswer = false;
clearInterval(this.timer);
this.timer = null;
}
}, 1000);
}
},
},

这样就实现了倒计时的过程,我们运行一下项目看一下效果,点击显示答案之后,可以看到倒计时秒数添加到了按钮的文本后面,之后等待 5 秒,可以看到答案就自动隐藏了。这里 watch 就是根据 showAnswer 的值的变化而自动执行了,我们在里边判断,如果当前为显示答案,那么就添加一个间隔定时器。 我们也可以试试反复点一下按钮,也可以看到倒计时也还是能正常的进行。

小结​

这个就是 watch 的使用方法,当需要在 Vue 的 js 代码中监听 data 属性的变化时,我们就在 watch 配置对象里,添加一个同名的方法,方法接收两个参数,一个是属性变化后的值,一个是属性变化前的值,这样就可以在方法里边根据属性值,做一些异步的或者耗时的业务逻辑了。

提示

一系列的课程让你成为高级前端工程师。课程覆盖工作中所有常用的知识点和背后的使用逻辑,示例全部都为工作项目简化而来,学完即可直接上手开发!

即使你已经是高级前端工程师,在课程里也可能会发现新的知识点和技巧,让你的工作更加轻松!

《React 完全指南》课程,包含 React、React Router 和 Redux 详细介绍,所有示例改编自真实工作代码。点击查看详情。

《Vue 3.x 全家桶完全指南与实战课程》课程,包括 Vue 3.x、TypeScript、Vue Router 4.x、Vuex 4.x 所有初级到高级的语法特性详解,让你完全胜任 Vue 前端开发的工作。点击查看详情。

《React即时通信UI实战》课程,利用 Storybook、Styled-components、React-Spring 打造属于自己的组件库。

《JavaScript 基础语法详解》本人所著图书,包含 JavaScript 全面的语法知识和新特性, 可在京东、当当、淘宝等各大电商购买