本篇目录:
5.1 基本指令css
5.2 条件渲染指令html
5.4 方法与事件webpack
回顾一下第2.2节,咱们己经介绍过指令(Directive)的概念了,Vue.js的指令是带有特殊前缀v-
的HTML特性,它绑定一个表达式,并将一些特性应用到DOM上。
其实咱们已经用到过不少Vue内置的指令,好比v-html
、v-pre
,还有上一章的v-bind
。
本章将继续介绍Vue.js中更多经常使用的内置指令。vue-router
v-cloak
不须要表达式,它会在Vue实例结束编译时从绑定的HTML元素上移除,常常和CSS的display:none;
配合使用:编程
1 <div id="app" v-cloak> 2 {{message}} 3 </div> 4 5 <script> 6 var app = new Vue({ 7 el: "#app", 8 data: { 9 message: "这是一段文本" 10 } 11 }); 12 </script>
这时虽然已经加了指令v-cloak
,但其实并无起到任何做用。
当网速较慢、Vue.js文件还没加载完时,在页面上会显示{{message}}
的字样。
直到Vue建立实例、编译模板时,DOM才会被替换,因此这个过程屏幕是有闪动的。
只要加一句CSS就能够解决这个问题了:数组
1 <style> 2 [v-cloak] { 3 display: none; 4 } 5 </style>
在通常状况下,v-cloak
是一个解决初始化慢致使页面闪动的最佳实践,对于简单的项目很实用,可是在具备工程化的项目里,好比后面进阶篇将介绍webpack和vue-router时,项目的HTML结构只有一个空的div元素,剩余的内容都是由路由去挂载不一样组件完成的,因此再也不须要v-cloak
。安全
v-once
也是一个不须要表达式的指令,做用是定义它的元素或组件只渲染一次,包括元素或组件的全部子节点。
首次渲染后,再也不随数据的变化从新渲染,将被视为静态内容,例如:app
1 <div id="app"> 2 <span v-once>{{message}}</span> 3 <div v-once> 4 <span>{{message}}</span> 5 </div> 6 </div> 7 8 <script> 9 var app = new Vue({ 10 el: "#app", 11 data: { 12 message: "这是一段文本" 13 } 14 }); 15 </script>
v-once
在业务中也不多使用,当你须要进一步优化性能时,可能会用到。
与JavaScript的条件语句if
、else
、else if
相似,Vue.js的条件指令能够根据表达式的值在DOM中渲染或销毁元素/组件。
例如:
1 <div id="app"> 2 <p v-if="status===1">当status为1时显示此行</p> 3 <p v-else-if="status===2">当status为2时显示此行</p> 4 <p v-else>不然显示此行</p> 5 </div> 6 7 <script> 8 var app = new Vue({ 9 el: "#app", 10 data: { 11 status: 1 12 } 13 }); 14 </script>
v-else-if
要紧跟v-if
,v-else
要紧跟v-else-if
或v-if
。
表达式的值为true
时,当前元素/组件及全部子节点将被渲染,为false
时被移除。
若是一次判断的是多个元素,能够在Vue.js内置的<template>
元素上使用条件指令,最终渲染的结果不包含钙元素。
例如:
1 <div id="app"> 2 <template v-if="status===1"> 3 <p>这是一段文本</p> 4 <p>这是一段文本</p> 5 <p>这是一段文本</p> 6 </template> 7 </div> 8 9 <script> 10 var app = new Vue({ 11 el: "#app", 12 data: { 13 status: 1 14 } 15 }); 16 </script>
Vue在渲染元素时,出于效率考虑,会尽量地复用已有的元素而非从新渲染。
好比下面的示例:
1 <div id="app"> 2 <template v-if="type==='name'"> 3 <label for="name">用户名:</label> 4 <input type="text" id="name" name="name" placeholder="请输入用户名"> 5 </template> 6 <template v-else> 7 <label for="mail">邮箱:</label> 8 <input type="text" id="mail" name="mail" placeholder="请输入邮箱"> 9 </template> 10 <button type="button" @click="handleToggleClick">切换输入类型</button> 11 </div> 12 13 <script> 14 var app = new Vue({ 15 el: "#app", 16 data: { 17 type: "name" 18 }, 19 methods: { 20 handleToggleClick: function() { 21 this.type = this.type === "name" ? "mail" : "name"; 22 } 23 } 24 }); 25 </script>
如图5-1和图5-2所示,键入内容后,点击切换按钮,虽然DOM变了,可是以前在输入框键入的内容并无改变,只是替换了placeholder的内容,说明<input>
元素被复用了。


