Vue.js从入门到实践学习笔记(三)——计算属性
Vue.js从入门到实践学习笔记(三)——计算属性)表达式的逻辑过于复杂时,应当考虑使用计算属性。1、定义计算属性表达式的逻辑过于复杂时,应当考虑使用计算属性。1、定义计算属性计算属性是以函数形式,在vue实例的选项对象的computed选项中定义。<div id = "app"><p>原始字符串:{{message}}</p><p>计算后的反转字符
Vue.js从入门到实践学习笔记(三)——计算属性)
表达式的逻辑过于复杂时,应当考虑使用计算属性。计算属性主要用于封装对现有对象的各种操作,并返回一个新的数据。可以像普通的数据属性一样使用,不过要注意,如果要修改计算属性,需要为它提供setter方法。此外,计算属性会根据依赖的数据变化而更新,并且在该数据没有变化时,使用缓存的计算属性数据。
1、定义计算属性
计算属性是以函数形式,在vue实例的选项对象的computed选项中定义。
<div id = "app">
<p>原始字符串:{{message}}</p>
<p>计算后的反转字符串:{{reversedMessage}}</p>
</div>
<script>
var vm=new Vue({
el:'#app',
data:{
message:'hello world! hey!'
},
computed:{
//计算属性的 getter
reversedMessage:function(){
return this.message.split('').reverse().join('')
}
}
})
</script>
上例中声明了一个计算属性reversedMessage,给出的函数将用作属性vm.reversedMessage的getter函数。
计算属性默认只有getter,因此是不能直接修改计算属性的,如有需要也可以提供一个setter。
<div id = "app">
<p>原始字符串:{{message}}</p>
<p>计算后的反转字符串:{{reversedMessage}}</p>
<p>firstName:<input type="text" v-model="firstName"></p>
<p>lastName:<input type="text" v-model="lastName"></p>
<p>{{fullName}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm=new Vue({
el:'#app',
data:{
message:'hello world! hey!',
firstName:'Smith',
lastName:'Will'
},
computed:{
fullName:{
//getter
get: function(){
return this.firstName+' '+this.lastName
},
//setter
set:function(newValue){
var names=newValue.split(' ')
this.firstName=names[0]
this.lastName=names[names.length-1]
}
},
//计算属性的 getter
reversedMessage:function(){
return this.message.split('').reverse().join('')
}
}
})
</script>
上例中,可以在控制台输入vm.fullName="Bruce wills"
修改计算属性fullName的值。
2、计算属性缓存
复杂的表达式也可以放到方法中去实现(methods),然后调用方法即可。
注意:调用计算属性直接用函数名,调用方法要在函数名后加括号()。
既然使用方法能实现与计算属性相同的结果,那么还有必要使用计算属性吗?
答案是有必要的。
因为计算属性是基于它的响应式依赖进行缓存的,只有在计算属性的相关响应式依赖发生改变时才会重新求值。
这意味着,只要message没有发生改变,多次访问reversedMessage计算属性会立即返回之前的计算结果,而不会再次执行函数。
而如果采用方法(methods),不管什么时候访问,都会重新执行函数。
下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖:
computed: {
now: function () {
return Date.now()
}
}
为什么需要缓存?
假设有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后可能有其他的计算属性依赖于 A 。如果没有缓存,将不可避免的多次执行 A 的 getter。
如果不希望有缓存,请用方法来替代。
3、v-for和v-if一起使用的替代方案
v-for和v-if一起使用,可以在渲染列表时,根据v-if指令的条件判断来过滤列表中不满足条件的列表项。
这个功能可以用计算属性来完成。
<div id="app">
<h1>已完成的工作计划</h1>
<ul>
<li v-for="plan in completedPlans">
{{plan.content}}
</li>
</ul>
<h1>未完成的工作计划</h1>
<ul>
<li v-for="plan in incompletePlans">
{{plan.content}}
</li>
</ul>
</div>
<script src="vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
plans: [
{content: '写《Vue.js无难事》', isComplete: false},
{content: '买菜', isComplete: true},
{content: '写PPT', isComplete: false},
{content: '做饭', isComplete: true},
{content: '打羽毛球', isComplete: false}
]
},
computed: {
// 计算属性的 getter
completedPlans: function () {
return this.plans.filter(plan => plan.isComplete);
},
incompletePlans: function(){
return this.plans.filter(plan => !plan.isComplete);
}
}
})
</script>
不建议v-for和v-if同时用在一个元素上。因为即使由于v-if指令的使用而渲染了部分元素,但每次重新渲染的时候仍要遍历整个列表,而不论渲染的元素是否发生了改变。
采用计算属性过滤后再遍历,可以获得如下好处:
- 过滤后的列表只会在plans数组发生变化时才被重新计算,过滤更高效。
- 使用
v-for="plan in completedPlans"
之后,渲染时只遍历已完成的计划,渲染更高效。 - 解耦渲染层的逻辑,可维护性更强。
4、实例:购物车列表的实现
<!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>Document</title>
<style>
body{
width:600px;
}
table{
border: 1px solid black;
width:100%;
}
th{
height: 50px;
}
th,td{
border-bottom: 1px solid #ddd;
text-align: center;
}
span{
float:right;
}
[v-cloak]{
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<table>
<thead>
<tr>
<th>编号</th>
<th>商品名称</th>
<th>单价</th>
<th>数量</th>
<th>金额</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(book,index) in books" :key="book.id">
<td>{{book.id}}</td>
<td>{{book.title}}</td>
<td>{{book.price}}</td>
<td>
<button :disabled="book.count==1" @click="book.count--">-</button>
{{book.count}}
<button @click="book.count++">+</button>
</td>
<td>{{itemPrice(book.price, book.count)}}</td>
<td><button @click="deleteItem(index)">删除</button></td>
</tr>
</tbody>
</table>
<span>总价:¥{{totalPrice}}</span>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm=new Vue({
el:'#app',
data:{
books:[
{
id:1,
title:'vue.js无难事',
price:98,
count:1
},
{
id:2,
title:'vue.js无难事',
price:98,
count:1
},
{
id:3,
title:'vue.js无难事',
price:98,
count:1
},
{
id:4,
title:'vue.js无难事',
price:98,
count:1
},
{
id:5,
title:'vue.js无难事',
price:98,
count:1
}
]
},
methods:{
itemPrice(price, count){
return price*count;
},
deleteItem(index){
this.books.splice(index,1);
}
},
computed:{
totalPrice(){
var total = 0;
for(let book of this.books){
total+=book.price*book.count;
}
return total;
}
}
})
</script>
</body>
</html>
更多推荐
所有评论(0)