计算属性
姓名案例
我们想要让我们的姓名是动态变化的,就要使用v-model双向绑定指令。
姓名的全名是由姓和名共同组成。
普通的实现方式
<div id="app">
姓:<input type="text" v-model="firstName"><br/>
名:<input type="text" v-model="lastName"> <br/>
姓名:<span>{{firstName}}-{{lastName}}</span>
</div>
new Vue({
el:'#app',
data:{
firstName:'张',
lastName:'三'
}
}
针对全名进行一些有条件的输出,如以下:
姓名:<span>{{firstName.slice(0,3)}}-{{lastName}}</span>
如果姓名还要做一些其他的操作,就会显得代码十分臃肿,因此我们使用methods:
全名:<span>{{this.fullName()}}</span>
new Vue({
el:'#app',
data:{
firstName:'张',
lastName:'三'
},
methods:{
fullName(){
return this.firstName+'-'+this.lastName
}
}
})
通过方法调用可以解决我们模板看起来臃肿的问题,把数据的操作交给函数去处理。
计算属性实现
computed的实现原理是Object_defineProperty。使用get和set方法。
计算属性computed实现了缓存,值未改变时会使用缓存,否则得到set修改的值。
全名:<span>{{fullName}}</span>
computed:{
fullName: {
//当有人读取fullName时,就会调用get方法,返回值作为fullName的值
get(){
console.log('fullName被读取了')
return this.firstName + '-'+this.lastName
},
}
}
计算属性get什么时候被调用?
- 初始读取计算属性的时候被调用
- 计算属性所依赖的数据发生变化。
set的调用
使用value调用,对值进行分割,获取名和姓然后再赋值
set(value){
console.log('fullName被修改了')
//转化为数组形式,以-为分割符
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
总结
计算属性:
- 定义:要用的属性不存在,通过已有的属性计算而来。
- 原理: 底层使用Object_defineProperty方法来提供setter和getter方法。
- get方法什么时候被调用:
(1) 初始读取时会执行一次。
(2)所依赖的数据发生改变的时候调用。 - 同methods方法相比,内部有缓存机制,效率更高,调用方便。
- 计算属性最终会出现在vm上,直接读取即可。
- 如果计算属性被修改,那必须写set函数响应修改,且set方法要引起所依赖的数据发生变化。
计算属性简写
确定只有getter,而没有setter,可以实现简写,相当于getter使用。
<div id="app">
姓:<input type="text" v-model="firstName">
名:<input type="text" v-model="lastName">
全名:<span>{{fullName}}</span>
</div>
<script type="text/javascript">
Vue.config.productiontip=false
const vm = new Vue({
el: '#app',
data:{
firstName: '张',
lastName: '三'
},
computed:{
//只读不改才能简写
fullName(){
//当有人读取fullName时,就会调用get方法,返回值作为fullName的值
console.log('fullName被读取了')
return this.firstName + '-'+this.lastName
}
}
})
</script>
实现了计算属性的只读不写。
计算属性
天气案例
在Visual Studio Code安装一个叫Vue 3 Snippets的插件。
天气凉爽和炎热状态切换。
基本实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>天气案例</title>
<script src="/front-study/vue-study/vue-Devtools/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 模板里面可以直接读取vm身上的数据 -->
<h1>今天天气{{isHot ? '炎热' : '凉爽'}}</h1>
</div>
<!-- 开发者工具发现数据没有用到,则开发工具不更新数据 -->
<script type="text/javascript">
Vue.config.productiontip = false //阻止Vue生成提示
const vm = new Vue({
el: '#app',
data:{
isHot: true //true表示天气热,否则天气凉爽
},
})
</script>
</body>
</html>
通过计算属性实现
<h1>今天天气{{info}}</h1>
computed: {
info(){
return this.isHot ? '炎热' : '凉爽'
}
},
点击按钮实现天气转化:
<button @click="changeWeather()">切换天气</button>
methods: {
changeWeather(){
this.isHot = !this.isHot
}
},
简单的实现按钮状态切换:
<button @click="isHot = ! isHot">切换天气</button>
监视属性(天气案例)
监视属性通过watch来实现,watch实现方式有两种:
- 在new Vue()内部去实现Watch的配置
- 使用vm.$watch去配置
监视属性的原理,当监视的数据发生变化时,就会触发handler回调函数,执行相应的回调处理。
具体实现如下:
//实现1:在vm内部实现监视属性:已知监视谁
watch: {
//key: 监视属性
isHot: {
//配置项
immediate: true, //初始化时让handler调用一下
//内部实现handler,isHot被修改了handler方法触发
handler(newValue,oldValue){ //有俩参数
console.log('isHot被修改了',newValue,oldValue)
}
}
},
vm.$watch('isHot',{
immediate:true,
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
})
总结
1.当监视的属性发生变化时,会触发回调函数handler去处理相应的逻辑,其中handler(newValue,oldValue)有两个参数。
2.监视的属性一定要存在的,这样才会被监视到。
3.监视分为两种:
- 通过在new Vue时传入watch配置
- 通过vm.$watch监视
深度监视
data中有一个numbers对象,其中numbers对象包含a,b两个属性。
要求只监视a属性,而不监视b属性?
<div id="app">
<h3>a的值是{{numbers.a}}</h3>
<button @click="numbers.a++">点击我a+1</button>
<h3>b的值是{{numbers.b}}</h3>
<button @click="numbers.b++">点击我b+1</button>
</div>
<script type="text/javascript">
Vue.config.productionTip= false
const vm = new Vue({
el:'#app',
data:{
numbers:{
a: 1,
b: 2
}
},
watch:{
'numbers.a':{
//内部实现handler方法进行回调
handler(newValue,oldValue){
console.log('numbers.a被修改了')
}
}
}
})
</script>
运行结果:
深度监视是针对于对象的多层属性。让其能监测到对象内部值的变化。
通过deep:true实现,如下所示:
//监视整个对象的任何属性的变化
watch:{
numbers:{
deep:true, //配置深度监视
//Vue提供的Watch不会帮我们监视对象内布值的变化,需要监视的话需要配置deep:true
handler(){
console.log('numbers被改变了')
}
}
}
总结:
1.深度监视:
(1) Vue提供的watch默认不监视对象内部值的改变(一层)
(2) 如果要监视对象内部值的改变需要配置deep:true。
注意:
(1) Vue自身可以监测对象内部值的数据,但Vue提供的Watch不能够监测到。
(2) 使用深度监视还是普通监视,需要根据数据的具体结构。
监视简写
监视属性的简写就是不写其他的配置项,默认是使用自定义函数名实现了handler方法。
1.new Vue内部实现
//实现1 简写形式
watch:{
isHot(newValue,oldValue){
console.log('isHot被修改了')
}
}
2.vm.$watch实现
vm.$watch('isHot',function(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
})
简写Vue不能够实现深度监视以及初始化的时候调用handler方法。
计算属性和监视属性的对比
通过计算属性实现姓名案例:
计算属性本身就是一个属性,不需要在data中声明,计算属性结果依赖于return的返回值。
<div id="app">
姓:<input type="text" v-model="firstName">
名:<input type="text" v-model="lastName">
全名: <span>{{fullName}}</span>
</div>
<script type="text/javascript">
Vue.config.productiontip=false
const vm = new Vue({
el:'#app',
data:{
firstName: '张',
lastName: '三',
fullName: '张-三' //监视属性需要依赖fullName
},
computed: {
fullName(){
return this.firstName+'-'+this.lastName
}
},
通过监视属性实现姓名案例
watch: {
//姓或名一改,全名得改
firstName(value){
this.fullName = value+'-'+this.lastName
},
lastName(value){
this.fullName = this.firstName+'-'+value
}
},
data:{
firstName: '张',
lastName: '三',
fullName: '张-三' //监视属性需要依赖fullName
},
对比发现,同样的功能,计算属性实现起来比较容易,但计算属性并不都会满足要求,例如进行异步操作时。
要求改变data数据后 , 全名就会延迟一秒发送。
通过监视属性实现
firstName(value){
setTimeout(()=>{
this.fullName = value+'-'+this.lastName
},1000);
},
lastName(value){
this.fullName = this.firstName+'-'+value
}
能够实现要求。
但通过computed实现,无法实现要求,主要跟return有关。
computed: {
fullName(){
setTimeout(()=>{
//计算属性是依赖于return的返回值,计算属性不能实现异步任务。
return this.firstName+'-'+this.lastName
},1000);
}
故计算属性是依赖于return的返回值,计算属性不能实现异步任务。
总结:
1.computed能完成的功能,watch也能够完成
2.watch能完成的功能,computed不一顶能完成,例如watch能实现异步操作。
注意:
1.所有被Vue管理的函数,最好写成普通函数,这样this指向的是vm
2.所有不被vue管理的函数(定时器的回调函数,ajax回调函数,promise函数,),最好写成箭头函数,这样this指向为vm或组件实例对象.