【 Vue全家桶 · Vue(六)】Vue框架中的监听机制之二 —— 侦听器watch

上一篇中详细介绍了Vue框架中的计算属性computed,我们说它常用来做数据监听,比如典型的多对一情况 —— 多数据监听和深度监听。

一. watch适用场景

但这并不代表着计算属性computed可以胜任所有的监听场景。其实Vue框架中还有一种更通用,更正统的监听方式 —— 监听器watch。理论上他可以完成所有的监听工作。

监听器watch的监听适用于一对多的场景,即某个组件内的一个数据的改变会影响其他多个数据。

在这里插入图片描述


❀ 拓展一下❀

区别于计算属性computed的多对一场景,有关计算属性computed的讲解请看上一篇:

【Vue学习笔记 · 基础篇(五)】Vue框架中的监听机制之一 —— 计算属性computed


侦听器watch有两种书写形式,分别为:

  • Vue实例内部属性watch
  • Vue实例方法vm.$watch

这两种写法本质上是等价的,需要的参数也一模一样,唯一的区别就是书写位置不同而带来的格式上的差别。所以接下来的讲解就重点以Vue实例内部属性watch 形式为例,之后vm.$watch那一部分会给出对应的等价形式,可以对比选择。

二. Vue实例内部属性 watch

2.1 基础形式

  • watchObject(监听对象 —— data中的属性(Number / String / Boolean / Array))
  • callback —— 回调函数
    • newVal(非必须)—— 改变后的新值(可单选此项)
    • oldVal(非必须)—— 改变前的旧值
new Vue({
    
    
    el:"#app",
    data:{
    
    
        watchObject:''
    },
    watch:{
    
    
        watchObject:function(newVal,oldVal){
    
    
        // something...
        }
    }
})

来看几个简单的例子:

① 拿到改变前后的值:

比如我们在页面中添加一个输入框,使用v-model指令双向绑定username属性。我们希望监听该输入框的输入情况:每次输入一个字符,就在控制台打印出本次输入前后的值。

<div id="app">
    <input type="text" v-model="userName">
</div>
let vm = new Vue({
    
    
    el:"#app",
    data:{
    
    
        username:''
    },
    watch:{
    
    
        username:function(newName,oldName){
    
    
            console.log(newName,oldName)
        }
    }
})

在这里插入图片描述
② 调用methods中的方法:

根据实际需求不同,我们也可以选择不同的参数配置。比如拿到当前变化之后的值,作为参数调用methods中的方法func

new Vue({
    
    
    el:"#app",
    data:{
    
    
        watchObject:''
    },
    watch:{
    
    
        watchObject:function(val){
    
    
            this.func(val)
        }
    },
    methods:{
    
    
        func:function(val){
    
    
            console.log(val)
        }
    }
})

要注意的是回调函数中也可以只接收一个参数 —— 默认的是改变之后的值。

2.2 一对多的实现原理

仔细分析侦听器watch的基础形式,就可以看出来他为什么适用于一对多的场景了:

① 监听对象为一个具体的实例data属性;

言外之意就是如果你选择使用侦听器watch来做多数据监听,那么不好意思,你只能一个一个的监听。
比如之前计算属性computed中的两数相加add的例子:

data:{
    
    
    num1:0,
    num2:2
},
computed:{
    
    
    add:function(){
    
    
        return this.num1 + this.num2
    }
}

侦听器watch的版本(繁易程度显而易见):

data:{
    
    
    num1:0,
    num2:2,
    result:2
},
watch:{
    
    
    num1:function(val){
    
    
        this.result = val + this.num2
    },
    num2:function(val){
    
    
        this.result = val + this.num1
    }
}

② 回调函数体内可选择调用多个methods中的方法;

之前计算属性computedgetter函数体内,必须以return语句结尾,意思就是计算属性computed最后都会归结到一个值上;而在侦听器watch中就没有这个限制,可以选择调用多个methods中的方法,即对多个数据进行操作。

computed:{
    
    
    reverseMsg:function(){
    
    
        return this.msg.split('').reverse().join('')
    }
}

2.3 完整形式

侦听器watch的完整形式包括了三个属性:handlerdeepimmediate。之前的基础形式就是只设置了handler函数的完整形式的简写形式。
各属性的讲解如下:

  • watchObject(监听对象 —— data中的属性(Number / String / Boolean / Array / Object))
    • handler —— 回调函数
      • newVal(非必须)—— 改变后的新值(可单选此项)
      • oldVal(非必须)—— 改变前的旧值
    • deep(非必须)—— 是否开启深度监听
    • immediate(非必须)—— 是否在数据绑定阶段也执行handler函数
