计算属性
模板内表达式非常方便,但它们用于简单操作。在模板中加入太多逻辑可能会使它们变得臃肿且难以维护。例如:
<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('')
}
}
})
结果:
这里我们声明了一个计算属性reversedMessage。我们提供的函数将用作属性的getter函数vm.reversedMessage:
console.log(vm.reversedMessage) // => 'olleH'
vm.message = 'Goodbye'
console.log(vm.reversedMessage) // => 'eybdooG'
您可以打开控制台并自己玩示例vm。值vm.reversedMessage始终取决于值vm.message。
您可以像普通属性一样将数据绑定到模板中的计算属性。Vue知道vm.reversedMessage依赖于vm.message,因此它将更新依赖于vm.reversedMessage何时vm.message更改的任何绑定。最好的部分是我们以声明方式创建了这种依赖关系:计算出的getter函数没有副作用,这使得它更容易测试和理解。
Computed Caching vs Methods
您可能已经注意到我们可以通过调用表达式中的方法来获得相同的结果:
<p>Reversed message: "{{ reverseMessage() }}"</p>
// in component
methods: {
reverseMessage: function () {
return this.message.split('').reverse().join('')
}
}
我们可以定义与方法相同的函数,而不是计算属性。对于最终结果,这两种方法确实完全相同。但是,不同之处在于计算属性基于其反应依赖性进行缓存。计算属性仅在其某些反应依赖项发生更改时才会重新计算。这意味着只要message没有改变,对reversedMessage计算属性的多次访问将立即返回先前计算的结果,而不必再次运行该函数。
这也意味着以下计算属性永远不会更新,因为Date.now()它不是一个被动依赖:
computed: {
now: function () {
return Date.now()
}
}
相比之下,每当重新渲染发生时,方法调用将始终运行该函数。
为什么我们需要缓存?想象一下,我们有一个昂贵的计算属性A,它需要循环一个巨大的数组并进行大量的计算。然后,我们可以有其他的计算性能,在又取决于一个。没有缓存,我们将执行A的吸气次数超过必要的次数!如果您不想缓存,请改用方法。
Computed vs Watched Property
Vue确实提供了一种更通用的方法来观察和响应Vue实例上的数据更改:监视属性。当你有一些数据需要根据其他一些数据进行更改时,过度使用是很诱人的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: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
好多了,不是吗?
Computed Setter
计算属性默认为getter-only,但您也可以在需要时提供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地更新。
Watchers
虽然计算属性在大多数情况下更合适,但有时需要自定义观察器(Watchers)。这就是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 libraries -->
<!-- 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/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/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 function 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>
结果:
在这种情况下,使用该watch选项允许我们执行异步操作(访问API),限制我们执行该操作的频率,并设置中间状态,直到我们得到最终答案。对于计算属性,这些都不可能。
除了该watch选项,您还可以使用命令式vm。$ watch API。