在这之前,我们往html里面绑定的数据,都是非常直接的。给定一个值,就直接绑定它了。
但是更多的情况下,我们在绑定之前,需要对这些数据做一些处理。这就是计算属性发挥作用的地方了。
有了前面各期知识的加成,这次我们来一个好玩的。我们做一个愿望清单,列表里面是想买的PS4游戏:
点击购买某个游戏之后,它会自动从愿望清单移除,出现在已入游戏列表里面。同时,余额也相应减少:
当然,这里不涉及任何的真实货币结算,只是点击一下按钮,做出余额相应减少和两边列表更新的效果。
注:游戏价格是PSN上面2016圣诞元旦这段期间的价格,查的当时是港元,所以我们整个demo就使用港元作为货币单位了。
为了避免篇幅过长,这一期的代码只截取重点部分,完整的代码请看github代码库的版本。
本期的简单database
我把查询到的数据弄成了一个小DB,放在gamesDB.js
文件里面,主要是用来做列表循环,下面是它的样子:
// gamesDB.js
var games = [
{
"id" : 0,
"name" : "神秘海域123",
"img" : "../static/images/uncharted-collection.jpg",
"price" : 160.80,
"purchased": false
},
/* 余下的部分省略 */
]
purchased
字段表示该游戏是否已购入,初始时所有为false
。
HTML拆解
使用大胡子语法绑定余额
myBalance
在这个demo里面会全部使用大胡子语法。因为会遇到像下面这样,需要在数据前后加额外文字的情况。如果一些地方用v-text
,一些地方用大胡子,代码会很难分辨,所以就统一用大胡子了。
另外,跟金钱有关的部分都会使用toFixed
方法来保留两位小数。
<div class="page-header">
<h2 class="myCash">我的余额:HK$ {{ myBalance.toFixed(2) }}</h2>
</div>
v-for
循环列表
循环愿望清单wishList
:
<div class="gameItem" v-for="item in wishList">
<!-- v-bind 绑定图片属性 -->
<img :src="item.img">
<div class="gameInfo">
<h2 class="name">{{ item.name }}</h2>
<p class="price">HK$ {{ item.price.toFixed(2) }}</p>
<!-- v-on 监听点击事件 -->
<button class="btn btn-danger btn-lg" @click="buyGame(item.id)">立即购买</button>
</div>
</div>
已入游戏列表myGames
的循环同理,只是会减少一些UI:
<div class="gameItem" v-for="item in myGames">
<img :src="item.img">
<div class="gameInfo">
<h2 class="name">{{ item.name }}</h2>
</div>
</div>
初始的JS
window.games
是为了方便,我在gamesDB.js
里面直接把db挂了在window
对象下。在实际工作用请一定要避免这种做法。引入了gamesDB.js
之后,data.games
就有了我准备好的数据库里面的数据了。
另外初始化自己手上持有现金5000。
<script src="../static/js/vue.js" charset="utf-8"></script>
<!-- 引入gamesDB -->
<script src="../static/js/gamesDB.js" charset="utf-8"></script>
var app = new Vue({
el: '#app',
data: {
games: window.games,
myCash: 5000
}
})
处理点击事件
通过ID找到对应的游戏,修改purchased
字段为true
。ID会在html的绑定中传入:@click="buyGame(item.id)"
。
var app = new Vue({
el: '#app',
data: {
games: window.games,
myCash: 5000
},
methods: {
buyGame: function (id) {
this.games[id].purchased = true
}
}
})
让购入的游戏显示在已入清单中,并从愿望清单中移除
直接用filter
方法筛选出所有purchased
字段为true
的游戏,结果绑定到已入清单。同理,愿望清单中只筛选字段值为false
的游戏。
var app = new Vue({
el: '#app',
data: {
games: window.games,
myCash: 5000
},
methods: {
buyGame: function (id) {
this.games[id].purchased = true
}
},
computed: {
wishList: function () {
return this.games.filter(function (game) {
return !game.purchased
})
},
myGames: function () {
return this.games.filter(function (game) {
return game.purchased
})
}
}
})
在这里大家肯定都有一个疑问,computed
里面的方法和methods
里面的方法,到底有什么不同?它们看上去都是一样的啊。
区别就在于computed
里面的方法会进行缓存,只要方法依赖的数据源不改变,它们就不会被执行。我们用一个很简单的例子快速说明一下:
在我的余额下面插入一个h2:
<h2>data.myCash: {{ showCash }}</h2>
在计算属性里面增加一个showCash
方法,直接console里面log一下就知道它有没有执行过:
var app = new Vue({
/* 省略其它代码 */
computed: {
showCash: function () {
console.log('showCash in computed properties')
// 注意这里是myCash,它在这次的例子中是一直不变的
return this.myCash
}
}
})
进入页面的时候执行了一次(留意一下,不变的数字是为了说明而新加的h2
,用鼠标选中了文字的):
点击购买游戏(图中可以看到神秘海域已经点击购买了,所以不在愿望清单中),console中没有出现新log,说明computed
里面的showCash
方法没有再次执行:
也就是说,如果showCash
方法认为它所依赖的数据this.myCash
没有发生变化,它就不会再执行,而是直接返回之前已经计算好的值。
只有在它依赖的数据修改的情况下,才会进行更新:
然后我们修改一下html里面的调用方式,在methods
里面增加同样的方法:
记得要先屏蔽掉computed
里面的同名方法,否则会报错。
<h2>data.myCash: {{ showCash() }}</h2>
var app = new Vue({
/* 省略其它代码 */
methods: {
showCash: function () {
console.log('showCash in methods')
return this.myCash
}
}
})
进入页面的时候执行了一次:
点击购买游戏,log增加:
可见,即使数据源没有改变,methods
里面的showCash
方法依然会执行多次。
计算当前余额
最后,使用现金总额减掉已入游戏的总价,就得到当前余额啦。
var app = new Vue({
// 省略其它代码
computed: {
myBalance: function () {
var sum = 0
this.myGames.forEach(function (item, index) {
sum += item.price
})
return this.myCash - sum
}
}
})
这期就到这里,敬请期待下一期:组件。
写在最后
源码地址:https://github.com/levblanc/v...
视频攻略:小的不才,为求一赞,自制 本期视频攻略 在此。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。