计算属性和观察者

计算属性

​ 直接在模板内的嵌入表达式是非常便利的,但他们实际上是用于简单运算的,在实际开发中很少使用,因为在模板中嵌入太多逻辑会让模板过重且难以维护。例如:

1
2
3
<div id="example">
{{ message.split('').reverse().join('') }}
</div>

​ 在这里,模板不再简单和清晰,并且如果要在多处地方重复使用此处的翻转字符串时,会更加难以处理。 因此,对于复杂逻辑,vue引入了计算属性(computed)

方法

​ 我们可能将同计算属性相同的函数定义为一个方法,一般情况下,2种方法的作用效果是相同的。然而,不同的是计算属性是基于他们的依赖进行缓存的,计算属性只有在他的相关依赖发生改变时才会重新求值,而方法不同,每当触发重新渲染时,方法的调用方式总是再次执行函数。

  • 举个简单的例子:假设B依赖与A,要获取B的值,只要A的值不发生改变,每次访问B,计算属性都会立即返回之前的计算结果,而不会再次执行函数。如果是方法的话,只要页面一刷新,便会再次执行函数。
  • 为什么需要缓存? 假设我们有一个性能开销比较大的的计算属性 A,它需要遍历一个极大的数组和做大量的计算。然后我们可能有其他的计算属性依赖于 A 。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
1
2
3
4
5
6
7
<div id="example">
<p>Original message: "{{ message }}"</p>
<!--计算属性实现翻转字符串-->
<p>Computed reversed message:"{{ reversedMessage_1 }}"</p>
<!--方法实现翻转字符串-->
<p>Revered message: "{{ reversedMessage_2() }}"</p>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let vm = new Vue({
el:'#example',
data:{
message:'hello',
},
computed:{
//此函数相当于 vm.reversedMessage属性的getter函数
reversedMessage_1: function () {
return this.message.split('').reverse().join('');
},
},
methods:{
reversedMessage_2: function () {
return this.message.split('').reverse().join('');
},
}
});

因为 Date.now() 不是响应式依赖,下面再举个简单的栗子:

1
2
3
4
5
<div id="example">
<p>Time:"{{ now_1 }}"</p>
<p>Time:"{{ now_2() }}"</p>

</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let vm = new Vue({
el:'#example',
data:{
message:'hello',
},
computed:{
now_1: function () {
return new Date().toLocaleString();
}
},
methods:{
now_2: function () {
return new Date().toLocaleString();
},
}
});

打开控制台(console),分别调用方法vm.now_2()和计算属性vm.now_1,查看输出状态如下:

image.png

​ 可以看出,调用方法,获取到的是更新后的值,调用计算属性,获取到的是缓存的值。

观察者

  • 虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的 watcher。这是为什么 Vue 通过 watch 选项提供一个更通用的方法,来响应数据的变化。当你想要在数据变化响应时,执行异步操作或开销较大的操作,这是很有用的。demo如下:
1
2
3
4
5
6
7
8
9
 <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>
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
let watchVM = new Vue({
el:'#watch-example',
data:{
question:'',
answer:'I can not give you an answer untile you ask a question!'
},
watch:{
//如果question发生改变,这个函数就会执行
question: function () {
this.answer = 'waitting for you to stop typing...';
this.getAnswer();
}
},
methods:{
// _.debounce 是一个通过 lodash 限制操作频率的函数。
// 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
// ajax 请求直到用户输入完毕才会发出
// 学习更多关于 _.debounce function (and its cousin
// _.throttle),参考:https://lodash.com/docs#debounce
getAnswer: _.debounce(
function () {
if(this.question.indexOf('?') === -1){
this.answer = 'Questions usually contain a question mark .:-)';
return;
}
this.answer = 'Thinking...';
let vm = this;
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer);
})
.catch(function (err) {
vm.answer = 'Error!could not reach the API' + err;
})
},
//这是为用户停止输入等待的毫秒数
500
)}
});
  • ​ 在这个示例中,使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这是计算属性无法做到的。

番外:什么是 axios

​ axios 是一个基于 Promise 的,为浏览器和 Node.js 设计的 HTTP 客户端。它尽可能简化封装了 HTTP 相关的各种操作,在 Web App 中使用非常方便。Vue 2 官方建议在由 Vue 构建的 SPA 中使用 axios 进行 HTTP 操作。

​ 传送门:mzabriskie/axios