在vue中,使用watch来响应数据的变化,首先介绍一下,watch是一个对象,一定要当对象进行使用。对象则有key和value。

key:代表的是需要监控的东西,比如:路由的变化或者data中某个变量。
value:有三种:

  1. 函数, 即当监控的key 发生变化是,需要执行的函数, 第一个参数是新值,第二个参数是旧值。

  2. 函数名,该函数名需要单引号来包裹。

  3. 包括选项的对象:
    a、第一个handler:其值是一个回调函数。 即监听到变化时应该执行的函数。
    b、第二个是deep: 其值是true或者false; 确认是否需要深入监听。(一般监听是不能监听到对象属性值变化的,数组的值变化可以监听到)
    c、第三个是immediate: 其值是true或者false, 确认是否以当前的初始值执行handle的函数。

一、watch的三个写法:

1、普通数据类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<input type="text" v-model="userName"/>  
//监听 当userName值发生变化时触发
watch: {
userName (newName, oldName) {
console.log(newName)
}
}

watch: {
userName: {
handler (newName, oldName) {
console.log(newName)
},
immediate: true
}
}

缺点:
只有一个缺点,就是当值第一次绑定的时候不会执行监听函数,只有当值改变的时候才会执行,如果我们想在第一次绑定的时候执行此监听函数 则需要设置 immediate为true

2、对象类型:

当需要监听对象的改变时,此时就需要设置deep为true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<input type="text" v-model="cityName.name" />
data (){
return {
cityName: {name:'北京'}
}
},
watch: {
cityName: {
handler(newName, oldName) {
console.log(newName)
},
immediate: true,
deep: true
}
}

注意点:如果对象的属性较多,可以之监听某一个属性’cityName.name’:

3、数组类型:

数组的变化不需要深度监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
data() {
return {
winChips: new Array(11).fill(0)
}
},
watch: {
  winChips: {
    handler(newValue, oldValue) {
      for (let i = 0; i < newValue.length; i++) {
        if (oldValue[i] != newValue[i]) {
          console.log(newValue)
        }
      }
    },
    deep: true
  }
}

二、对immediate和handler的分析

1
2
3
<div id="root">
  <input type="text" v-model="cityName"/>
</div>
1
2
3
4
5
6
7
8
9
10
11
new Vue({
el: '#root',
data: {
cityName: 'shanghai'
},
watch: {
cityName(newName, oldName) {
// ...
}
}
})

这样使用watch时有一个特点,就是当值第一次绑定的时候,不会执行监听函数,只有值发生改变才会执行。如果我们需要在最初绑定值得时候也执行函数,则就需要用到immediate属性。

比如当父组件向子组件动态传值时,子组件props首次获取到父组件传来得默认值时,也需要执行函数,此时就需要将immediate设为true。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
new Vue({
el: '#root',
data: {
cityName: ''
},
watch: {
cityName: {
  handler(newName, oldName) {
  // ...
  },
  immediate: true
}
}
})

监听得数据后面写成对象形式,包含handler方法和immediate,之前我们写得函数其实就是在写这个handler方法;

immediate表示在watch中首次绑定得时候,是否执行handler,值为true则表示在watch中声明得时候,就立即执行handler方法,值为false,则和一般使用watch一样,在数据发生变化的时候才执行handler。

三、对deep的分析

当需要监听一个对象得改变时,普通的watch方法无法监听到对象内部属性的改变,只有data中的数据才能够监听到变化,此时就需要deep属性对对象进行深度监听。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
new Vue({
el: '#root',
data: {
cityName: {id: 1, name: 'shanghai'}
},
watch: {
cityName: {
handler(newName, oldName) {
// ...
},
deep: true,
immediate: true
}
}
})

设置deep:true则可以监听到cityName.name的变化,此时会给cityName的所有属性都加上这个监听器,当对象属性较多时,每个属性值的变化都会执行handler。

如果只需要监听对象中的一个属性值,则可以做以下优化:
使用字符串的形式监听对象属性:

1
2
3
4
5
6
7
8
9
watch: {
'cityName.name': {
handler(newName, oldName) {
// ...
},
deep: true,
immediate: true
}
}

这样只会给对象的某个特定的属性加监听器。

数组(一维、多维)的变化不需要通过深度监听,对象数组中对象的属性变化则需要deep深度监听。

四、使用中应注意的问题

1、在watch中不要使用箭头函数,即不应该使用箭头函数来定义 watcher 函数 , 因为箭头函数中的this是指向当前作用域。
对于箭头函数来说,箭头函数中的 this 指向的是定义时的对象而不是函数运行时所在的对象,即全局定义时的windows对象。

2、开启深度侦听后,触发一次,两个数据一致,这是vue做了处理

a)复杂数据类型的侦听需要开启深度侦听才可以检测到内部数据的改变,但开启深度侦听后,2个参数值是相同的,都是为数据的内存地址,精确到对象的属性名进行深度侦听可以解决此问题。

b)如果修改复杂类型内部的数据也会触发,触发的频率就回比较高,因为复制类型数据可能多个地方使用,如果都会触发侦听器,则会一直执行,因此vue做了这个优化,不发生触发,如果非要侦听,可以使用深度侦听