首页 > 编程笔记

Vue watch(监听器)用法详解

Vue 中的监听器是一个对象,以 key-value 的形式表示,key 是需要监听的表达式,value 是对应的回调函数,value 也可以是方法名,或者包含选项的对象。

Vue 实例将会在实例化时调用 $watch() 遍历 watch 对象的每一个 property。同时,当差值数据变化时,执行异步或开销较大的操作时,可以通过监听器的方式来达到目的。

监听器在 Vue 实例的 watch 选项中定义,它包括两个参数,第一个参数是监听数据的新值,第二个是旧值。

【实例一】监听 data() 函数中的 message 属性,并在控制台中打印新值和旧值。
<div id="app">
    时:<input type="text" v-model="time">
    分钟:<input type="text" v-model="minute">
</div>
<!--引入Vue文件-->
<script src="https://unpkg.com/vue@next"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
             time:0,
             minute:0
           }
        },
        watch:{
           time(val) {
               this.minute = val * 60;
           },
           // 监听器函数也可以接收两个参数,val是当前值,oldVal是改变之前的值
            minute(val, oldVal) {
                this.time = val / 60;
           }
        }
    //在指定的DOM元素上装载应用程序实例的根组件
    }).mount('#app');
</script>
在 Chrome 浏览器中运行程序,这里将分别监听数据属性 time 和 minute 的变化,当其中一个数据的值发生变化时,就会调用对应的监听器,经过计算得到另一个数据属性的值,结果如下图所示。


图 1 监听属性值的变化

注意,不要用箭头函数来定义 watch 函数。例如:
time:(val) =>{
    this.time = val;
    this.minute = this.time*60
}
因为箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.time 和 this.minute 都是 undefined。

监听方法

在使用监听器的时候,除了直接写一个监听处理函数外,还可以接收一个加字符串形式的方法名,方法在 methods 选项中定义。

【实例二】使用监听器方法。
<div id="app">
    <p>元和角的转换</p>
    <p>元:<input type="text" v-model="yuan"></p>
    <p>角:<input type="text" v-model="jiao"></p>
</div>
<!--引入Vue文件-->
<script src="https://unpkg.com/vue@next"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
             yuan:0,
             jiao:0
           }
        },
        methods:{
            method1:function (val,oldVal) {
                this.jiao=val*10;
            },
            method2:function (val,oldVal) {
                this.yuan=val/10;
            }
        },
        watch:{
            //监听yuan属性,yuan变化时,使jiao属性等于yuan*10
            yuan:"method1",
            //监听jiao属性,jiao变化时,使val属性等于jiao/10
            jiao:"method2"
        }
    //在指定的DOM元素上装载应用程序实例的根组件
    }).mount('#app');
</script>
示例中监听了 yuan 和 jiao 属性,后面直接加上字符串形式的方法名 method1 和 method2,最后在页面中使用 v-model 指令绑定 yuan 和 jiao 属性。

在 Chrome 浏览器中运行程序,在第一个输入框中输入 6,可以发现第二个输入框的值相应地变为 60,如下图所示。


图 2 监听方法

同样,在第二个输入框中输入内容,第一个输入框也会相应地变化。

监听对象

当监听器监听一个对象时,使用 handler 定义数据变化时调用的监听器函数还可以设置 deep 和 immediate 属性。

deep 属性在监听对象属性变化时使用,该选项的值为 true,表示无论该对象的属性在对象中的层级有多深,只要该属性的值发生变化,都会被监测到。

监听器函数在初始渲染时并不会被调用,只有在后续监听的属性发生变化时才会被调用;如果需要监听器函数在监听开始后立即执行,可以使用 immediate 选项将其值设置为 true。

【实例三】监听 goods 对象,在商品价格改变时显示是否可以采购。
<div id="app">
    商品价格:<input type="text" v-model="goods.price">
    <p>{{pess}}</p>
</div>
<!--引入Vue文件-->
<script src="https://unpkg.com/vue@next"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
             pess:'',
            goods: {
                name: '洗衣机',
                price:0
             }
           }
        },
        watch: {
            goods:{
                //该回调函数在goods对象的属性改变时被调用
                handler: function(newValue,oldValue){
                    if(newValue.price>=8000){
                        this.pess="价格太贵了,不可以采购!";
                    }
                    else{
                        this.pess="价格合适,可以采购!";
                    }
                },
                //设置为true,无论属性被嵌套多深,改变时都会调用handler函数
                deep:true
            }
        }
    //在指定的DOM元素上装载应用程序实例的根组件
    }).mount('#app');
</script>
在 Chrome 浏览器中运行程序,在输入框中输入 860,下面会显示“价格合适,可以采购!”,如下图所示。


图 3 输入“860”效果

从上面的示例可以发现,页面初始化时监听器不会被调用,只有在监听的属性发生变化时才会被调用;如果要让监听器函数在页面初始化时执行,可以使用 immediate 选项,将其值设置为 true。
watch: {
    goods:{
        //该回调函数在goods对象的属性改变时被调用
        handler: function(newValue,oldValue){
            if(newValue.price>=8000){
                this.pess="价格太贵了,不可以采购!";
            }
            else{
                this.pess="价格合适,可以采购!";
            }
    },
    //设置为true,无论属性被嵌套多深,改变时都会调用handler函数
    deep:true,
    //页面初始化时执行handler函数
    immediate:true
}
此时在 Chrome 浏览器中运行程序,可以发现,虽然没有改变属性值,也调用了回调函数,显示了“价格合适,可以采购!”,如下图所示。


图 4 immediate选项的作用

在上面的示例中,使用 deep 属性深入监听,监听器会一层一层地往下遍历,给对象的所有属性都加上这个监听器,修改对象里面任何一个属性都会触发监听器里的 handler 函数。

在实际开发过程中,用户很可能只需要监听对象中的某几个属性,设置 deep:true 之后就会增大程序性能的开销。这里可以直接监听想要监听的属性,例如修改上面的示例,只监听 score 属性。

【实例四】监听器对象的单个属性。
<div id="app">
    商品产地:<input type="text" v-model="goods.city">
    <p>{{pess}}</p>
</div>
<!--引入Vue文件-->
<script src="https://unpkg.com/vue@next"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
             pess:'',
            goods: {
                name: '洗衣机',
                city:''
             }
           }
        },
        watch: {
           //监听goods对象的city属性
            'goods.city':{
                handler: function(newValue,oldValue){
                    if(newValue == "上海"){
                        this.pess="商品的产地是上海!";
                    }
                    else{
                        this.pess="商品的产地不是上海!";
                    }
                },
                //设置为true,无论属性被嵌套多深,改变时都会调用handler函数
                deep:true
            }
        }
    //在指定的DOM元素上装载应用程序实例的根组件
    }).mount('#app');
</script>
在 Chrome 浏览器中运行程序,在输入框中输入“北京”,结果如下图所示。


图 5 输入“北京”的效果

注意,监听对象的属性时,因为使用了点号(.),所以要使用单引号(' ')或双引号(" ")将其包裹起来,例如"'goods.city'"。

推荐阅读