前一节讲到计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些“副作用”:例如更改 DOM
,或是根据异步操作的结果去修改另一处的状态。
此时计算属性就不再适用,Vue
给我们提供了监视属性/侦听器。
1 介绍
定义 被侦听的属性,在被赋新值时,会触发回调函数handler。
- 当被监视的属性变化时,回调函数handler自动调用
- 监视属性必须存在,才能进行监视
一般写法
watch:{
被监听的属性:{
immediate:true, //初始化时让handler调用一下
handler(newValue,oldValue){
//逻辑
}//当该属性发生改变时调用
}
}
一开始并不会自动调用监视属性里对应的handler函数,可以加上
immediate:true
让handler调用
案例
上面一行显示的天气信息,用插值语法绑定计算属性info
;下面是一个绑定了methods的按钮,点击后会调用methods里的changeWeather
函数;点击按钮,会对天气信息进行更改。
画面
实现代码
<body>
<div id="root">
<h2>今天的天气{{info}}</h2>
<button @click="changeWeather">切换</button>
</div>
</body>
<script type="text/javascript" src="../vue.js">
new Vue({
el:'#root',
data:{
isHot:true
},
computed:{
info(){
return this.isHot ? '炎热' : '凉爽'
//此处为三元表达式
}
},
methods:{
changeWeather(){
this.isHot = !this.isHot
}
}
watch:{
isHot:{
immediate:true, //初始化时让handler调用一下
handler(newValue,oldValue){
console.log(this.isHot,'isHot')
}//当isHot发生改变时调用
},//监视data属性
info:{
immediate:true, //初始化时让handler调用一下
handler(newValue,oldValue){
console.log(this.info,'info')
}//当isHot发生改变时调用
}//监视计算属性
}
})
</script>
三元表达式的补充
点击切换按钮后,调用了绑定的changeWeather
函数,改变了data
中isHot
的值,然后发生以下的影响:
- 监视到
isHot
值发生改变,于是调用了对应handler函数 - 计算属性
info
根据三元表达式返回对应值 - 监视到
info
发生改变,于是调用了对应handler函数
第二种写法
若已经创建好Vue实例,需要新增监视,则可以采用以下这种写法。
vm.$watch('isHot',{
immediate:true, //初始化时让handler调用一下
handler(newValue,oldValue){
console.log(this.isHot,'isHot')
}
})
关于采用哪种监视方法:
(1)在创建实例时,已经清楚要监视谁,就用第一种
(2)反则,需要后续根据用户行为才需要监视,就用第二种
小改写
将按钮绑定的事件不写在methods
中,可以直接写在绑定事件里。
<button @click="isHot = !isHot">切换</button>
绑定事件时候:
@xxx="yyy"
可以写简单的语句,但是不推荐用;
间隔写多个语句
2 深度监视
Vue自身可以检测对象内部值的改变,但Vue中的watch默认不检测对象内部值的改变(默认看一层)。
案例
在data中定义一个对象numbers,有两个属性以及给定的属性值;点击按钮后,numbers中的属性a值+1。
定义
监视多级结构中某个属性的变化,这里是要监视numbers中的a是否变化
代码
<body>
<div id="root">
<h3>a的值是:{{numbers.a}}</h3>
<button @click="numbers.a++">点我a+1</button>
</div>
</body>
<script type="text/javascript" src="../vue.js">
new Vue({
el:'#root',
data:{
numbers:{
a:1,
b:1
}
},
watch:{
//监视多级结构中某个属性的变化
'numbers.a':{
handler(newValue,oldValue){
console.log('a被改变了')
}
}
numbers:{
handler(){
console.log('numbers改变了')
}
}
}
})
</script>
此时点a+1按钮无反应,无法监视numbers{}
里的东西改变,因为其保存的是对象的地址.
监视的对象不能写为
numbers.a
,因为JS中对象的key(即data的numbers)永远是字符串,所以这里要写原始写法
写法
配置deep:true
可以检测对象内部值该改变(多层),根据具体结构决定是否开启
numbers:{
deep:true,//默认为false
handler(){
onsole.log('numbers改变了')
}
}
3 监视的简写形式
前提:配置项只有handler
3.1 在vue里简写
一般:
watch:{
isHot:{
handler(newValue,oldValue){
onsole.log('isHot被修改了',newValue,oldValue)
}
}
简写:
watch:{
isHot(newValue,oldValue){
onsole.log('isHot被修改了',newValue,oldValue)
}
}
3.2 在vue外简写
一般
vm.$watch('isHot',{
immediate:true,
<!--初始化时让handler调用一下-->
deep:true,<!--默认为false-->
handler(newValue,oldValue){
console.log()
}
})
简写
vm.$watch('isHot',funtion(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
})
4 计算属性computed与监视属性watch的区别
- 计算属性能完成的,监视属性都可以完成,优先考虑计算属性
- 监视属性能完成的,计算属性不一定能完成,例如:watch可以进行异步操作
案例
两个输入框分别展示姓和名,然后自动拼成全名,采用定时器延迟一秒显示改变。
有关定时器
- 定时器是在
vue
管理的函数firstname
中开启的,但是其所指定的回调是不受Vue
控制的,而是由浏览器定时器管理模块控制的,定时器到点了是JS
引擎调的,这种地方需要写成箭头函数。- 定时器内若写成了普通函数,控制台打印出的
this
也是window
。
尝试用计算属性实现
<body>
<div id="root">
姓:<input type="text" v-model="firstname" > <br/><br/>
名:<input type="text" v-model="lastname" ><br/><br/>
全名:<span>{{fulName}}</span>
</div>
</body>
<script type="text/javascript" src="../vue.js">
new Vue({
el:'#root',
data:{
firstname:'张',
lastname:'三',
},
//计算属性
computed:{
fulname(val){
setTimeout(() => {
this.fulName = val + '-' + this.lastname
},1000)
}//函数直接当getter()用
lastname(val){
this.fulName = this.firstname + '-' + val
}
}
})
</script>
此时在计算属性的{}里加函数,return的返回值给不到此属性。
监视属性实现
<body>
<div id="root">
姓:<input type="text" v-model="firstname" > <br/><br/>
名:<input type="text" v-model="lastname" ><br/><br/>
全名:<span>{{fulName}}</span>
</div>
</body>
<script type="text/javascript" src="../vue.js">
new Vue({
el:'#root',
data:{
firstname:'张',
lastname:'三',
fulName:'张-三'
},
//监视属性
watch:{
firstname(val){
setTimeout(() => {
this.fulName = val + '-' + this.lastname
},1000)
},
lastname(val){
this.fulName = this.firstname + '-' + val
}
}
})
</script>
箭头函数和普通函数:
- 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vue实例或组件实例对象
- 所有不被vue管理的函数(定时器的回调函数、Ajax的回调函数、Promise的回调函数)最好写成箭头函数,这样this的指向才是Vue实例或组件实例对象