在vue中,使用watch来响应数据的变化。
一、 watch是什么?
监测 Vue 实例变化的一个表达式或方法。回调函数得到的参数为新值和旧值,用一个函数取代。
简洁的说:watch的作用可以监控一个值的变换,并调用因为变化需要执行的方法。可以通过watch动态改变关联的状态。
watch函数的参数中,第一个是改变之前的值,第二个是改变之后的值, 这两个参数非常有用。
watch的2个参数 (newValue, oldValue)
1.需要新旧对比时 可以用到第二个参数
2.参数不写,直接通过 this.数据的方式 也可以获取到最新的数据
AcNo(val, oldVal) {
if(val != oldVal) {
const params = {
AcNo: this.AcNo
}
this.$http.post('/eweb/eweb-query.AcInfoQry.do', params).then(res => {
this.Balance = res.AvailBal
this.Currency = res.Currency
this.AcName = res.AcName
})
}
},
二、应用场景
当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。例如ajax请求,复杂的业务逻辑处理等。
三、应用
<1>基础用法
数据初始化
data() {
return {
a: 1,
b: 'enen',
shipStatusArr: {
name: 'zhangsanlisi',
age: 12
}
}
}
mounted
mounted () {
this.a = 2
this.shipStatusArr.name = 'lisi'
}
watch
(1)watch基本数据类型(string,number等)
watch: {
a: function (newValue, oldVal) {
console.log( newValue, oldVal )
}
// 也可以写成
a(newValue, oldVal){
console.log( newValue, oldVal )
}
}
// handler方法和immediate属性
// 上面的例子是值变化时候,watch才执行,我们想让值最初时候watch就执行就用到了handler和immediate属性
watch: {
a: {
handler(newName, oldName) {
this.fullName = newName + ' ' + this.lastName;
},
// 代表在wacth里声明了a这个方法之后立即先去执行handler方法,如果设置了false,那么效果和上边例子一样
immediate: true
}
}
(2)watch对象/数组(复杂数据类型)
# deep属性
需要使用handler函数,并且开启深度侦听deep
当需要监听一个对象的改变时,普通的watch方法无法监听到对象内部属性的改变,只有data中的数据才能够监听到变化,此时就需要deep属性对对象进行深度监听。
当监测为对象的时候(非数组情况),deep = true 可以监测对象中属性的变化,并且(监测为对象的时候,newVal == oldVal)
此时,需要watch对象的某一具体属性才可以监听到数据的变化,具体做法是使用对象点方法获取到属性,并且用引号
(1)如果要观察data下一个对象的属性,可以使用 '对象.属性' 的方式, 注意: 一定要要引号。
(2)如果改变了一个对象的属性,就必须使用 deep: true, 否则检测不到变化。
(3)数组(一维、多维)的变化不需要通过深度监听,对象数组中对象的属性变化则需要deep深度监听。
watch: {
shipStatusArr: {
handler(newValue, oldValue) {
console.log(newValue, oldValue)
},
deep: true // 深度监听
}
}
// 下面的做法,检测对象的时候, newVal == oldVal
watch: {
obj: {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true
}
}
// 延伸
受现代 JavaScript 的限制 (以及废弃 Object.observe),Vue 不能检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化过程,所以属性必须在 data 对象上存在才能让 Vue 转换它,这样才能让它是响应的。
默认情况下 handler 只监听obj这个属性它的引用的变化,我们只有给obj赋值的时候它才会监听到,比如我们在 mounted事件钩子函数中对obj进行重新赋值
如果此时,需要对obj对象里的属性a的值作监测,可以这样处理,这时候deep属性就派上用场了
watch: {
obj: {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true,
deep: true
}
}
这样的方法对性能影响很大,修改obj里面任何一个属性都会触发这个监听器里的 handler。我们可以做如下处理:
watch: {
'obj.a': {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true,
// deep: true
}
}
<2>进阶用法
(1)监听对象的某一属性
以上监听shipStatusArr的用法,会在shipStatusArr中任一属性变化时触发,如果是只监听shipStatusArr的name变化,更优写法是:
watch: {
'shipStatusArr.name': function (newValue, oldVal) {
console.log( newValue, oldVal )
}
}
设置deep: true 则可以监听到shipStatusArr.name的变化,此时会给shipStatusArr的所有属性都加上这个监听器,当对象属性较多时,每个属性值的变化都会执行handler。如果只需要监听对象中的一个属性值,则可以做以下优化:使用字符串的形式监听对象属性。这样只会给对象的某个特定的属性加监听器。
(2)immediate属性
进入组件的时候,第一次并不会执行watch,只有值发生改变才会执行 , 是因为immediate 默认 false
当 immediate = true 的时候,进入组件会立即执行。并且可以监测到组件传递数据。
回调将会在侦听开始之后被立即调用。
如果我们需要在最初绑定值的时候也执行函数,则就需要用到immediate属性。
监听的复杂数据后面写成对象形式,包含handler方法和immediate,之前我们写的函数其实就是在写这个handler方法。
immediate表示在watch中首次绑定的时候,是否执行handler,值为true则表示在watch中声明的时候,就立即执行handler方法,值为false,则和一般使用watch一样,在数据发生变化的时候才执行handler。
watch: {
shipStatusArr: {
handler(newValue, oldValue) {
console.log(newValue, oldValue)
},
deep: true,
immediate: true
}
}
四、注意问题
(1)在watch中不要使用箭头函数,即不应该使用箭头函数来定义 watcher 函数 , 因为箭头函数中的this是指向当前作用域
(2)开启深度侦听后,触发一次,两个数据一致,这是vue做了处理
=>复杂数据类型的侦听需要开启深度侦听才可以检测到内部数据的改变,但开启深度侦听后,2个参数值是相同的,都是为数据的内存地址,精确到对象的属性名进行深度侦听可以解决此问题
=>如果修改复杂类型内部的数据也会触发,触发的频率就回比较高,因为复制类型数据可能多个地方使用,如果都会触发侦听器,则会一直执行,因此vue做了这个优化,不发生触发,如果非要侦听,可以使用深度侦听
五、工作中实际运用
情景一
有时候,在使用watch侦听数据变化后 , 需要清空某些数据 , 同时用计算属性对该字段进行判断
如果直接用等于号赋值清空 , 计算属性由于有缓存而导致 , 计算属性认为被清空的数据没有变化 , 从而不会发生重新计算
需要使用$set清空数据 , 并且让被清空的字段是响应式的
如下所示
Snipaste_2020-07-09_19-35-48.png
可以给watch检测的变量赋值
watch: {
Qmoney(newValue, oldValue){
if (newValue != oldValue){
this.Qmoney = newValue
this.RemainingSum = parseFloat(this.detail.BillBalance) - this.Qmoney
}
}
},
情景二
watch监听的值可以不判断条件,只要数据发生变化就执行某些逻辑
比如 搜索功能实现
watch: {
search (val) {
console.log('watch---',val)
// if(val){ // 这里仅仅是判断val有值的情况,但不涵盖val为空,对于搜索功能,只考虑数据变化(包含数据为空的情况)执行数据获取接口即可,所以把if条件去掉即可
this.CurrentIndex = '0'
this.BankName = val
this.List3 = []
this.getData('watch')
// }
}
},