首页 > 编程笔记 > JavaScript笔记
阅读:12
Vue watch监听的用法(非常详细)
Vue 中的计算属性可以根据已有的一个或多个数据,通过同步计算返回一个新的用于显示的数据。在计算数据的依赖数据发生变化时,计算属性函数会重新执行并返回一个新的值进行显示。
但有的时候,在依赖数据发生变化时,我们需要让其产生一些“副作用”。例如,直接更新 DOM,或者执行异步操作后,更新其他数据。此时我们可以使用 Vue 的监听语法来实现。

图 1 页面效果
现有两个需求,具体如下:
下面我们对需求进行分析:输入的标题可以通过 v-model 收集到 data 对象中的 title 属性上,同时用 watch 选项来监听 title 属性的变化,在监听回调函数中将最新的值设置给文档的标题。同时发送 AJAX 请求,根据 title 属性获取一个最匹配的问题并设置给 data 对象中的 question 属性,显示到输入框下面。
实现代码如下所示,Vue 代码如下:
其实 watch 选项不仅可以监听 data 对象中外部的属性,还可以监听其内部的属性。现有一个 person 对象,内部有 name 和 age 两个属性,我们使用 watch 选项对 name 属性进行监听。其语法格式如下:
Vue 的 watch 语法也支持这种场景需求,但是用法有些许差别,watch 的属性不能再是一个函数了,需要是一个配置对象,并通过 handler 配置来指定监听回调函数。同时我们可以通过配置 immediate 为 true 来指定监听回调函数在初始化过程中执行第 1 次,当然在被监听数据发生改变时也会执行。
思考下面代码的运行效果。Vue 代码如下:
JavaScript 代码如下:

图 2 页面效果(1)
由于我们配置了 immediate 为 true,因此 watch 的回调在初始化时就会执行 1 次。我们又通过定时器模拟异步向后台提交请求,即提交当前 person 的信息数据,如下图所示。

图 3 模拟异步向后台提交请求(1)
点击“更新人员信息”按钮,直接将当前 person 更新为一个新的人员对象,监听的回调也会执行,用来模拟异步向后台提交请求,发送最新的人员信息,如下图所示。

图 4 点击“更新人员信息”按钮后页面发生变化

图 5 模拟异步向后台提交请求(2)
watch 默认是浅层监听,只有在被监听属性本身发生变化时才会触发回调,它监听不到属性对象内部的数据变化。
对于上面的案例来说,如果只是更新 person 对象中的内部属性,比如 name 属性,那么 watch 的回调就不会执行。
点击“更新人员信息”按钮后,页面上的 name 属性值会发生改变,但是不会触发 watch 中的异步操作,因此页面上不会弹出警告提示,如下图所示。

图 6 点击“更新人员信息”按钮后的页面效果
那么如何才能深度监听到对象或数组内任意数据的变化呢?其实可以通过将 deep 配置为 true 来实现。也就是说,deep 的默认值为 false,因此之前 watch 监听不到内部数据的修改。
现在修改一下 watch 的配置,具体如下:

图 7 模拟异步向后台提交请求(3)
其实,无论是更新 person 对象的 name 属性,还是更新 person 对象中的 likes 属性,监听的回调都会调用,会将最新的人员信息提交给后台,如下代码所示:

图 8 页面效果(2)

