内容提要:
- 计算属性的使用场景及表示方法
- 计算缓存与方法的差异
- 计算属性与监听器属性的使用比较
- 计算属性的Setter表示方法
- 监听器的使用方式
计算属性
在模板内写表达式是非常方便的,但它们仅仅能做一些简单的操作,把太复杂的表达式放在模板中可能会导致臃肿和难以维护。例如:
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
如上,模板不再是简单的和说明式的。在你读懂它是
message
的字符串反转之前你不得不读几秒钟。当这样的语法不止一次的出现,情况就会变得很糟糕。
这就是为什么对于复杂一点的逻辑你需要使用计算属性的原因。
基础的例子
<div id="example">
<p>Original message:"{{ message }}"</p>
<p>Computed reversed message:"{{ reversedMessage }}</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// a computed getter
reversedMessage: function() {
// 'this'points to the vm instance
return this.message.split('').reverse().join('')
}
}
})
Result:
Original message: "Hello"<br>
Computed reversed message: "olleH"
这里声明了一个计算属性
reversedMessage
.我们为属性
vm.reversedMessage
提供了一个getter函数:
console.log(vm.reversedMessage) // => 'olleH'
vm.message = 'Goodbye'
console.log(vm.reversedMessage) // => 'eybdooG'
而
vm.reversedMessage
的值依赖于
vm.message
.
我们可以像绑定正常的函数一样绑定计算属性。Vue能够意识到
vm.reversedMessage
依赖
vm.message
,所以当
vm.message
更新的时候它能够更新所有
vm.reversedMessage
的绑定。因为我们已经创建了他们的依赖关系:计算getter函数没有副作用,它更容易测试和理解。
计算缓存 vs 方法
你可能意识到了可以通过一个方法表达式得到同样的结果。
// in component
methods: {
reverseMessage: function() {
return this.message.split('').reverse().join('')
}
}
我们定义了一个相同的函数作为方法来代替计算属性。作为结果,这两种实现是相同的。然而,他们的不同在于计算属性缓存了他们的依赖关系。当依赖关系改变的时候计算属性才会被重新计算。这意味着只要
message
没有改变,多次访问reversedMessage计算属性将立刻返回之前的结果而不再运行函数。
这也意味着以下属性从不会更新,因为
Date.now()
没有一个可响应的依赖。
computed:{
now: function () {
return Date.now()
}
}
相比之下,无论是否重新渲染一个方法调用总是会运行函数。
我们为什么需要缓存?想象一下我们有非常耗时的计算属性A,要求遍历一个很大的数组并且做很多的计算。而后我们有其他计算属性反过来依赖A。没有缓存,我们将比实际需要执行A的getter很多次!某些情况下,如果你不需要缓存,就用一个方法代替。
计算 vs 监听器属性
Vue提供了一个更为常用的方式去观察和响应数据的改变在Vue实例:watch properties。当一些数据需要基于一些其他数据做改变的时候,过度使用
watch
是很诱人的 - 尤其是如果你有一个AngularJS背景。然而,通常使用计算属性要比强制回调
watch
要好。考虑这个例子:
<div id="demo">
{{ fullName }}
</div>
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
},
})
上面的代码是强制的和重复的。比较一下计算属性版本:
var vm = new Vue({
el: '#demo',
data: {
firtName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
显然,第二个要好一些。
计算Setter
计算属性默认仅为getter,但是当你需要的时候你也能提供一个setter:
// ...
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
// ...
现在当你运行
vm.fullName = 'John Doe'
,setter将被调用,
vm.firstName
和
vm.lastName
将会被更新。
监听器
虽然计算属性在大多数情况下更合适,有时仍需要一个自定义的监听器。这就是为什么Vue提供了更通用的方式去响应数据的变化通过
watch
选项。当你执行异步或耗时操作去响应数据改变的时候会更有用。
例如:
<div id="watch-example">
<p>Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<!-- Since there is already a rich ecosystem of ajax libraris -->
<!-- and collections of general-purpose utility methods, Vue core -->
<!-- is able to remain small by not reinventing them. This also -->
<!-- gives you the freedom to use what you're familiar with.' -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
el: '#watch-example',
data: {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
// whenever question changes, this function will run
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},
created: function () {
// _.debounce is a funciton provided by lodash to limit how often a particularly expensive operation can be run.In this case, we want to limit how often we access yesno.wtf/api, waiting until the user has completely finished typing before making the ajax request. To learn more about the _.debounce function (and its cousin _.throttle), visit: https://lodash.com/docs#debounce
this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
},
methods: {
getAnswer: function () {
if (this.question.indexOf('?') === -1) {
this.answer = 'Questions usually contain a question mark. ;-)'
return
}
this.answer = 'Thinking...'
var vm = this
axios.get('https:// yesno.wtf/api')
.then(function (response)) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error)) {
vm.answer = 'Error! Could not reach the API. ' + error
})
}
}
})
</script>
Result:
问题输入改变时,文字提示会实时改为:Waiting for you to stop typing …
问题不规范:

请求中:
请求回来:
在这个例子里,使用
watch
操作允许我们执行一个异步的操作(访问一个API的时候),限制我们操作的频率,当得到最后的回答时立刻改变状态。用计算属性是做不到的。
除了
watch
选项,你也可以使用命令
vm.$watch API
.