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
的值。