图 9 模拟异步向后台提交请求(4)
但有的时候,在依赖数据发生变化时,我们需要让其产生一些“副作用”。例如,直接更新 DOM,或者执行异步操作后,更新其他数据。此时我们可以使用 Vue 的监听语法来实现。
Vue监听的基本使用
在选项式 API 中,我们可以通过 watch 选项配置一个函数来监听某个响应式属性的变化。监听回调函数默认在数据发生变化时回调,且接收新值和旧值两个参数。其语法格式如下:watch: { xxx(newVal, oldVal) { // 处理 xxx 数据发生变化后的逻辑 } }下面我们利用监听来实现如下图所示的页面效果:

图 1 页面效果
现有两个需求,具体如下:
- 输入标题,实时同步显示到页面标题上;
- 输入标题,实时 AJAX 请求获取一个最匹配的问题并显示到输入框下面。
下面我们对需求进行分析:输入的标题可以通过 v-model 收集到 data 对象中的 title 属性上,同时用 watch 选项来监听 title 属性的变化,在监听回调函数中将最新的值设置给文档的标题。同时发送 AJAX 请求,根据 title 属性获取一个最匹配的问题并设置给 data 对象中的 question 属性,显示到输入框下面。
实现代码如下所示,Vue 代码如下:
<div id="app"> 标题:<input type="text" placeholder="输入内容可同步到标题" v-model="title"><br> 问题:<span>{{question}}</span> </div> JavaScript 代码如下: const { createApp } = Vue createApp({ data () { return { title: '', question: '' } }, watch: { title (newVal, oldVal) { console.log(newVal, oldVal) // 直接更新 DOM document.title = newVal // 模拟请求异步获取对应答案 setTimeout(() => { const question = `S(newVal)最匹配的问题?` this.question = question }, 1000) }, } }).mount('#app')运行代码后,我们在输入框中输入 Vue,会发现 1 秒后显示问题。
其实 watch 选项不仅可以监听 data 对象中外部的属性,还可以监听其内部的属性。现有一个 person 对象,内部有 name 和 age 两个属性,我们使用 watch 选项对 name 属性进行监听。其语法格式如下:
data () { return { person: { name: 'tom', age: 12 } } }, watch: { // 监听 data 中 person 对象的 name 属性的变化 'person.name': function (newVal, oldVal) { // 处理 person 对象中 name 属性变化后的逻辑 } }
即时回调与深度监听
watch 回调默认是在数据发生变化时自动调用,如果我们需要在初始化时就执行一次监听的回调,那么要如何实现呢?Vue 的 watch 语法也支持这种场景需求,但是用法有些许差别,watch 的属性不能再是一个函数了,需要是一个配置对象,并通过 handler 配置来指定监听回调函数。同时我们可以通过配置 immediate 为 true 来指定监听回调函数在初始化过程中执行第 1 次,当然在被监听数据发生改变时也会执行。
思考下面代码的运行效果。Vue 代码如下:
<div id="app"> <h3>name:{{person.name}}</h3> <h3>likes:{{person.likes}}</h3> <button @click="updateP">更新人员信息</button> </div>
JavaScript 代码如下:
const { createApp } = Vue createApp({ data () { return { title: '', question: '' } }, watch: { title (newVal, oldVal) { console.log(newVal, oldVal) // 直接更新 DOM document.title = newVal // 模拟请求异步获取对应答案 setTimeout(() => { const question = `S(newVal)最匹配的问题?` this.question = question }, 1000) }, } }).mount('#app')运行代码后,页面上展示了 person 中的 name 属性和 likes 属性,如下图所示:

图 2 页面效果(1)
由于我们配置了 immediate 为 true,因此 watch 的回调在初始化时就会执行 1 次。我们又通过定时器模拟异步向后台提交请求,即提交当前 person 的信息数据,如下图所示。

图 3 模拟异步向后台提交请求(1)
点击“更新人员信息”按钮,直接将当前 person 更新为一个新的人员对象,监听的回调也会执行,用来模拟异步向后台提交请求,发送最新的人员信息,如下图所示。

图 4 点击“更新人员信息”按钮后页面发生变化

图 5 模拟异步向后台提交请求(2)
watch 默认是浅层监听,只有在被监听属性本身发生变化时才会触发回调,它监听不到属性对象内部的数据变化。
对于上面的案例来说,如果只是更新 person 对象中的内部属性,比如 name 属性,那么 watch 的回调就不会执行。
点击“更新人员信息”按钮后,页面上的 name 属性值会发生改变,但是不会触发 watch 中的异步操作,因此页面上不会弹出警告提示,如下图所示。

图 6 点击“更新人员信息”按钮后的页面效果
那么如何才能深度监听到对象或数组内任意数据的变化呢?其实可以通过将 deep 配置为 true 来实现。也就是说,deep 的默认值为 false,因此之前 watch 监听不到内部数据的修改。
现在修改一下 watch 的配置,具体如下:
watch: { person: { handler (newVal, oldVal) { // 模拟异步将人员信息提交给后台 setTimeout(() => { alert('向后台提交人员信息' + JSON.stringify(newVal)) }, 1000) }, immediate: true, // 标识立即执行:也就是在初始化时会执行第1次 deep: true, // 深度监听 } }此时我们再次重复当前的操作,可以发现除了页面发生变化,1 秒后还弹出了警告提示,证明此时已经监听到内部数据了,如下图所示。

图 7 模拟异步向后台提交请求(3)
其实,无论是更新 person 对象的 name 属性,还是更新 person 对象中的 likes 属性,监听的回调都会调用,会将最新的人员信息提交给后台,如下代码所示:
updateP () { // 更新 person 对象中的 likes 属性 this.person.likes.push('atguigu') }重复点击“更新人员信息”按钮,这次 likes 属性的数组中新增了一项“atguigu”,这个修改同样被监听到了,也会异步向后台提交数据,如下图所示。

图 8 页面效果(2)

图 9 模拟异步向后台提交请求(4)