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


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

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

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

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

在明确需求后,我们就可以开始编程了。
因为业务代码较多,这次我们将HTML、CSS、JavaScript分离为3个文件,便于阅读和维护:
- index.html (引入资源及模板)
- index.js (Vue实例及业务代码)
- index.css (样式)
现在index.html中引入Vue.js和相关资源,创建一个根元素来挂在Vue实例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>购物车示例</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<div id="app" v-cloak></div> <script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script src="index.js"></script>
</body>
</html>
注意,这里将vue.min.js
和index.js
文件写在<body>
的最底部,如果写在<head>
里,Vue实例将无法创建,因为此时DOM还没有被解析完成,除非通过异步或在事件DOMContentLoaded(IE是onreadystatechange
)触发时再创建Vue实例,这有点像jQuery的$(document).ready()
方法。
本例需要用到Vue.js的computed
、methods
等选项,在index.js
中县初始化实例:
var app = new Vue({
el: "#app",
data: {},
computed: {},
methods: {}
});
我们需要的数据比较简单,只有一个列表,里面包含了商品名称、单价、购买数量。
在实际业务中,这个列表应该是通过Ajax从服务端动态获取的,这里只做示例,所以直接写入在data
选项内,另外每个商品还应该有一个全局唯一的id
。
我们在data
内写入列表list
:
data: {
list: [
{id: 1, name: "iPhone 7", price: 6188, count: 1},
{id: 2, name: "iPad Pro", price: 5888, count: 1},
{id: 3, name: "McaBook Pro", price: 21488, count: 1}
]
}
数据构建好后,可以在index.html
中展示列表了,毫无疑问,肯定会用到v-for
,不过在此之前,我们先做一些小的优化。
因为每个商品都是可以从购物车移除的,所以当列表为空时,在页面中显示一个”购物车为空“的提示更为友好,我们可以通过判断数组list
的长度来实现该功能:
<div id="app" v-cloak>
<template v-if="list.length"></template>
<div v-else>购物车为空</div>
</div>
<template>
里的代码分两部分,一部分是商品列表信息,我们用表格<table>
来展现;
另一部分就是带有千位分隔符的商品总价(每隔三位数加一个逗号)。
这部分代码如下:
<template v-if="list.length">
<table>
<thead>
<tr>
<th></th>
<th>商品名称</th>
<th>商品单价</th>
<th>购买数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div>总价:¥ {{totalPrice}}</div>
</template>
总价totalPrice
是依赖于商品列表而动态变化的,所以我们用计算属性来实现,顺便将结果转换为带有"千位分隔符"的数字。
在index.js
的computed
选项内写入:
computed: {
totalPrice: function () {
var total = 0;
for (var i = 0; i < this.list.length; i++) {
var item = this.list[i];
total += item.price * item.count;
}
return total.toString().replace(/\B(?=(\d{3})+$)/g, ",");
}
}
这段代码难点在于千位分隔符的转换,大家可以查阅正则匹配的相关内容后尝试了解replace()
的正则含义。
最后就剩下商品列表的渲染和相关的几个操作了。
现在<body>
内把数组list
用v-for
指令循环出来:
<tbody>
<tr v-for="(item, index) in list">
<td>{{index + 1}}</td>
<td>{{item.name}}</td>
<td>{{item.price}}</td>
<td>
<button @click="handleReduce(index)" :disabled="item.count===1">-</button>
{{item.count}}
<button @click="handleAdd(index)">+</button>
</td>
<td>
<button @click="handleRemove(index)">移除</button>
</td>
</tr>
</tbody>
商品序号、名称、单价、数量都是直接使用插值来完成的,在第4列的两个按钮<button>
用于增/减购买数量,分别绑定了两个方法handleReduce
和handleAdd
,参数都是当前商品在数组list
中的索引。
很多时候,一个元素上会同时使用多个特性(尤其是在组件中使用props
传递数据时),写在一行代码较长,不便阅读,所以建议特性过多时,将每个特性都单独写为一行,比如第一个<button>
中使用了v-bind
和v-on
两个指令(这里都用的语法糖写法)。
每件商品购买数量最少是1件,所以当count
为1时,不允许再继续减少,所以这里给<button>
动态绑定了disabled
特性来禁用按钮。
在index.js
中继续完成剩余的3个方法:
methods: {
handleReduce: function (index) {
if (this.list[index].count === 1) return;
this.list[index].count--;
},
handleAdd: function (index) {
this.list[index].count++;
},
handleRemove: function (index) {
this.list.splice(index, 1);
}
}
这3个方法都是直接对数组list
的操作,没有太复杂的逻辑。
需要说明的是,虽然在<button>
上已经绑定了disabled
特性,但是在handleReduce
方法内有判断了以便,这是因为在某些时刻,可能不一定会用<button>
元素,也可能是div、span等,给它们增加disabled
是没有任何作用的,所以安全起见,在业务逻辑中在判断一次,避免因修改HTML模板后出现bug。
一下是购物车示例的完整代码:
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>购物车示例</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<div id="app" v-cloak>
<template v-if="list.length">
<table>
<thead>
<tr>
<th></th>
<th>商品名称</th>
<th>商品单价</th>
<th>购买数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in list">
<td>{{index + 1}}</td>
<td>{{item.name}}</td>
<td>{{item.price}}</td>
<td>
<button @click="handleReduce(index)" :disabled="item.count===1">-</button>
{{item.count}}
<button @click="handleAdd(index)">+</button>
</td>
<td>
<button @click="handleRemove(index)">移除</button>
</td>
</tr>
</tbody>
</table>
<div>总价:¥ {{totalPrice}}</div>
</template>
<div v-else>购物车为空</div>
</div> <script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script src="index.js"></script>
</body>
</html>
index.js:
var app = new Vue({
el: "#app",
data: {
list: [
{id: 1, name: "iPhone 7", price: 6188, count: 1},
{id: 2, name: "iPad Pro", price: 5888, count: 1},
{id: 3, name: "McaBook Pro", price: 21488, count: 1}
]
},
computed: {
totalPrice: function () {
var total = 0;
for (var i = 0; i < this.list.length; i++) {
var item = this.list[i];
total += item.price * item.count;
}
return total.toString().replace(/\B(?=(\d{3})+$)/g, ",");
}
},
methods: {
handleReduce: function (index) {
if (this.list[index].count === 1) return;
this.list[index].count--;
},
handleAdd: function (index) {
this.list[index].count++;
},
handleRemove: function (index) {
this.list.splice(index, 1);
}
}
});
index.css:
[v-cloak]{display:none;}
table{border:1px solid #E9E9E9; border-collapse:collapse; border-spacing:; empty-cells:show;}
th, td{padding:8px 16px; border:1px solid #E9E9E9; text-align:left;}
th{background:#F7F7F7; color:#5C6B77; font-weight:; white-space:nowrap;}
练习1:在当前示例基础上扩展商品列表,新增一项是否选中该商品的功能,总价变为只计算选中商品的总价,同时提供一个全选的按钮。
练习2:将商品列表list
改为一个二维数组来实现商品的分类,比如可分为"电子产品"、"生活用品"和"果蔬",同类商品聚合在一起。提示,你可能会用到两次v-for
。
Vue.js 学习笔记 第5章 内置指令的更多相关文章
- Vue.js学习笔记 第六篇 内置属性
computed属性 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> &l ...
- Vue.js 学习笔记 第2章 数据绑定和第一个Vue应用
本篇目录: 2.1 Vue实例与数据绑定 2.2 指令与事件 2.3 语法糖 学习任何一种框架,从一个Hello World应用开始是最快了解该框架特性的途径. 我们先从一段简单的HTML代码开始,感 ...
- AngularJS学习笔记(四)内置指令
说说指令 不得不赞叹,指令是ng最为强大的功能之一,好吧,也可以去掉之一,是最强大的功能.ng内置了许多自定义的指令,这避免了我们自己去造轮子.同时,ng也提供了自定义指令的功能,可以让我们的页面元素 ...
- Vue.js 学习笔记 第7章 组件详解
本篇目录: 7.1 组件与复用 7.2 使用props传递数据 7.3 组件通讯 7.4 使用slot分发内容 7.5 组件高级用法 7.6 其他 7.7 实战:两个常用组件的开发 组件(Compon ...
- Vue.js 学习笔记 第4章 v-bind 及 class与style绑定
本篇目录: 4.1 了解v-bind指令 4.2 绑定class的几种方式 4.3 绑定内联样式 DOM元素经常会动态地绑定一些class类名或style样式,本章将介绍使用v-bind指令来绑定cl ...
- Vue.js 学习笔记 第1章 初识Vue.js
本篇目录: 1.1 Vue.js 是什么 1.2 如何使用Vue.js 本章主要介绍与Vue.js有关的一些概念与技术,并帮助你了解它们背后相关的工作原理. 通过对本章的学习,即使从未接触过Vue.j ...
- Vue.js 学习笔记 第6章 表单与v-model
本篇目录: 6.1 基本用法 6.2 绑定值 6.3 修饰符 表单类控件承载了一个网页数据的录入与交互,本章将介绍如何使用指令v-model完成表单的数据双向绑定. 6.1 基本用法 表单控件在实际业 ...
- Vue.js 学习笔记 第3章 计算属性
本篇目录: 3.1 什么是计算属性 3.2 计算属性用法 3.3 计算属性缓存 模板内容的表达式常用语简单的运算,当其过长或逻辑复杂时,会难以维护,本章的计算属性就是用于解决该问题的. 3.1 什么是 ...
- Vue.js 学习笔记 一
本文的Demo和源代码已放到GitHub,如果您觉得本篇内容不错,请点个赞,或在GitHub上加个星星! https://github.com/zwl-jasmine95/Vue_test 以下所有知 ...
随机推荐
- Docker 多主机网络总结(非常全)
PS:文章首发公众号,欢迎大家关注我的公众号:aCloudDeveloper,专注技术分享,努力打造干货分享平台,二维码在文末可以扫,谢谢大家. 上篇文章介绍了容器网络的单主机网络,本文将进一步介绍多 ...
- vue config.js配置生产环境和发布环境不同的接口地址问题
第一步,分别设置不同的接口地址 首先,我们分别找到下面的文件: /config/dev.env.js /config/prod.env.js 其实,这两个文件就是针对生产环境和发布环境设置不同参数的文 ...
- WebApiClient库支持AOT
1 库简介 WebApiClient是开源在github上的一个http客户端库,内部基于HttpClient开发,只需要定义c#接口(interface),并打上相关特性,即可异步调用http-ap ...
- Redis 5种主要数据类型和命令
redis是键值对的数据库,有5中主要数据类型: 字符串类型(string),散列类型(hash),列表类型(list),集合类型(set),有序集合类型(zset) 几个基本的命令: KEYS * ...
- InnoDB存储引擎结构介绍
Ⅰ.InnoDB发展史 时间 事件 备注 1995 由Heikki Tuuri创建Innobase Oy公司,开发InnoDB存储引擎 Innobase开始做的是数据库,希望卖掉该公司 1996 My ...
- css布局-双飞翼布局
<div class="header">Header</div> <div class="bd"> <div clas ...
- 如何将网页保存为PDF文件
怎样将网页保存为PDF文件... 问题: 很多时候我们需要将网页上的内容,在排版不变的情况下完整的保存下来,那么用pdf格式是最好的效果了,还图文并茂,效果与真实的网页很相似,如果另存为网页的话,会下 ...
- 用secureCRT连接虚拟机中的Ubuntu系统,出现“远程主机拒绝连接”错误
因为我的Ubuntu中未安装ssh服务,终端下运行命令: sudo apt-get install openssh-server 之后重启一下sshd服务: sudo service sshd res ...
- java 中 一个int类型的num,num&1
n&1 把n与1按位与,因为1除了最低位,其他位都为0,所以按位与结果取决于n最后一位,如果n最后一位是1,则结果为1.反之结果为0.(n&1)==1: 判断n最后一位是不是1(可能用 ...
- uuid.go
package uuid import "time" func GenerateUUID() uint64 { return uint64(time.Now().UnixN ...