在这之前,我们往html里面绑定的数据,都是非常直接的。给定一个值,就直接绑定它了。
但是更多的情况下,我们在绑定之前,需要对这些数据做一些处理。这就是计算属性发挥作用的地方了。
有了前面各期知识的加成,这次我们来一个好玩的。我们做一个愿望清单,列表里面是想买的PS4游戏:
点击购买某个游戏之后,它会自动从愿望清单移除,出现在已入游戏列表里面。同时,余额也相应减少:
当然,这里不涉及任何的真实货币结算,只是点击一下按钮,做出余额相应减少和两边列表更新的效果。
注:游戏价格是PSN上面2016圣诞元旦这段期间的价格,查的当时是港元,所以我们整个demo就使用港元作为货币单位了。
为了避免篇幅过长,这一期的代码只截取重点部分,完整的代码请看github代码库的版本。
我把查询到的数据弄成了一个小DB,放在 gamesDB.js
文件里面,主要是用来做列表循环,下面是它的样子:
// gamesDB.js var games = [ { "id" : 0, "name" : "神秘海域123", "img" : "../static/images/uncharted-collection.jpg", "pice" : 160.80, "purchased": false }, /* 余下的部分省略 */ ]
purchased
字段表示该游戏是否已购入,初始时所有为 false
。
使用大胡子语法绑定余额 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>
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...
视频攻略:小的不才,为求一赞,自制 本期视频攻略 在此。