前言
在之前的课程中,我们使用到
v-text
、v-html
、v-bind
这三个 Vue 指令,接下来我们继续学习其他的指令,其中条件渲染(v-if
)和列表渲染(v-for
)是最需要掌握的。
Vue 指令
指令 (Directives) 是带有 v- 前缀的特殊属性。指令属性的值预期是单个 JavaScript 表达式 (v-for 是例外情况)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
v-text
预期接收: String
说明: 更新元素的
textContent
。如果要更新部分的textContent
,需要使用{{ Mustache }}
插值。
示例:
<span v-text="msg"></span> <!-- 和下面的一样 --> <span>{{msg}}</span>
v-html
预期接收: String
说明: 更新元素的
innerHTML
。
注意:
- 内容按普通 HTML 插入,不会作为 Vue 模板进行编译。
- 在单文件组件里,
scoped
的样式不会应用在v-html
内部,因为那部分 HTML 没有被 Vue 的模板编译器处理。- 在网站上动态渲染任意 HTML 是非常危险的,因为容易导致 XSS 攻击。只在可信内容上使用
v-html
,永不用在用户提交的内容上。
示例:
<div v-html="html"></div>
v-show
预期接收: any
说明: 根据表达式之真假值,切换元素的
display
CSS property,当条件变化时该指令触发过渡效果。我们将在条件渲染小节和v-if
进行对比演示。
示例:
<h1 v-show="ok">Hello!</h1>
v-once
不需要表达式
说明: 只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
示例:
<!-- 单个元素 --> <span v-once>This will never change: {{msg}}</span> <!-- 有子元素 --> <div v-once> <h1>comment</h1> <p>{{msg}}</p> </div> <!-- 组件 --> <my-component v-once :comment="msg"></my-component> <!-- `v-for` 指令--> <ul> <li v-for="i in list" v-once>{{i}}</li> </ul>
v-pre
不需要表达式
说明: 跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
示例:
<span v-pre>{{ this will not be compiled }}</span>
v-cloak
不需要表达式
说明: 这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如
[v-cloak] { display: none }
一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。
示例:
[v-cloak] { display: none; }
<div v-cloak> {{ message }} </div>
v-bind
预期接收: any (带参数) | Object (不带参数)
说明: 动态地绑定一个或多个 attribute 或一个组件 prop,支持进行缩写
:
。没有参数时,可以绑定到一个包含键值对的对象。关于绑定style
和class
比较特殊,我们将在样式绑定章节进行讲解。
示例:
<!-- 绑定一个 attribute --> <img v-bind:src="imageSrc"> <!-- 缩写 --> <img :src="imageSrc"> <!-- 动态 attribute 名 (2.6.0+) --> <button v-bind:[key]="value"></button> <!-- 绑定一个全是 attribute 的对象 --> <div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
v-on
预期接收: Function | 内联语句 | Object
说明: 绑定事件监听器,事件类型由参数指定,支持缩写
@
。表达式可以是一个方法的名字或一个内联语句,如果没有修饰符也可以省略。用在普通元素上时,只能监听原生 DOM 事件。用在自定义元素组件上时,也可以监听子组件触发的自定义事件。在监听原生 DOM 事件时,方法以事件为唯一的参数。如果使用内联语句,语句可以访问一个$event
property:v-on:click="handle('ok', $event)"
。具体的使用演示,会在事件处理和组件基础章节中进行。
示例:
<!-- 方法处理器 --> <button v-on:click="doThis"></button> <!-- 缩写 --> <button @click="doThis"></button> <!-- 动态事件 (2.6.0+) --> <button v-on:[event]="doThis"></button> <!-- 内联语句 --> <button v-on:click="doThat('hello', $event)"></button> <!-- 对象语法 (2.4.0+) --> <button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
v-model
预期接收: 随表单控件类型不同而不同。
说明: 在表单控件或者组件上创建双向绑定,我们将在表单输入绑定章节进行详细讲解。
示例:
<input v-model="message" placeholder="edit me"> <p>Message is: {{ message }}</p>
v-solt
说明: 我们将在深入了解组件章节进行详细讲解。
条件渲染
v-if
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。
<h1 v-if="awesome">Vue is awesome!</h1>
你可以使用
v-else
指令来表示v-if
的“else 块”:
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
v-else
元素必须紧跟在带v-if
或者v-else-if
的元素的后面,否则它将不会被识别。v-else-if
,顾名思义,充当v-if
的“else-if 块”,可以连续使用:
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
因为
v-if
是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个<template>
元素当做不可见的包裹元素,并在上面使用v-if
。最终的渲染结果将不包含<template>
元素。
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
v-if 搭配 key 的使用
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使 Vue 变得非常快之外,还有其它一些好处。例如,如果你允许用户在不同的登录方式之间切换:
<template v-if="loginType === 'username'">
<label>Username:</label>
<input placeholder="Enter your username">
</template>
<template v-else>
<label>Email:</label>
<input placeholder="Enter your email address">
</template>
那么在上面的代码中切换
loginType
将不会清除用户已经输入的内容。因为两个模板使用了相同的元素,<input>
不会被替换掉——仅仅是替换了它的placeholder
。
这样也不总是符合实际需求,所以 Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的
key
attribute 即可:
<template v-if="loginType === 'username'">
<label>Username:</label>
<input placeholder="Enter your username" key="username-inp">
</template>
<template v-else>
<label>Email:</label>
<input placeholder="Enter your email address" key="email-inp">
</template>
现在,每次切换时,输入框都将被重新渲染。请看:
这里的演示用到了 chrome 浏览器插件
Vue.js devtools
进行调试,可前往 https://github.com/vuejs/devtools 自行安装或网上查找相关教程。
v-if 和 v-show 的区别
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,
v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换:
<div v-if="show">v-if</div>
<div v-show="show">v-show</div>
一般来说,
v-if
有更高的切换开销,而v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用v-show
较好;如果在运行时条件很少改变,则使用v-if
较好。另外需要注意的是,v-show
不支持<template>
元素,也不支持v-else
。
列表渲染
v-for 使用数组
我们可以用
v-for
指令基于一个数组来渲染一个列表。v-for
指令需要使用item in items
形式的特殊语法,其中items
是源数据数组,而item
则是被迭代的数组元素的别名。你也可以用of
替代in
作为分隔符,因为它更接近 JavaScript 迭代器的语法。
<li v-for="student in students" :key="student.name">{{ student.name }} {{ student.gender }}</li>
// <script>块
export default {
data(){
return {
students: [{
name: '小明',
gender: '男'
},{
name: '小红',
gender: '女'
}]
}
}
}
渲染结果:
v-for
还支持一个可选的第二个参数,即当前项的索引:
<li v-for="(label, index) in labels" :key="label">
<label>{{ index + 1 }}、{{ label }}:</label>
<input type="text">
</li>
// <script>块
export default {
data(){
return {
labels: ['姓名', '性别']
}
}
}
渲染结果:
当 Vue 正在更新使用
v-for
渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一key
attribute。下面进行加key
和不加key
的对比。
以上面的代码为例,指定
key
的情况下进行删除元素:
现在我们去掉
key
attribute,这时编译的时候会出现语法报错,因为之前我们开发依赖装有eslint
进行语法检测。
我们只需在<template>
块顶部加上eslint-disable
的注释,便可以跳过 eslint 对代码块的检测。
<template>
<!-- eslint-disable -->
<div id="app">
<li v-for="(label, index) in labels">
<label>{{ index + 1 }}、{{ label }}:</label>
<input type="text">
</li>
</div>
</template>
现在,我们来看一下没有指定
key
进行删除的情况:
所以尽可能在使用
v-for
时提供key
attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
v-for 使用对象
你也可以用
v-for
来遍历一个对象的 property。
<li v-for="value in object">
{{ value }}
</li>
// <script>块
export default {
data(){
return {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
}
}
渲染结果:
你也可以提供第二个的参数为 property 名称 (也就是键名):
<div v-for="(value, name) in object">
{{ name }}: {{ value }}
</div>
还可以用第三个参数作为索引:
<div v-for="(value, name, index) in object">
{{ index }}. {{ name }}: {{ value }}
</div>
v-for 使用值范围
v-for
也可以接受整数。在这种情况下,它会把模板重复对应次数。
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
渲染结果:
v-for 和 v-if 一起使用
注意,我们不推荐在同一元素上使用
v-if
和v-for
。当它们处于同一节点,v-for
的优先级比v-if
更高,这意味着v-if
将分别重复运行于每个v-for
循环中。当你只想为部分项渲染节点时,推荐采用下一章节会学习的计算属性。而如果你的目的是有条件地跳过循环的执行,那么可以将v-if
置于外层元素 (或<template>
) 上。如:
<ul v-if="todos.length">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
<p v-else>No todos left!</p>
内容预告
本章我们介绍了 Vue 中的指令,其中
v-if
和v-for
是最常用的指令。下一章节,我们会学习 Vue 中的事件处理、计算属性、监听属性以及实例的生命周期等。