new Vue({
    
    
    el:"#app",
    data:{
    
    
        watchObject:''
    },
    watch:{
    
    
        watchObject:{
    
    
            handler:function(newVal,oldVal){
    
    
                // something...
            },
            deep:true,
            immediate:true
        }
    }
})

2.3.1 watchObject 监听对象

不知道你们注意到没有,这一次监听对象列表里我新添了Object对象,就是说之前讲的基础形式不能用来监听对象内部属性。因为deep属性默认为false,需要我们使用完整写法来手动开启深度监听。

比如以下的监听写法就会失效:

new Vue({
    
    
    el:"#app",
    data:{
    
    
        Obj:{
    
    
            username:''
        }
    },
    watch:{
    
    
        Obj:function(newVal,oldVal){
    
    
            console.log(newVal)
        }
    }
})

2.3.2 handler 函数

handler函数是监听到对象内任何一个属性的变化后都会执行的回调函数。要注意的是这里必须使用handler关键字,不能为自定义函数名。

2.3.3 deep 深度监听属性

之前说过计算属性computed中默认为深度监听,而在侦听器watch中要想完成对对象内部属性的深度监听,就需要手动设置deep:true。比如2.3.1中的例子可以这么写:

new Vue({
    
    
    el:"#app",
    data:{
    
    
        Obj:{
    
    
            username:''
        }
    },
    watch:{
    
    
        obj:{
    
    
            handler:function(newVal,oldVal){
    
    
                console.log(newVal)
            },
            deep:true
        }
    }
})

当然不使用deep属性也是阔以的:在设置监听对象时,使用键路径(点访问符选择对象内部属性)。比如上例的等价形式为:

// 完整形式
watch:{
    
    
    obj.username:{
    
    
        handler:function(newVal,oldVal){
    
    
            console.log(newVal)
        }
    }
}
// 基础形式
watch:{
    
    
    obj.username:function(newVal,oldVal){
    
    
        console.log(newVal)
    }
}

2.3.4 immediate 立即执行属性

我们都知道监听的原理就是在发现监听对象发生变化之后会去执行某些操作。所以在没有触发监听的阶段,比如数据绑定阶段,就不会执行监听里的handler函数。如果这个时候我们就想在数据绑定阶段也执行一次handler函数就需要设置immediate:true

比如常见的数据初始化操作(数据绑定阶段调用初始化函数):

<div id="app">
    {
    
    {
    
    Obj}}<br>
    <input type="text" v-model="Obj.username">
</div>
new Vue({
    
    
    el:"#app",
    data:{
    
    
        Obj:{
    
    
            username:''
        }
    },
    watch:{
    
    
        obj:{
    
    
            handler:function(){
    
    
                // 属性值为空时调用初始化函数
                if(!this.Obj.username){
    
    this.initFunc()} 
            },
            deep:true,
            immediate:true
        }
    },
    methods:{
    
    
        initFunc:function(){
    
    
            this.Obj.username = "zevin"
        }
    }
})

在这里插入图片描述

三. Vue实例方法 vm.$watch

Vue框架中也为我们提供了丰富的API,很多Vue实例内的属性和方法都有对应的API。附上官方API文档:

Vue官方API文档地址

vm.$watch就是监听属性watch的对应API,以下为官方文档的API介绍截图:
在这里插入图片描述
这里就不过多介绍了,用法和之前的监听属性watch都一样,只是书写格式不同,这里就简单列出对应的基础形式和完整形式:

3.1 基础形式

  • watchObject(监听对象 —— data中的属性(Number / String / Boolean / Array))
  • callback —— 回调函数
    • newVal(非必须)—— 改变后的新值(可单选此项)
    • oldVal(非必须)—— 改变前的旧值
vm.$watch(watchObject, function (newVal, oldVal) {
    
    
  // something
})

3.2 完整形式

  • watchObject(监听对象 —— data中的属性(Number / String / Boolean / Array / Object))
  • callback —— 回调函数
    • newVal(非必须)—— 改变后的新值(可单选此项)
    • oldVal(非必须)—— 改变前的旧值
  • options —— 可选属性(对象)
    • deep(非必须)—— 是否开启深度监听
    • immediate(非必须)—— 是否在数据绑定阶段也执行handler函数
 vm.$watch(watchObject,function () {
    
    
      doSomething()
  },
  {
    
     
      deep:true,
      immediate: true
   }
)

需要注意的区别点在于:

  1. 在实例外调用;
  2. 回调函数使用function关键字而不是handler
  3. 最后可选属性deepimmediate需要封装到对象内;

猜你喜欢

转载自blog.csdn.net/JZevin/article/details/108469140