一般情况下属性都是放到data
中的,但是有些属性可能是需要经过一些逻辑计算后才能得出来,那么我们可以把这类属性变成计算属性。比如以下:
<div id="example"> {{ message.split('').reverse().join('') }} </div>
在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message
的翻转字符串。当你想要在模板中的多处包含此翻转字符串时,就会更加难以处理。
所以,对于任何复杂逻辑,你都应当使用计算属性。
<div id="app"> <h2>总价格:{{totalPrice}}</h2> </div> <script> const vm = new Vue({ el: "#app", data: { message: "hello", books: [ {name: '三国演义', price: 30}, {name: '红楼梦', price: 40}, {name: '西游记', price: 50}, {name: '水浒传', price: 60}, ], }, computed: { // 计算属性的 getter totalPrice: function (){ let result = 0; // `this` 指向 vm 实例 for (let book of this.books){ result += book.price; } return result } } }) </script>
结果:总价格:180
这里我们声明了一个计算属性 totalPrice
。然后通过for循环计算出书的总价,像这种需要计算的属性,就写在computed
中。
属性一般都有get
和set
两个方法,get
获取属性值,set
设置属性值,computed
中默认就是get
属性,我们的vm.totalPrice
是依赖于books.price
,如果书本的价格发生变化,那么计算属性totalPrice
也随之动态变化
你可能已经注意到我们可以通过在表达式中调用方法来达到同样的效果:
<div id="app"> <h2>总价格:{{getAllPrice()}}</h2> </div> <script> const vm = new Vue({ el: "#app", data: { message: "hello", books: [ {name: '三国演义', price: 30}, {name: '红楼梦', price: 40}, {name: '西游记', price: 50}, {name: '水浒传', price: 60}, ], }, methods: { getAllPrice: function () { let result = 0; // `this` 指向 vm 实例 for (let book of this.books){ result += book.price; } return result } }, }) </script>
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同
的。然而,不同的是计算属性
是基于它们的响应式依赖进行缓存
的。只在相关响应式依赖发生改变时它们才会重新求值
。这就意味着只要 books
还没有发生改变,多次访问 totalPrice
计算属性会立即返回之前的计算结果,而不必再次执行函数。
所以说计算属性是有缓存的
我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次
执行 A 的 getter
!如果你不希望有缓存,请用方法来替代。
计算属性默认只有 getter
,不过在需要时你也可以提供一个 setter
:
computed: { totalPrice: { get: function () { let result = 0; // `this` 指向 vm 实例 for (let book of this.books){ result += book.price; } return result }, set: function (newValue) { for (let book of this.books){ book.price += 10 } } } }
这里我们添加了set
方法,在运行vm.totalPrice=[...]
时,setter
会被调用,随后书本的总价格也会随之变化,但是一般情况下不会使用set