目录
1.姓名案例插值语法实现
2.姓名案例methods实现
3.姓名案例计算属性实现
4.get什么时候会被调用?
5.set什么时候会被调用?
6.计算属性简写
7.总结
我们这一节将通过一个小案例来讲清楚Vue的计算属性。
案例的实现效果是,有两个输入框,一个输入姓,一个输入名,最后呈现姓-名
当姓或者名改变的时候,最后呈现的姓-名也同步发生改变
但是我们接下来并不会直接用计算属性去实现这个动态效果,而是先用我们之前学过的插值语法和methods的方式分别实现一下,最后再用计算属性去实现,通过这种对比让大家能够明白为什么Vue要设计计算属性的功能以及它的优势在哪里。
1.姓名案例插值语法实现
注意:要实现页面改变数据的这种效果v-bind就已经满足不了需求了,这里就需要使用
v-model:value ,简写形式为v-model
<!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>
<!--引入Vue-->
<script type="text/javascript" src="../js/vue.js"></script>
<style>
</style>
</head>
<body>
<!--准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br><br>
名:<input type="text" v-model="lastName"> <br><br>
全名:<span>{{firstName + '-' + lastName}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止Vue在启动时生成生产提示
new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
}
})
</script>
</html>
实现效果:
上面的全名是使用拼接字符串的方式 {{firstName + '-' + lastName}} 写的,其实我们还可以换一种方式写 {{firstName}}-{{lastName}}
那么上面我们就使用插值语法实现了这个需求,这样看都挺好。所以接下来我们再丰富一下这个需求。我们让全名在展示姓的时候只展示前三位。
我们可以像下面这样实现:
<!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>
<!--引入Vue-->
<script type="text/javascript" src="../js/vue.js"></script>
<style>
</style>
</head>
<body>
<!--准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br><br>
名:<input type="text" v-model="lastName"> <br><br>
全名:<span>{{firstName.slice(0,3)}}-{{lastName}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止Vue在启动时生成生产提示
new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
}
})
</script>
</html>
实现效果:
这样虽然实现了效果,但是代码有点小问题,像 {{firstName.slice(0,3)}} 这样的代码是非常不便于观察的,如果我们不仅需要截取3位,还需要反转加全部变为大写,那么这个代码就变得非常长了。这样得话就违背了Vue的简单表达式的原则,我们可以去Vue官网看一下风格指南。
这样的写法并不是错误的写法,只是说Vue并不推荐这样去做,而Vue推荐的方法就是下面这样的计算属性
我们接下来,并不直接使用计算属性。而是通过之前学过的methods的方式去实现这种复杂的业务逻辑。
2.姓名案例methods实现
<!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>
<!--引入Vue-->
<script type="text/javascript" src="../js/vue.js"></script>
<style>
</style>
</head>
<body>
<!--准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br><br>
名:<input type="text" v-model="lastName"> <br><br>
全名:<span>{{fullName()}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止Vue在启动时生成生产提示
new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
methods:{
fullName(){
return this.firstName + '-' + this.lastName
}
}
})
</script>
</html>
注意:插值语法中如果写的是函数,则会将函数的返回值插入,插值语法中的函数括号不能省略
实现效果:
实现原理注意点:只要data中的数据发生改变,Vue就会重新解析模板,而在重新解析模板的时候,只要遇到插值语法里面写方法的,这个方法就一定会被重新调用
使用methods去实现这个案例,也可以,但是效率不高。
3.姓名案例计算属性实现
上面我们使用插值语法和methods实现了姓名案例,下面我们就用计算属性来实现一下。
如果我们想理解好计算属性,我们就得先明白什么是属性?对于Vue来说,它认为在data配置项中所写的东西就是属性
红色部分的是属性名,蓝色部分的是属性值
那什么是计算属性呢?计算属性就是,我们拿着我们已有的属性,去加工,计算,然后生成一个全新的属性
在Vue中,属性和计算属性是分开放的,data中放的是属性,而计算属性则放在一个全新的配置项中computed
<!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>
<!--引入Vue-->
<script type="text/javascript" src="../js/vue.js"></script>
<style>
</style>
</head>
<body>
<!--准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br><br>
名:<input type="text" v-model="lastName"> <br><br>
全名:<span>???</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止Vue在启动时生成生产提示
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
computed:{
fullName:{
/*
get有什么作用?当有人读取fullName时,get就会被调用
且返回值就作为fullName的值
这里的get底层调用的其实就是Object.defineProperty中的get方法
*/
get(){
console.log('get被调用')
return '123'
}
}
},
methods:{
}
})
</script>
</html>
我们可以看到 当我们访问计算属性的时候,get方法就会被调用。
这里还有一个细节问题需要注意:我们之前说过vm身上有一个_data,而_data中存放的是data中的东西,就目前来说_data中有firstName,lastName,但是绝对没有fullName,因为fullName是属于以后计算出来的东西。而不是当时就写在data中的东西。我们可以去验证一下:
那这个时候就会有人发出疑问,如果_data中没有计算属性,那我们要怎么使用它呢?
其实不要紧,因为fullName是由firstName,lastName经过计算之后得到的,在计算之后会被直接丢到vm身上的。所以我们在使用的时候,就直接使用插值语法读取fullName就行了。
<!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>
<!--引入Vue-->
<script type="text/javascript" src="../js/vue.js"></script>
<style>
</style>
</head>
<body>
<!--准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br><br>
名:<input type="text" v-model="lastName"> <br><br>
全名:<span>{{fullName}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止Vue在启动时生成生产提示
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
computed:{
fullName:{
/*
get有什么作用?当有人读取fullName时,get就会被调用
且返回值就作为fullName的值
这里的get底层调用的其实就是Object.defineProperty中的get方法
*/
get(){
console.log('get被调用')
return '123'
}
}
},
methods:{
}
})
</script>
</html>
实现效果:
下面我们来真正的实现这个需求。这里就需要注意,在computed配置中,Vue已经帮我们把get方法中的this指向调成了vm。所以我们可以直接使用this的属性值去计算。
<!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>
<!--引入Vue-->
<script type="text/javascript" src="../js/vue.js"></script>
<style>
</style>
</head>
<body>
<!--准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br><br>
名:<input type="text" v-model="lastName"> <br><br>
全名:<span>{{fullName}}</span> <br><br>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止Vue在启动时生成生产提示
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
computed:{
fullName:{
/*
get有什么作用?当有人读取fullName时,get就会被调用
且返回值就作为fullName的值
这里的get底层调用的其实就是Object.defineProperty中的get方法
*/
get(){
console.log('get被调用')
return this.firstName + '-' + this.lastName
}
}
},
methods:{
}
})
</script>
</html>
4.get什么时候会被调用?
到这里我们就把计算属性写完了,但是我们还得去研究一个问题,就是get什么时候会被调用?
这时候就有人发出疑问,你上面的代码不是已经写了当有人访问fullName的时候,get方法就会被调用吗?为什么还要再问一遍呢?那如果我像下面这样去写呢?
<!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>
<!--引入Vue-->
<script type="text/javascript" src="../js/vue.js"></script>
<style>
</style>
</head>
<body>
<!--准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br><br>
名:<input type="text" v-model="lastName"> <br><br>
全名:<span>{{fullName}}</span> <br><br>
全名:<span>{{fullName}}</span> <br><br>
全名:<span>{{fullName}}</span> <br><br>
全名:<span>{{fullName}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止Vue在启动时生成生产提示
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
computed:{
fullName:{
/*
get有什么作用?当有人读取fullName时,get就会被调用
且返回值就作为fullName的值
这里的get底层调用的其实就是Object.defineProperty中的get方法
*/
get(){
console.log('get被调用')
return this.firstName + '-' + this.lastName
}
}
},
methods:{
}
})
</script>
</html>
我们可以看到上面输出了4个 {{fullName}},那是不是就代表get方法会被调用4次呢?我们可以实验一下。
我们可以看到4个 {{fullName}} ,而get方法只被调用了一次。
所以,我们就引申出Vue在计算属性中做的特别好的一件事,就是缓存
它在解读第一个fullName的时候 ,Vue发现有人调用fullName了,于是就去调用get,然后拿到返回值,就作为fullName的值去使用,然后剩下的3个fullName,Vue就不会再去找get要了,直接走了缓存。这个时候反应快的就会想到一个问题,如果是缓存的话,其实也不太好,万一fullName以后改了,那它还是去读缓存的话,不是就有问题了吗,读的还是原来的值。这一点Vue也考虑到了,所以get的调用其实是有两个时机的。下面我们就仔细说说get什么时候调用。
1.初次读取fullName时,get会被调用。
2.所依赖的数据发生变化时,get也会被调用。
那么这个时候计算属性相对于methods的优势就体现出来了,计算属性有缓存而methods没有
5.set什么时候会被调用?
如果我们的计算属性在计算完之后,只是简单的读取,那么只写get方法就足够了,如果我们的计算属性在后期还有可能被修改,则必须有set方法
<!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>
<!--引入Vue-->
<script type="text/javascript" src="../js/vue.js"></script>
<style>
</style>
</head>
<body>
<!--准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br><br>
名:<input type="text" v-model="lastName"> <br><br>
全名:<span>{{fullName}}</span> <br><br>
全名:<span>{{fullName}}</span> <br><br>
全名:<span>{{fullName}}</span> <br><br>
全名:<span>{{fullName}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止Vue在启动时生成生产提示
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
computed:{
fullName:{
/*
get有什么作用?当有人读取fullName时,get就会被调用
且返回值就作为fullName的值
这里的get底层调用的其实就是Object.defineProperty中的get方法
*/
get(){
console.log('get被调用')
return this.firstName + '-' + this.lastName
},
//set什么时候调用,当fullname被修改时
set(value){
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
},
methods:{
}
})
</script>
</html>
实现效果:
6.计算属性简写
刚才我们演示了计算属性的一个完整的情况,有读取,有修改。但是更多的情况,计算属性是不修改的。计算出来,呈现到页面上去看才是更多的情况。而一旦我们确定我们只需要读取不需要修改,我们就可以使用简写形式。
<!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>
<!--引入Vue-->
<script type="text/javascript" src="../js/vue.js"></script>
<style>
</style>
</head>
<body>
<!--准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br><br>
名:<input type="text" v-model="lastName"> <br><br>
全名:<span>{{fullName}}</span> <br><br>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止Vue在启动时生成生产提示
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
computed:{
//简写形式
fullName:function(){
console.log('get被调用')
return this.firstName + '-' + this.lastName
}
},
methods:{
}
})
</script>
</html>
而上面的方式还可以继续精简为:
fullName(){
console.log('get被调用')
return this.firstName + '-' + this.lastName
}
7.总结
1.定义:要用的属性不存在,需要通过已有属性计算得来
2.原理:底层借助了Object.defineproperty方法提供的getter和setter
3.get函数什么时候执行?
(1)初次读取时会执行一次
(2)当依赖的数据发生变化时会被再次调用
4.优势:与methods实现相比,内部有缓存机制(复用),效率更高。调试方便
5.备注:
(1)计算属性会出现在vm上,直接读取使用即可
(2)如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。