1.概念
computed(计算属性)和watch(监听器)都是以vue的依赖追踪机制为基础的,当依赖数据发生变化时,依赖此数据的相关数据会自动变化
2.应用场景
computed处理场景:一个数据受多个数据的影响;watch处理场景:一个数据影响多个数据
-
当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的值有缓存的特性,避免每次获取值时,都要重新计算;所以如果v-if涉及较多的数值计算,用computed性能会比v-if更好 不需要每次重新计算
-
当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
3.原理
watch实现过程:
- watch的初始化在data初始化之后(此时的data已经通过
Object.defineProperty
的设置成响应式) - watch的key会在Watcher里进行值的读取,也就是立马执行get获取value(从而实现data对应的key执行getter实现对于watch的依赖收集),此时如果有
immediate
属性那么立马执行watch对应的回调函数 - 当data对应的key发生变化时,触发user watch实现watch回调函数的执行
computed实现过程:
- 当组件初始化的时候,
computed
和data
会分别建立各自的响应系统,Observer
遍历data
中每个属性设置get/set
数据拦截 -
初始化
computed
会调用initComputed
函数- 注册一个
watcher
实例,并在内实例化一个Dep
消息订阅器用作后续收集依赖(比如渲染函数的watcher
或者其他观察该计算属性变化的watcher
) - 调用计算属性时会触发其
Object.defineProperty
的get
访问器函数 - 调用
watcher.depend()
方法向自身的消息订阅器dep
的subs
中添加其他属性的watcher
- 调用
watcher
的evaluate
方法(进而调用watcher
的get
方法)让自身成为其他watcher
的消息订阅器的订阅者,首先将watcher
赋给Dep.target
,然后执行getter
求值函数,当访问求值函数里面的属性(比如来自data
、props
或其他computed
)时,会同样触发它们的get
访问器函数从而将该计算属性的watcher
添加到求值函数中属性的watcher
的消息订阅器dep
中,当这些操作完成,最后关闭Dep.target
赋为null
并返回求值函数结果。
- 注册一个
- 当某个属性发生变化,触发
set
拦截函数,然后调用自身消息订阅器dep
的notify
方法,遍历当前dep
中保存着所有订阅者wathcer
的subs
数组,并逐个调用watcher
的update
方法,完成响应更新。
4.demo
computed:根据不同状态展示不同文案
<div>{{getAlertText}}</div>
data() {
return {
alertText: 0,
};
},
computed: {
getAlertText() {
let text = [
'为了您的账户安全,请先完成实名认证,并设置资金密码',
'为了您的账户安全,请先设置资金密码',
'为了您的账户安全,请先完成实名认证',
];
return text[this.alertText];
}
},
methods: {
preCheckAccount() {
this.$_http.preCheckAccount().then(res => {
this.alertText = res.alertText
});
},
}
watch:监听输入值的变化
<input type="text" v-model="childrens.name" />
data() {
return {
childrens: {
name: '小强',
age: 20
},
lastName:"张三"
}
},
watch:{
childrens:{
handler (val,oldval){
console.log(`对象属性name由${oldval.name}变为${val.name}`)
},
deep:true//对象内部的属性监听,也叫深度监听,当使用deep watch的时候监听器会给对象的所有属性加上监听器,增大性能开销。如果这里不加,是监听不到childrens的变化的
},
//键路径必须加上引号
'childrens.name' (val,oldval){
console.log(`name由${oldval.name}变为${val.name}`)
}
},