1、介绍
我们首先在官网看下对技术属性computed的介绍:
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。
<div id="example"> {{ message.split('').reverse().join('') }} </div>
在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量
message
的翻转字符串。当你想要在模板中的多处包含此翻转字符串时,就会更加难以处理。所以,对于任何复杂逻辑,你都应当使用计算属性。
你可以像绑定普通 property 一样在模板中绑定计算属性。Vue 知道
vm.reversedMessage
依赖于vm.message
,因此当vm.message
发生改变时,所有依赖vm.reversedMessage
的绑定也会更新。
computed用来监控自己定义的变量,该变量不在data里面声明,直接在computed里面定义,然后就可以在页面上进行双向数据绑定展示出结果或者用作其他处理;
计算属性由两部分组成:get和set,分别用来获取计算属性和设置计算属性。默认只有get,如果需要set,要自己添加。另外set设置属性,并不是直接修改计算属性,而是修改它的依赖。
一个例子
<template>
<div>
<ul>
<li>
firstName: {{ firstName }}
</li>
<li>
lastName: {{ lastName }}
</li>
<li>
fullName: {{ fullName }}
</li>
</ul>
<button @click="handle">操作</button>
</div>
</template>
<script>
export default {
data() {
return {
firstName: '',
lastName: ''
};
},
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName;
},
// setter
set: function (newValue) {
let names = newValue.split(' ');
this.firstName = names[0]; // 对它的依赖进行赋值
this.lastName = names[names.length - 1];
}
}
},
methods: {
handle() {
this.fullName = 'John Doe';
}
}
};
</script>
现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstName 和 vm.lastName 也会相应地被更新。
另一个例子
<template>
<div id="app">
<!-- 桌腿数量用type= range 选择器,绑定变量legsCount-->
<label>
桌腿数量: <input
v-model="legsCount"
type="text"
>
</label>
<!-- 桌子数量 绑定变量tableCount,并且输入数量时触发update -->
<label>
桌子数量: <input
v-model="tableCount"
type="text"
>
</label>
<!-- 统计结果 -->
<output>
我们将需要做 {{ legsCount }}桌腿,组成 {{ tableCount }} 桌子
</output>
</div>
</template>
<script>
export default {
data() {
return {
legsCount: 0
};
},
computed: {
tableCount: {
get() {
return this.legsCount / 4;
},
set(newValue) {
this.legsCount = newValue * 4;
}
}
}
};
</script>
<style>
label, output {
display: block;
margin: 20px;
text-align: center;
}
input {
vertical-align: middle;
margin-left: 10px;
}
</style>
2、计算属性vs普通属性
可以像绑定普通属性一样在模板中绑定计算属性,在定义上有区别:计算属性的属性值必须是一个函数。
data() { // 普通属性
return {
msg: ''
};
},
computed: { // 计算属性
reverseMsg: function () { // 该函数必须有返回值,用来获取属性,称为get函数
// 可以包含逻辑处理操作,同时reverseMsg依赖于msg,一旦msg发生变化,reverseMsg也会跟着变化
return this.msg.split(' ').reverse().join(' ');
}
},
3、计算属性vs方法
计算属性缓存是定义在计算属性 computed
里的,调用 methods
里的方法也可以与计算属性起到同样的作用。
<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在组件中
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
两者最主要的区别:computed 是可以缓存的,methods 不能缓存;
- 只要相关依赖没有改变,多次访问计算属性得到的值是之前缓存的计算结果,不会多次执行。
- 网上有种说法就是方法可以传参,而计算属性不能,其实并不准确,计算属性可以通过闭包来实现传参:
:data="closure(item, itemName, blablaParams)"
computed: {
closure() {
return function (a, b, c) {
/** do something */
return data;
};
}
},
4、计算属性vs侦听属性
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性watch。watch中可以执行任何逻辑,如函数节流,Ajax异步获取数据,甚至操作 DOM(不建议)。
常规用法
<template>
<div class="attr">
<h1>watch属性</h1>
<h2>{{ $data }}</h2>
<button @click="() => (a += 1)">修改a的值</button>
</div>
</template>
<script>
export default {
data() {
return {
a: 1,
b: {c: 2, d: 3},
e: {
f: {
g: 4
}
},
h: []
};
},
watch: {
a: function (val, oldVal) {
this.b.c += 1;
},
'b.c': function (val, oldVal) {
this.b.d += 1;
},
'b.d': function (val, oldVal) {
this.e.f.g += 1;
},
e: {
handler: function (val, oldVal) {
this.h.push('CSDN');
},
deep: true // 用于监听e对象内部值的变化
}
}
};
</script>
两者之间的对比
从上面流程图中,我们可以看出它们之间的区别:
- watch:监测的是属性值, 只要属性值发生变化,其都会触发执行回调函数来执行一系列操作。
- computed:监测的是依赖值,依赖值不变的情况下其会直接读取缓存进行复用,变化的情况下才会重新计算。
除此之外,有点很重要的区别是:计算属性不能执行异步任务,计算属性必须同步执行。也就是说计算属性不能向服务器请求或者执行异步任务。如果遇到异步任务,就交给侦听属性。watch也可以检测computed属性。
5、计算属性禁用箭头函数
注意,不应该使用箭头函数来定义计算属性函数 (例如 aDouble: () => this.a * 2
)。理由是箭头函数绑定了父级作用域的上下文,所以 this
将不会按照期望指向 Vue
实例,this.a
将是 undefined
。当然有很多this需要指向vue实例的时候,都需要慎用箭头函数。
6、总结
计算属性适合用在模板渲染中,某个值是依赖了其它的响应式对象甚至是计算属性计算而来;而侦听属性适用于观测某个值的变化去完成一段复杂的业务逻辑。
- computed能做的,watch都能做,反之则不行
- 能用computed的尽量用computed