🌓 Vue 计算属性与侦听器 watch
# 计算属性 computed
Vue 中不推荐在模版中使用复杂的表达式,应该重构为「计算属性」或方法;(官网风格指南)
要显示的「属性」不存在时,要通过「已有属性(在 Vue 下管理)」计算得到时可以使用计算属性 computed
:
原理:底层借助了
Objcet.defineProperty()
方法提供的getter
和setter
。get
函数什么时候执行?- 初次读取时会执行一次(将保存在缓存提供重复使用);
- 依赖的数据发生改变时会被再次调用;
与
methods
实现相比,内部有缓存机制(复用),效率更高,调试方便。
⚠️注意:
- 最终的计算属性会出现在
vm
,直接读取使用(会自动调用get
)即可; - 如果计算属性要被修改,那必须写
set
函数去响应修改,且set
中要引起计算时依赖的数据发生改变。
- 最终的计算属性会出现在
使用方法:
- 在
computed
对象中定义计算属性(get()
和set()
); - 在页面中使用
来显示计算的结果;
- 只读不改的简写,省略
set()
只有get()
时,直接当计算属性的get()
;
- 在
🌰 例子:
<div id="#app">
{{ msg }}
{{ reversedMsg}}
</div>
2
3
4
const vm = new Vue({
el: "#app",
data: {
msg: "Hello"
},
computed: {
reversedMessage: function() {
return this.message.split('').reverse().join('')
}
}
})
2
3
4
5
6
7
8
9
10
11
🌰 例子 / setter
:
(计算属性的完整写法):
computed: {
fullName: {
// getter
get: function() {
return this.firstName + ' ' + this.lastName
},
// setter
set: function() {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
现在设置
fullName
的值时,例如将vm.fullName = 'John Doe'
,setter
会被调用,此时firstName
和lastName
也会相应发生改变。
# 侦听器 watch
(监听属性)提供了一个更加通用、能够自定义的方法,来相应数据的变化。当需要数据变化时执行异步或者开销较大的操作时,使用 watch
最合适。
当被监视的属性发生变化时,回调函数自动调用进行相关的操作:
⚠️:监视的「属性」必须存在才能进行监视;
监视的两种写法:通过在
new Vue
时传入watch
配置来监视指定的属性或者通过vm
对象的$watch()
监视 ;监视的
watch(oldValue, newValue)
可以通过参数获取变化的值(旧值和新值);监视的简写(适用于不需要配置
immediate
等参数);
深度监视:
- Vue 中的
watch
默认不监测对象内部值的改变(一层); - 配置
deep: true
可以监测对象内部值的改变; - ⚠️注意:
- Vue 自身可以检测对象的内部值改变,但是 Vue 提供的监视属性
watch
默认不可以; - 使用
watch
可以根据数据的具体结构,决定是否采用深度监视;
- Vue 自身可以检测对象的内部值改变,但是 Vue 提供的监视属性
🌰 例子:
<div id="demo">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>
{{ answer }}
</p>
</div>
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>
<script>
var watchExampleVM = new Vue({
el: '#watch-example',
data: {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
// 如果 `question` 发生改变,这个函数就会运行
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},
created: function () {
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>
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
参考自官网的例子。使用
watch
允许执行一个异步操作,限制执行该操作的频率,并且在得到最终结果之前,设置answer
的中间状态,这些都是computed
无法做到的。
# 使用监视与计算属性对比
computed
能完成时,watch
都能完成:当watch
和computed
都能实现时,首选用computed
提高性能;watch
能完成的功能,computed
不一定完成:当时需要实现一些异步功能(例如延时显示)时,就使用watch
(computed
依靠return
获取值)
🌰 例子:
<div id="app">
{{ fullName }}
</div>
2
3
const vm = new Vue({
el: "#app",
data: {
firstName: 'foo',
lastName: 'bar',
fullName: 'foo bar'
},
watch: {
firstName: function(value) {
this.fullName = val + ' ' + this.lastName
},
lastName: function(value) {
this.fullName = this.firstName + ' ' + val
}
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
通过监视属性
watch
侦听firstName
和lastName
的变化,修改fullName
的值。但是两个代码逻辑发生了重复。明显使用computed
计算属性的效率更高:computed: { fullName: function() { return this.firstName + ' ' + this.lastName } }
1
2
3
4
5