computed计算属性
先解读下官方文档,conputed的设计的初衷用于简答的运算,模板内不宜放入太多的逻辑,否则维护艰难。
<div id="app">{{message.split("").reverse().join("")}}</div>
<script>
var vm = new Vue({
el:"#app",
data:{
message:"lxc"
}
})
上边代码,把字符串 "lxc" 逆反,在模板中写看似很酷,实则给维护增加困难。我们可以用计算属性:
<div id="app">{{newMessage}}</div>
<script>
var vm = new Vue({
el:"#app",
data:{
message:"lxc"
},
computed:{
newMessage(){
return this.message.split("").reverse().join("")
}
}
})
上边代码,模板是不是很清爽,而且逻辑很清晰,newMessage是计算出来的,它依赖data中的message,newMessage始终依赖于message,只要message改变,计算属性会检测到,newMessage也会发生改变。
大家可能会想我们把上边的例子写在方法中不是一样吗?
然而不同的是计算属性computed是基于他们的响应式依赖进行缓存的,只有依赖的值发生改变才会重新计算执行,也就是说,只有当message发生改变,newMessage才会重新计算,如果message没发生改变,多次访问newMessage,计算属性会返回之前的计算结果,而不必执行newMessage函数。
computed里边不是方法,而是属性,通过计算算出的一个属性,计算属性computed有几个特点:
1、缓存数据;
2、computed里边的属性不能再data中定义;
3、不支持异步;
4、默认get方法必须要有返回值。
5、默认会调用getter方法,此方法的意思是:我们以上边为例,newMessage里边我们需要取到data中的message数据,对message进行一系列的操作,返回的是一个全新的值,这就是getter方法。其实上边也可以这样写:
computed:{
newMessage:{
get(){
return this.message.split("").reverse().join("")
},
set(){
}
}
}
6、computed里边还有一个setter方法,当自身改变影响其他依赖,都会调用setter方法,意思是:监听当前属性的变化,当属性发生改变时执行;此方法接收一个唯一的参数,参数意思是:更新的属性。。
举个例子:
<div id="app">
<input type="checkbox" v-model="isAllSelect">全选<br>
<div v-for="item in arr" :key="item.id">
<input type="checkbox" v-model="item.state">{{item.text}}<br>
</div>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
arr:[
{text:"财富",state:false,id:"1"},
{text:"青春",state:false,id:"2"}
]
},
computed:{
isAllSelect:{
get(){
return this.arr.every(ele=>ele.state)
},
set(newValue){
this.arr.forEach(ele=>ele.state = newValue)
}
}
}
})
</script>
上边例子是一个全选功能,大家可以复制代码运行;先下详细分析下:全选功能可以细分为2个小功能
一是:全选框依赖复选框,当复选框全部勾选时,全选框会自动勾选,计算属性会调用computed中的getter方法,isAllSelect 依赖数组中的state状态,every方法会判断每一个state状态,条件都符合才返回true,有一个不符合则返回false;
二是:勾选全选框,复选框会跟随全选框变化而变化。也就是说,全选框自身变化,会影响复选框的状态,在计算属性会调 用setter方法,参数newValue就是全选框的状态(true或false),把全选框状态赋值给下边的每一个复选框的状态,功 能即可实现。。。
watch:侦听器
官方文档解释:当数据变化时执行异步时,用watch;无缓存功能。(主要应用场景:函数节流、防抖、ajax异步操作)
eg:(下边应用场景非常常用吗,输入框输入信息,watch监听到输入框值发生变化,从而向后台发球ajax请求!!!)
<div id="app">
<input type="number" v-model.number="num" placeholder="请输入大于1的数字">
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
num:"",
timer:null
},
// watch里边的num函数,是查看num是否符合要求
watch:{
num(){
if( this.num >= 2){
this.debounce(this.success,2000);
}else{
this.debounce(this.error,2000)
}
}
},
methods:{
// 防抖函数
debounce(fn,delay){
clearTimeout(this.timer);
this.timer = setTimeout(fn,delay)
},
// 成功的回调
success(){
console.log(this)//这里的this指向vue实例,但是如果把回调函数写在setTimeout里边,this指向window,再次说明了:this的指向看函数在哪调用的,而不是看在哪执行的
alert("成功")
},
// 失败的回调
error(){
alert("您输入的数字小于2了")
}
}
})
</script>
实际开发中,实时校验输入框的内容,一般都是把内容发给后台,后台判断是否符合要求,返回给前台结果,这个过程是异步的了。上边代码,我们使用的定时器模拟了异步的过程;用watch监控num的数值,(computed计算属性无法执行异步操作)2秒钟后返回输入的数值是否符合要求;这里说下,里边用到了防抖函数,如果实时的去触发watch里边的监听函数,实际开发中对增加了服务器的负担,而且用户体验不好。。。
(补充)深度监听:
watch对于监测对象属性或方法的变化时,vue给到了一个深度监听的方法(此属性对性能开销比较大!!!因为每次修改对象的属性值watch都会监听到!!!):
watch:{
object:{//对象
handler(){//必写
··· ···
},
deep:true//必写
}
}
watch监听一个对象object,对象里边是vue提供的一个必须写的方法handler方法,在此方法里边写逻辑;下边还需价一个属性deep:true。。。
eg:(监测对象属性,如果修改对象,把修改的对象存到内存中)
<div id="app">
<p>{{ text }}</p>
<button @click="click">click</button>//改变对象化中的属性按钮
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
text: [{ name: "lxc" }]
},
methods: {
click() {
this.text[0].name = "hehe";
}
},
watch: {
text: {
handler() {
alert("1");
localStorage.setItem("content", JSON.stringify(this.text));//如果对象属性改变,会存到内存中去
},
deep: true
}
}
});
</script>
点击修改结果:
computed和watch的区别
1、computed不支持异步,watch支持异步;
2、computed对于监控多个数据的变化比watch写法上更优;(下边我们看下官网文档里的例子)
3、总之,计算属性computed能做的watch都能做,反之不行。
先用wtach来写下:用watch分别监听firstName和lastName的变化,发生改变会影响fullName
<div id="app">
<h2>{{fullName}}</h2>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
firstName:"吕",
lastName:"星辰",
fullName:"吕星辰"
},
watch:{
firstName(val){
this.fullName = val + this.lastName;
},
lastName(val){
this.fullName = this.firstName + val;
}
}
})
</script>
在看下用computed写法:
<div id="app">
<h2>{{fullName}}</h2>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
firstName:"吕",
lastName:"星辰"
},
computed:{
fullName(){
return this.firstName + this.lastName;
}
}
})
</script>
对比下是不是computed写法更好一些。。。
补充:
1、关于watch监听对象的问题,之前在群里看到有朋友在问题这样一个问题:为什么watch监听的对象中的handler函数2个参数输出结果都一样,当时也没在意,后来想了想决定自己打印下看看结果,结果还真是,都是输出的是改变之后的结果;
原因:出现以下问题的原因在于——只是修改了对象中的属性,对象的地址没改变,所以输出的值都是改变之后的;如果对象引用地址发生改变,两个参数就不一样了,一个是新值,一个是旧值。。。
<template>
<div>
<button @click="click">click</button>
</div>
</template>
<script>
export default {
name: "first",
data(){
return{
ability:{
name:"lxc",
height:170
}
}
},
methods:{
click(){
this.ability.name = '鸡小西'
}
},
watch:{
ability:{
handler(newValue,oldValue){
console.log(newValue.name)
console.log(oldValue.name)
},
deep:true
}
}
};
我们修改对象的引用地址,来看下:
methods:{
click(){
this.ability = {name:'鸡小西'}
}
},
watch:{
ability:{
handler(newValue,oldValue){
console.log(newValue.name)
console.log(oldValue.name)
},
deep:true
}
}
这回结果正常了: