概述
指令是带有v-前缀的特殊属性,其值限定为单个表达式。指令的作用是,当表达式的值发生改变时,将其产生的连带影响应用到DOM上。
此外,一些指令还可以带有参数,在指令名称之后以冒号表示。从Vue.js 2.6.0版本开始,指令的参数可以是动态参数,语法为指令:[JavaScript表达式]。
内置指令
Vue.js针对一些常用的页面功能提供了以指令来封装的使用形式,以HTML元素属性的方式使用,这些指令数量并不是很多。
v-show
v-show指令根据表达式的值的真假,来显示或隐藏HTML元素。
代码示例如下
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>v-show 指令</title>
</head>
<body>
<div id="app">
<h1 v-show="yes">Yes!</h1>
<h1 v-show="no">No!</h1>
<h1 v-show="age <= 25">Age: {{age}}</h1>
<h1 v-show="name.indexOf('John') >= 0">Name: {{name}}</h1>
</div>
<script src="vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
yes: true,
no: false,
age: 18,
name: 'Will John'
}
})
</script>
</body>
</html>
我们用Chrome浏览器打开上述页面,使用开发者工具展开div元素,结果如下图所示:
从上图中可以看到,因为数据对象中的no属性为false,因此使用v-show指令计算no表达式的<h1>元素没有显示;其他表达式的值计算都为true,所以这些表达式所在的<h1>元素都正常显示了。
从Chrome浏览器的Elements窗口中可以看到,使用v-show指令,元素本身是要被渲染的,至于显示与否是通过设置css样式属性display来控制的,如果表达式的值计算为false,则设置样式"display: none"。接下来切换到Console窗口,修改age属性的值为28(vm.age = 28),然后切换回Elements窗口,如下图所示:
由此可以进一步确认v-show指令是通过css样式属性displa来控制元素的显示与否。
指令都是在某个元素上使用,如果要显示或隐藏多个元素,其实并不需要在每个元素上都使用v-show指令。我们可以使用HTML 5新增的<template>元素来包裹需要切换显示与隐藏的多个元素,然后在<template>元素上使用v-show指令,最终的渲染结果中是不会包含<template>元素的。实际上,<template>元素是被当作一个不可见的包裹元素,主要用于分组的条件判断和列表渲染。
使用<template>标签实现多个元素显示隐藏
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>v-show 指令</title>
</head>
<body>
<div id="app">
<template v-show="!isLogin">
<form>
<p>username: <input type="text"></p>
<p>password: <input type="password"></p>
</form>
</template>
</div>
<script src="vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
isLogin: false
}
})
</script>
</body>
</html>
渲染结果如图所示:
v-if / v-else-if / v-else
v-if、v-else-if、v-else这三个指令用于实现条件判断。
1. v-if
v-if指令根据表达式的值的真假来生成或删除一个元素。
代码示例如下
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>v-if 指令</title>
</head>
<body>
<div id="app">
<h1 v-if="yes">Yes!</h1>
<h1 v-if="no">No!</h1>
<h1 v-if="age <= 25">Age: {{age}}</h1>
<h1 v-if="name.indexOf('John') >= 0">Name: {{name}}</h1>
</div>
<script src="vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
yes: true,
no: false,
age: 18,
name: 'Will John'
}
})
</script>
</body>
</html>
使用Chrome浏览器打开页面,如下图所示:
从上图可以看到,包含no表达式(值为false)的<h1>元素并没有生成,其他表达式的值为true的<h1>元素正常生成了。也就是说,v-if指令在HTML元素的显示与否的实现机制上与v-show指令不同,当表达式的值计算为false时,v-if指令不会创建该元素,只有当表达式的值为true时,v-if指令才会真正创建该元素;而v-show指令不管表达式的值是真是假,元素本身都会被创建。
切换到Console窗口,修改age属性的值为28(vm.age = 28),然后切换回Elements窗口,如下图所示:
与v-show指令一样,如果v-if需要控制多个元素的创建或删除,可以用<template>元素来包裹这些元素,然后在<template>元素上使用v-if指令。
一般来说,v-if有更高的切换开销,而v-show有更高的出事渲染开销。因此,如果需要非常频繁地切换元素的显示或隐藏,则使用v-show较好;如果在运行时条件很少改变,则使用v-if较好。
2. v-else-if / v-else
v-else-if指令是在Vue.js 2.1.0版本中新增的,与v-if一起使用,可以实现互斥的条件判断。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<span v-if="score >= 85">优秀</span>
<span v-else-if="score >= 75">良好</span>
<span v-else-if="score >= 60">及格</span>
<span v-else>不及格</span>
</div>
<script src="vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
score: 90
}
})
</script>
</body>
</html>
当一个条件满足时,后续的条件都不会再判断。
- 使用时,v-else-if和v-else要紧跟在v-if或v-else-if之后
3. 用key管理可复用的元素
Vue会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染,这么做使Vue渲染效率变得非常高。
不使用key的情况
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>v-if-key</title>
</head>
<body>
<div id="app">
<template v-if="loginType === 'username'">
<label>用户名:</label>
<input placeholder="请输入你的用户名">
</template>
<template v-else>
<label>Email:</label>
<input placeholder="请输入你的Email">
</template>
<p><button v-on:click="changeLoginType">切换登陆方式</button></p>
</div>
<script src="vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
loginType: 'username'
},
methods: {
changeLoginType() {
if (this.loginType === 'username') {
this.loginType = "email"
} else {
this.loginType = "username"
}
}
}
})
</script>
</body>
</html>
使用浏览器打开上述页面,初始显示的是用户名输入框,可以在其中任意输入些内容,在点击“切换登陆方式”按钮后,之前输入的内容会被保留下来。如下图所示:
这是因为在两个模版中使用了相同的<input>元素,Vue为了提高渲染效率,复用了<input>元素,因此,在切换登陆时,<input>不会被替换掉,仅仅是替换了它的placeholder属性。
但有时这并不是我们想要的,我们不希望在Email输入框中看到之前输入的用户名,这时我们可以通过为<input>元素添加一个具有唯一值的key属性,来取消复用。
修改上述代码,为<input>标签添加key属性
<template v-if="loginType === 'username'">
<label>用户名:</label>
<input placeholder="请输入你的用户名" key="username-input">
</template>
<template v-else>
<label>Email:</label>
<input placeholder="请输入你的Email" key="email-input">
</template>
使用浏览器再次打开该页面,可以发现切换时,输入框会被重新渲染。
v-for
顾名思义,v-for指令就是通过循环的方式来渲染一个列表,循环的对象可以是数组,也可以是一个JavaScript对象。
1. v-for遍历数组
表达式的语法形式为item in times,其中items是源数据数组,而item则是被迭代的数组元素的别名
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<ul>
<li v-for="book in books">{{book.title}}</li>
</ul>
</div>
<script src="vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
books: [
{title: 'Book1'},
{title: 'Book2'},
{title: 'Book3'}
]
}
})
</script>
</body>
</html>
Vue实例的数据对象中定义了一个数组属性books,然后在<li>元素上使用v-for指令遍历该数组,这将循环渲染<li>元素。在v-for块中,可以访问所有父作用域的属性,book是数组中元素的别名,每次循环,book的值都被重置为数组当前索引的值,在<li>元素内部,可以通过Mustache语法来引用该变量值。
最终渲染结果如下图:
- v-for指令的表达式也可以使用of替代in作为分隔符,它更接近JavaScript迭代器的语法,如<div v-for="item of items"></div>
v-for指令的表达式还支持一个可选的参数作为当前项的索引。
修改代码中的<li>元素
<li v-for="(book, index) in books">{{index}} - {{book.title}}</li>
多个参数需要放到圆括号中,最后的渲染结果如下图所示:
2. 数组更新检测
Vue的核心是数据与视图的双向绑定,为了检测数组中元素的变化,以便能及时将变化反映到视图中,Vue对数组的下列变异方法(mutation method)进行了包裹。
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
我们可以直接使用Chrome浏览器的Console窗口输入下列语句
vm.books.push({title: 'Book4'})
数组中还有一些非变异方法(non-mutating method),如filter()、concat()和slice(),它们不会改变原始数组,而总是返回一个新数组。对于这些方法,要想让Vue帮我们自动更新视图,可以使用新数组来替换原来的数组。
依然是上述代码,在浏览器控制台输入下面的语句
vm.books = vm.books.concat([{title: 'Book5'}, {title: 'Book6'}])
Vue在检测到数组变化时,并不是直接重新渲染整个列表,而是最大化地复用DOM元素,替换的数组中,含有相同元素的项不会被重新渲染,因此可以大胆地使用新数组来替换旧数组,不用担心性能问题。
- 要注意的是,通过下述方法引起的数组变动,Vue不能检测到
// 通过索引直接设置数组值
vm.books[0] = {title: 'Book'}
// 修改数组的长度
vm.books.length = 1
要解决上述第一类问题,可以采用下面两种方式,这两种方式都可以实现vm.books[0] = {...}相同的效果
// 使用Vue的全局set()方法
Vue.set(vm.books, 0, {title: 'BookA'})
// 使用数组原型的splice()方法
vm.books.splice(0, 1, {title: 'BookB'})
作为替代,也可以使用Vue实例的$set方法,该方法是全局方法Vue.set的一个别名
// 使用Vue实例的$set()方法
vm.$set(vm.books, 0, {title: 'BookC'})
前述第二类问题也是使用数组原型的splice()方法来解决的,代码如下
vm.books.splice(1)
由于篇幅有限,本篇文章就先介绍到这里,v-for的更多用法以及其余指令将在下篇文章继续介绍。后续更多内容请参考“Vue学习——指令(二)”