首页 > 编程笔记 > JavaScript笔记 阅读:12

Vue watch监听的用法(非常详细)

Vue 中的计算属性可以根据已有的一个或多个数据,通过同步计算返回一个新的用于显示的数据。在计算数据的依赖数据发生变化时,计算属性函数会重新执行并返回一个新的值进行显示。

但有的时候,在依赖数据发生变化时,我们需要让其产生一些“副作用”。例如,直接更新 DOM,或者执行异步操作后,更新其他数据。此时我们可以使用 Vue 的监听语法来实现。

Vue监听的基本使用

在选项式 API 中,我们可以通过 watch 选项配置一个函数来监听某个响应式属性的变化。监听回调函数默认在数据发生变化时回调,且接收新值和旧值两个参数。其语法格式如下:
watch: {
    xxx(newVal, oldVal) {
        // 处理 xxx 数据发生变化后的逻辑
    }
}
下面我们利用监听来实现如下图所示的页面效果:


图 1 页面效果

现有两个需求,具体如下:
下面我们对需求进行分析:输入的标题可以通过 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)

相关文章