Vue:侦听属性
- watch
- 深度侦听
- 异步任务
watch
在Vue中,允许用户在数据改变时,做出一定的处理。
语法:
new Vue({watch:{属性名:{handler(newValue, oldValue){// 函数体} }}
})
当一个属性被写入watch中,每当这个属性值修改,就会调用对应的handler方法,该方法的第一个参数为旧值,第二个参数为新值。
示例:
<div id="root"><button @click="num++">num加一</button>
</div><script type="text/javascript">const vm = new Vue({el:'#root',data:{num: 0},watch:{num:{handler(newValue,oldValue){console.log(oldValue, " -> ", newValue)}}}})
</script>
以上代码中,设置了一个按钮,每次点击按钮num++。在Vue实例中侦听num属性,当num的值发生改变,就会调用handler方法。
输出结果:

如果加上immediate: true属性,那么当整个Vue初始化时,也会调用一次侦听属性的方法:
const vm = new Vue({el:'#root',data:{num: 0},watch:{num:{immediate: true,handler(newValue,oldValue){console.log(oldValue, " -> ", newValue)}}}
})
输出结果:

这次多了一个undefined -> 0的过程,也就是第一次初始化data.num的过程,也触发了侦听属性。
如果侦听属性中不带有任何其他属性,只有一个handler,可以简写:
new Vue({watch:{num(newValue,oldValue){console.log(oldValue, " -> ", newValue)}}
})
也就是直接把被侦听的属性num写为一个函数,而不是一个对象。
深度侦听
当被侦听的属性是一个对象时,修改对象的属性不会触发侦听属性,只有整个对象都被替换,才会触发侦听属性。
<div id="root"><h3>a的值是:{{nums.a}}</h3><button @click="nums.a++">a+1</button><button @click="nums = {a:666,b:888}">彻底替换掉nums</button>
</div><script type="text/javascript">const vm = new Vue({el:'#root',data:{nums:{a:1,b:100}},watch:{nums:{handler(){console.log('nums changed')}}}})
</script>
以上代码中,对对象nums进行侦听,如果属性修改触发回调函数。在nums中包含a b两个属性。在两个按钮中,一个让a++,另一个直接替换掉整个nums。
输出结果:

将a的值一直添加到10,都没有触发侦听属性的回调函数。

替换掉整个nums,才会触发回调,输出nums changed。
如果希望当任意一个属性值被修改时,都能触发对象的回调,可以添加deep: true属性:
nums:{deep:true,handler(){console.log('nums changed')}
}
这样不论对象内部有多少层嵌套,都会触发对象的侦听属性。
异步任务
绝大部分情况下,watch的任务,computed都可以完成,但是watch的功能其实比computed更加强大,比如computed无法处理异步任务。
示例:
<div id="root">姓:<input type="text" v-model="firstName"> <br/><br/>名:<input type="text" v-model="lastName"> <br/><br/>全名:<span>{{fullName}}</span> <br/><br/>
</div><script type="text/javascript">const vm = new Vue({el:'#root',data:{firstName:'张',lastName:'三'},computed: {fullName(){return this.firstName + '-' + this.lastName}}})
</script>
以上代码,是一个通过姓和名得到姓名的过程,fullName通过计算属性实现。
假设要求fullName在姓或名修改后一秒,再输出新名称,这就需要使用setTimeout创建一个异步任务:
computed: {fullName(){let namesetTimeout(()=>{name = this.firstName + '-' + this.lastName},1000);return name}
}
输出:

结果fullName直接不输出了,因为fullName依赖于函数返回值,但是函数返回值是name,这个变量一开始是let name,随后进入异步任务setTimeout中拿到name = this.firstName + '-' + this.lastName,不过当异步任务还没有返回,就直接return name了,这导致name = undefined,Vue直接不输出。
watch就可以处理异步任务:
new Vue({el:'#root',data:{firstName:'张',lastName:'三',fullName:'张-三'},watch:{firstName(val){setTimeout(()=>{this.fullName = val + '-' + this.lastName},1000);},lastName(val){setTimeout(()=>{this.fullName = val + '-' + this.lastName},1000);}}
})
对firstName和lastName进行侦听,在侦听的函数内部,执行异步任务setTimeout,修改fullName。由于fullName是一个存在于data内部的变量,当一秒后异步任务执行完毕,就会修改data内的值,导致模板重新渲染,实现一秒后更新fullName的值。