若是你不但愿这样作,可使用Vue.js提供的key属性,它可让你本身决定是否要复用元素,key的值必须是惟一的。
例如:
1 <div id="app"> 2 <template v-if="type==='name'"> 3 <label for="name">用户名:</label> 4 <input type="text" id="name" name="name" key="name-input" placeholder="请输入用户名"> 5 </template> 6 <template v-else> 7 <label for="mail">邮箱:</label> 8 <input type="text" id="mail" name="mail" key="mail-input" placeholder="请输入邮箱"> 9 </template> 10 <button type="button" @click="handleToggleClick">切换输入类型</button> 11 </div> 12 13 <script> 14 var app = new Vue({ 15 el: "#app", 16 data: { 17 type: "name" 18 }, 19 methods: { 20 handleToggleClick: function() { 21 this.type = this.type === "name" ? "mail" : "name"; 22 } 23 } 24 }); 25 </script>
给两个<input>
元素都增长key
后,就不会复用了,切换类型时键入的内容也会被删除,不过<label>
元素仍然是被复用的,由于没有添加key
属性。
v-show
的用法与v-if
基本一致,只不过v-show
是改变元素的CSS属性display
。
当v-show
表达式的值为false
时,元素会隐藏,查看DOM结构会看到元素上加载了内联样式display:none
。
例如:
1 <div id="app"> 2 <p v-show="status===1">当status为1时显示此行</p> 3 </div> 4 5 <script> 6 var app = new Vue({ 7 el: "#app", 8 data: { 9 status: 2 10 } 11 }); 12 </script>
渲染后的结果为:
1 <div id="app"> 2 <p style="display: none;">当status为1时显示此行</p> 3 </div>
提示:
v-show
不能在<template>
上使用。
v-if
和v-show
具备相似的功能,不过v-if
才是真正的条件渲染,它会根据表达式适当地销毁或重建元素及绑定的事件或子组件。
若表达式初始值为false
,则一开始元素/组件并不会渲染,只有当条件第一次变为true
时才开始编译。
而v-show
只是简单的CSS属性切换,不管条件真与否,都会被编译。
相比之下,v-if
更适合条件不常常改变的场景,由于它切换开销相对较大,而v-show
适用于频繁切换条件。
当须要将一个数组遍历或枚举一个对象循环显示时,就会用到列表渲染指令v-for
。
它的表达式需结合in
来使用,相似item in items
的形式。
看下面的代码:
1 <div id="app"> 2 <ul> 3 <li v-for="book in books">{{book.name}}</li> 4 </ul> 5 </div> 6 7 <script> 8 var app = new Vue({ 9 el: "#app", 10 data: { 11 books: [ 12 { name: "《Vue.js实战》" }, 13 { name: "《JavaScript语言精粹》" }, 14 { name: "《JavaScript高级程序设计》" } 15 ] 16 } 17 }); 18 </script>
咱们定义一个数组类型的数据books
,用v-for
将<li>
标签循环渲染。
效果如图5-3所示:

在表达式中,books
是数据,book
是当前数组元素的别名,循环出的每一个<li>
内的元素均可以访问到对应的当前数据book
。
列表渲染也支持用of
来代替in
做为分隔符,它更接近JavaScript迭代器的语法:
1 <li v-for="book of books">{{book.name}}</li>
v-for
的表达式支持一个可选参数做为当前项的索引,例如:
1 <div id="app"> 2 <ul> 3 <li v-for="(book, index) in books">{{index}} - {{book.name}}</li> 4 </ul> 5 </div> 6 7 <script> 8 var app = new Vue({ 9 el: "#app", 10 data: { 11 books: [ 12 { name: "《Vue.js实战》" }, 13 { name: "《JavaScript语言精粹》" }, 14 { name: "《JavaScript高级程序设计》" } 15 ] 16 } 17 }); 18 </script>
分隔符in
前的语句使用括号,第二项就是books
当前项的索引。
渲染后的结果如图5-4所示:

提示:
若是你使用过Vue.js 1.x的版本,这里的index
也能够由内置的$index
代替,不过在2.x里取消了该用法。
与v-if
同样,v-for
也能够用在内置标签<template>
上,将多个元素进行渲染:
1 <div id="app"> 2 <ul> 3 <template v-for="book in books"> 4 <li>书名:{{book.name}}</li> 5 <li>做者:{{book.author}}</li> 6 </template> 7 </ul> 8 </div> 9 10 <script> 11 var app = new Vue({ 12 el: "#app", 13 data: { 14 books: [ 15 { name: "《Vue.js实战》", author:"梁灏" }, 16 { name: "《JavaScript语言精粹》", author:"Douglas Crockford" }, 17 { name: "《JavaScript高级程序设计》", author:"Nicholas C.Zakas" } 18 ] 19 } 20 }); 21 </script>
除了数组外,对象的属性也是能够遍历的。
例如:
1 <div id="app"> 2 <span v-for="value in user">{{value}}</span> 3 </div> 4 5 <script> 6 var app = new Vue({ 7 el: "#app", 8 data: { 9 user: { 10 name: "Aresn", 11 gender: "男", 12 age: 26 13 } 14 } 15 }); 16 </script>
渲染后的结果为:
1 <div id="app"> 2 <span>Aresn</span> 3 <span>男</span> 4 <span>26</span> 5 </div>
遍历对象属性时,有两个可选参数,分别是键名和索引:
1 <div id="app"> 2 <ul> 3 <li v-for="(value, key, index) in user"> 4 {{index}} - {{key}} - {{value}} 5 </li> 6 </ul> 7 </div> 8 9 <script> 10 var app = new Vue({ 11 el: "#app", 12 data: { 13 user: { 14 name: "Aresn", 15 gender: "男", 16 age: 26 17 } 18 } 19 }); 20 </script>
渲染后的结果如图5-5所示:

v-for
还能够迭代整数:
1 <div id="app"> 2 <span v-for="n in 10">{{n}} </span> 3 </div> 4 5 <script> 6 var app = new Vue({ 7 el: "#app" 8 }); 9 </script>
渲染后的结果为:
1 <div id="app"> 2 <span>1 </span> 3 <span>2 </span> 4 <span>3 </span> 5 <span>4 </span> 6 <span>5 </span> 7 <span>6 </span> 8 <span>7 </span> 9 <span>8 </span> 10 <span>9 </span> 11 <span>10 </span> 12 </div>
Vue的核心是数据与视图的双向绑定,当咱们修改数组时,Vue会检测到数据变化,因此用v-for
渲染的视图也会当即更新。
Vue包含了一组观察数组变异的方法,使用它们改变数组也会触发视图更新:
例如,咱们将以前一个示例的数据books
添加一项:
1 app.books.push({ 2 name: "《CSS解密》", 3 author: "[希] Lea Verou" 4 });
能够尝试编写完整示例来查看效果。
使用以上方法会改变被这些方法调用的原始数组,有些方法不会改变原数组,例如:
它们返回的是一个新数组,在使用这些非变异方法时,能够用新数组来替换原数组。
仍是以前展现节目的示例,咱们找出含有JavaScript
关键词的书目,例如:
1 <div id="app"> 2 <ul> 3 <template v-for="book in books"> 4 <li>书名:{{book.name}}</li> 5 <li>做者:{{book.author}}</li> 6 </template> 7 </ul> 8 </div> 9 10 <script> 11 var app = new Vue({ 12 el: "#app", 13 data: { 14 books: [ 15 { name: "《Vue.js实战》", author:"梁灏" }, 16 { name: "《JavaScript语言精粹》", author:"Douglas Crockford" }, 17 { name: "《JavaScript高级程序设计》", author:"Nicholas C.Zakas" } 18 ], 19 } 20 }); 21 22 app.books = app.books.filter(function(item) { 23 return item.name.match(/JavaScript/); 24 }); 25 </script>
渲染的结果中,第一项《Vue.js实战》被过滤掉了,只显示了书名中含有JavaScript的选项。
Vue在检测到数组变化时,并非直接从新渲染整个列表,而是最大化地复用DOM元素。
替换的数组中,含有相同元素的项不会被从新渲染,所以能够大胆地用新数组来替换就数组,不用担忧性能问题。
须要注意的是,如下变更的数组中,Vue是不能检测到的,也不会触发视图更新:
app.book[3] = {...}
app.books.length=1
解决第一个问题能够用两种方法实现一样的效果,第一种是使用Vue内置的set方法:
1 Vue.set(app.books, 2, { 2 name: "《CSS揭秘》", 3 author: "[希] Lea Verou" 4 });
若是是在webpack中使用组件化的方式(进阶篇中将介绍),默认是没有导入Vue的,这时可使用$set
。
例如:
1 this.$set(app.books, 2, { 2 name: "《CSS揭秘》", 3 author: "[希] Lea Verou" 4 });
这里的this
指向的就是当前组件的实例,即app
。
在非webpack模式下也能够用$set
方法,例如app.$set(...)
。
另外一种方法:
1 app.books.splice(2, 1, { 2 name: "《CSS揭秘》", 3 author: "[希] Lea Verou" 4 });
第二个问题也能够直接用splice
来解决:
1 app.books.splice(1);
当你不想改变原数组,想经过一个数组的副原本作过滤或排序的显示时,可使用计算属性来返回过滤或排序后的数组。
例如:
1 <div id="app"> 2 <ul> 3 <template v-for="book in filterBooks"> 4 <li>书名:{{book.name}}</li> 5 <li>做者:{{book.author}}</li> 6 </template> 7 </ul> 8 </div> 9 10 <script> 11 var app = new Vue({ 12 el: "#app", 13 data: { 14 books: [ 15 { name: "《Vue.js实战》", author:"梁灏" }, 16 { name: "《JavaScript语言精粹》", author:"Douglas Crockford" }, 17 { name: "《JavaScript高级程序设计》", author:"Nicholas C.Zakas" } 18 ], 19 }, 20 computed: { 21 filterBooks: function () { 22 return this.books.filter(function (book) { 23 return book.name.match(/JavaScript/); 24 }); 25 } 26 } 27 }); 28 </script>
上例是把书名中包含"JavaScript"关键词的数据过滤出来,计算属性filterBooks
依赖books
,可是不会修改books
。
实现排序也是相似的,好比在此基础上新加一个计算属性sortedBooks
,按照书名的长度由长到短进行排序:
1 computed: { 2 sortedBooks: function () { 3 return this.books.sort(function (a, b) { 4 return a.name.length < b.name.length; 5 }); 6 } 7 }
提示:
在Vue.js 2.x中废弃了1.x中内置的limitBy
、filterBy
和orderBy
过滤器,统一改用计算属性来实现。
在第2.2节,咱们已经引入了Vue事件处理的概念v-on
。
在事件绑定上,相似原生JavaScript的onclick
等写法,也是在HTML上进行监昕的。
例如,咱们监昕一个按钮的点击事件,设置一个计数器,每次点击都加1:
1 <div id="app"> 2 点击次数: {{counter}} 3 <button type="button" @click="counter++">累加1</button> 4 </div> 5 6 <script> 7 var app = new Vue({ 8 el: "#app", 9 data: { 10 counter: 0 11 } 12 }); 13 </script>
提示:
上面的@click
等同于v-on:click
,是一个语法糖,如不特殊说明,后面都将使用语法糖写法,能够回顾第2.3章节。
@click
的表达式能够直接使用JavaScript语句,也能够是一个在Vue实例中methods
选项内的函数名。
好比对上例进行扩展,再增长一个按钮,点击一次,计数器累加10:
1 <div id="app"> 2 点击次数: {{counter}} 3 <button type="button" @click="handleAdd()">累加1</button> 4 <button type="button" @click="handleAdd(10)">累加10</button> 5 </div> 6 7 <script> 8 var app = new Vue({ 9 el: "#app", 10 data: { 11 counter: 0 12 }, 13 methods: { 14 handleAdd: function(count) { 15 count = count || 1; 16 // this指向当前Vue实例app 17 this.counter += count; 18 } 19 } 20 }); 21 </script>
在methods
中定义了咱们须要的方法供@click
调用,须要注意的是,@click
调用的方法名后能够不跟括号()
。
此时,若是该方法有参数,默认会将原生事件对象event
传入,能够尝试修改成@click="handleAdd"
,而后在handleAdd
内打印出count
参数看看。
在大部分业务场景中,若是方法不须要传入参数,为了渐变能够不写括号。
这种在HTML元素上监听事件的设计看似将DOM与JavaScript紧耦合,违背分离的原理,实则恰好相反。
由于经过HTML就能够知道调用的是哪一个方法,讲逻辑与DOM接口,便于维护。
最重要的是,当ViewModel销毁时,全部的事件处理器都会自动删除,无需本身清理。
Vue提供了一个特殊变量$event
,用于访问原生DOM事件,例以下面的实例能够阻止链接打开:
1 <div id="app"> 2 <a href="http://www.baidu.com" @click="handleClick('禁止打开', $event)">打开链接</a> 3 </div> 4 5 <script> 6 var app = new Vue({ 7 el: "#app", 8 methods: { 9 handleClick: function(message, event) { 10 event.preventDefault(); 11 alert(message); 12 } 13 } 14 }); 15 </script>
在上例使用的event.preventDefault()
也能够用Vue事件的修饰符来实现。
在@
绑定的事件后加小圆点.
,在跟一个后缀来使用修饰符。
Vue支持如下修饰符:
具体用法以下:
1 <!-- 阻止事件冒泡 --> 2 <a @click.stop="handle"></a> 3 4 <!-- 提交事件再也不重载页面 --> 5 <form @submit.prevent="handle"></form> 6 7 <!-- 修饰符能够串联 --> 8 <a @click.stop.prevent="handle"></a> 9 10 <!-- 只有修饰符 --> 11 <form @submit.prevent></form> 12 13 <!-- 添加事件侦听器时使用事件捕获模式 --> 14 <div @click.capture="handle">...</div> 15 16 <!-- 只当事件在该元素自己(而不是子元素)触发时触发回调 --> 17 <div @click.self="handle">...</div> 18 19 <!-- 只触发一次,组件一样适用 --> 20 <div @click.once="handle">...</div>
在表单元素上监听键盘事件时,还可使用按键修饰符。
好比按下具体某个键时才调用方法:
1 <!-- 只有在keyCode是13时调用 vm.submit() --> 2 <input @keyup.13="submit">
也能够本身配置具体按键:
1 Vue.config.keyCodes.f1 = 112; 2 // 全局定义后,就可使用@keyup.f1
除了具体的某个keyCode外,Vue还提供了一些快捷名称。
如下是所有的别名:
这些按键修饰符也能够组合使用,或和鼠标一块儿使用:
例如:
1 <!-- Shift + S --> 2 <input @keyup.shift.83="handleSave"> 3 4 <!-- Ctrl + Click --> 5 <div @click.ctrl="doSomething">Do Smothing</div>
以上就是事件指令v-on
的基本内容,在第7章的组件中,咱们还将介绍用v-on
来绑定自定义事件。
前五章内容基本涵盖了Vue.js最核心的经常使用的知识点,掌握这些内容已经能够上手开发一些小功能了。
本节则以Vue.js的计算属性、内置指令、方法等内容为基础,完成一个在业务中具备表明性的小功能:购物车。
在开始写代码前,要对需求进行分析,这样有助于咱们理清业务逻辑,尽量还原设计产品交互。
购物车须要展现一个已加入购物车的商品列表,包含商品名称、商品单价、购买数量和操做等信息,还须要实时显示购买的总价。
其中购买数量能够增长或减小,每类商品还能够从购物车中移除。
最终实现的效果如图5-6所示:

在明确需求后,咱们就能够开始编程了。
由于业务代码较多,此次咱们将HTML、CSS、JavaScript分离为3个文件,便于阅读和维护:
如今index.html中引入Vue.js和相关资源,建立一个根元素来挂在Vue实例:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <title>购物车示例</title> 8 <link rel="stylesheet" href="index.css"> 9 </head> 10 <body> 11 <div id="app" v-cloak></div> 12 13 <script src="https://unpkg.com/vue/dist/vue.min.js"></script> 14 <script src="index.js"></script> 15 </body> 16 </html>
注意,这里将vue.min.js
和index.js
文件写在<body>
的最底部,若是写在<head>
里,Vue实例将没法建立,由于此时DOM尚未被解析完成,除非经过异步或在事件DOMContentLoaded(IE是onreadystatechange
)触发时再建立Vue实例,这有点像jQuery的$(document).ready()
方法。
本例须要用到Vue.js的computed
、methods
等选项,在index.js
中县初始化实例:
1 var app = new Vue({ 2 el: "#app", 3 data: {}, 4 computed: {}, 5 methods: {} 6 });
咱们须要的数据比较简单,只有一个列表,里面包含了商品名称、单价、购买数量。
在实际业务中,这个列表应该是经过Ajax从服务端动态获取的,这里只作示例,因此直接写入在data
选项内,另外每一个商品还应该有一个全局惟一的id
。
咱们在data
内写入列表list
:
1 data: { 2 list: [ 3 {id: 1, name: "iPhone 7", price: 6188, count: 1}, 4 {id: 2, name: "iPad Pro", price: 5888, count: 1}, 5 {id: 3, name: "McaBook Pro", price: 21488, count: 1} 6 ] 7 }
数据构建好后,能够在index.html
中展现列表了,毫无疑问,确定会用到v-for
,不过在此以前,咱们先作一些小的优化。
由于每一个商品都是能够从购物车移除的,因此当列表为空时,在页面中显示一个”购物车为空“的提示更为友好,咱们能够经过判断数组list
的长度来实现该功能:
1 <div id="app" v-cloak> 2 <template v-if="list.length"></template> 3 <div v-else>购物车为空</div> 4 </div>
<template>
里的代码分两部分,一部分是商品列表信息,咱们用表格<table>
来展示;
另外一部分就是带有千位分隔符的商品总价(每隔三位数加一个逗号)。
这部分代码以下:
1 <template v-if="list.length"> 2 <table> 3 <thead> 4 <tr> 5 <th></th> 6 <th>商品名称</th> 7 <th>商品单价</th> 8 <th>购买数量</th> 9 <th>操做</th> 10 </tr> 11 </thead> 12 <tbody> 13 </tbody> 14 </table> 15 <div>总价:¥ {{totalPrice}}</div> 16 </template>
总价totalPrice
是依赖于商品列表而动态变化的,因此咱们用计算属性来实现,顺便将结果转换为带有"千位分隔符"的数字。
在index.js
的computed
选项内写入:
1 computed: { 2 totalPrice: function () { 3 var total = 0; 4 for (var i = 0; i < this.list.length; i++) { 5 var item = this.list[i]; 6 total += item.price * item.count; 7 } 8 return total.toString().replace(/\B(?=(\d{3})+$)/g, ","); 9 } 10 }
这段代码难点在于千位分隔符的转换,你们能够查阅正则匹配的相关内容后尝试了解replace()
的正则含义。
最后就剩下商品列表的渲染和相关的几个操做了。
如今<body>
内把数组list
用v-for
指令循环出来:
1 <tbody> 2 <tr v-for="(item, index) in list"> 3 <td>{{index + 1}}</td> 4 <td>{{item.name}}</td> 5 <td>{{item.price}}</td> 6 <td> 7 <button @click="handleReduce(index)" :disabled="item.count===1">-</button> 8 {{item.count}} 9 <button @click="handleAdd(index)">+</button> 10 </td> 11 <td> 12 <button @click="handleRemove(index)">移除</button> 13 </td> 14 </tr> 15 </tbody>
商品序号、名称、单价、数量都是直接使用插值来完成的,在第4列的两个按钮<button>
用于增/减购买数量,分别绑定了两个方法handleReduce
和handleAdd
,参数都是当前商品在数组list
中的索引。
不少时候,一个元素上会同时使用多个特性(尤为是在组件中使用props
传递数据时),写在一行代码较长,不便阅读,因此建议特性过多时,将每一个特性都单独写为一行,好比第一个<button>
中使用了v-bind
和v-on
两个指令(这里都用的语法糖写法)。
每件商品购买数量最少是1件,因此当count
为1时,不容许再继续减小,因此这里给<button>
动态绑定了disabled
特性来禁用按钮。
在index.js
中继续完成剩余的3个方法:
1 methods: { 2 handleReduce: function (index) { 3 if (this.list[index].count === 1) return; 4 this.list[index].count--; 5 }, 6 handleAdd: function (index) { 7 this.list[index].count++; 8 }, 9 handleRemove: function (index) { 10 this.list.splice(index, 1); 11 } 12 }
这3个方法都是直接对数组list
的操做,没有太复杂的逻辑。
须要说明的是,虽然在<button>
上已经绑定了disabled
特性,可是在handleReduce
方法内有判断了以便,这是由于在某些时刻,可能不必定会用<button>
元素,也多是div、span等,给它们增长disabled
是没有任何做用的,因此安全起见,在业务逻辑中在判断一次,避免因修改HTML模板后出现bug。
一下是购物车示例的完整代码:
index.html:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <title>购物车示例</title> 8 <link rel="stylesheet" href="index.css"> 9 </head> 10 <body> 11 <div id="app" v-cloak> 12 <template v-if="list.length"> 13 <table> 14 <thead> 15 <tr> 16 <th></th> 17 <th>商品名称</th> 18 <th>商品单价</th> 19 <th>购买数量</th> 20 <th>操做</th> 21 </tr> 22 </thead> 23 <tbody> 24 <tr v-for="(item, index) in list"> 25 <td>{{index + 1}}</td> 26 <td>{{item.name}}</td> 27 <td>{{item.price}}</td> 28 <td> 29 <button @click="handleReduce(index)" :disabled="item.count===1">-</button> 30 {{item.count}} 31 <button @click="handleAdd(index)">+</button> 32 </td> 33 <td> 34 <button @click="handleRemove(index)">移除</button> 35 </td> 36 </tr> 37 </tbody> 38 </table> 39 <div>总价:¥ {{totalPrice}}</div> 40 </template> 41 <div v-else>购物车为空</div> 42 </div> 43 44 <script src="https://unpkg.com/vue/dist/vue.min.js"></script> 45 <script src="index.js"></script> 46 </body> 47 </html>
index.js:
1 var app = new Vue({ 2 el: "#app", 3 data: { 4 list: [ 5 {id: 1, name: "iPhone 7", price: 6188, count: 1}, 6 {id: 2, name: "iPad Pro", price: 5888, count: 1}, 7 {id: 3, name: "McaBook Pro", price: 21488, count: 1} 8 ] 9 }, 10 computed: { 11 totalPrice: function () { 12 var total = 0; 13 for (var i = 0; i < this.list.length; i++) { 14 var item = this.list[i]; 15 total += item.price * item.count; 16 } 17 return total.toString().replace(/\B(?=(\d{3})+$)/g, ","); 18 } 19 }, 20 methods: { 21 handleReduce: function (index) { 22 if (this.list[index].count === 1) return; 23 this.list[index].count--; 24 }, 25 handleAdd: function (index) { 26 this.list[index].count++; 27 }, 28 handleRemove: function (index) { 29 this.list.splice(index, 1); 30 } 31 } 32 });
index.css:
1 [v-cloak]{display:none;} 2 table{border:1px solid #E9E9E9; border-collapse:collapse; border-spacing:0; empty-cells:show;} 3 th, td{padding:8px 16px; border:1px solid #E9E9E9; text-align:left;} 4 th{background:#F7F7F7; color:#5C6B77; font-weight:600; white-space:nowrap;}
练习1:在当前示例基础上扩展商品列表,新增一项是否选中该商品的功能,总价变为只计算选中商品的总价,同时提供一个全选的按钮。
练习2:将商品列表list
改成一个二维数组来实现商品的分类,好比可分为"电子产品"、"生活用品"和"果蔬",同类商品聚合在一块儿。提示,你可能会用到两次v-for
。