操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是 attribute,所以我们可以用 v-bind
处理它们:只需要通过表达式计算出字符串结果即可。
不过,字符串拼接麻烦且易错。因此,在将 v-bind
用于 class
和 style
时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。
(1)我们可以传给 v-bind:class
一个对象,以动态地切换 class:
<div v-bind:class="{ active: isActive }">div>
上面的语法表示 active
这个 class 存在与否将取决于数据 property isActive
的 truthiness(真值)。
(2)你可以在对象中 传入更多字段 来动态切换多个 class。此外,v-bind:class
指令也可以与普通的 class attribute 共存。当有如下模板:
<div
class="static"
:class="{ active: isActive, 'text-danger': hasError }"
>div>
和如下 data:
data: {
isActive: true,
hasError: false
}
结果渲染为:
// 动态切换多个class
<div class="static active">div>
(3)当 isActive
或者 hasError
变化时,class 列表将相应地更新。例如,如果 hasError
的值为 true
,class 列表将变为 "static active text-danger"
。
// 动态选择绑定class
<div class="static active text-danger">div>
————
(4)绑定的数据对象不必内联定义在模板里:
【绑定class样式----对象写法】,适用于:【要绑定的样式个数确定、名字也确定,但要动态决定用不用
】
<div :class="classObject">div>
data: {
classObject: {
active: true,
'text-danger': false
}
}
渲染的结果和上面一样。
————
(5)我们也可以在这里 绑定一个返回对象的计算属性!。这是一个常用且强大的模式:
<div :class="classObject">div>
data: {
isActive: true,
error: null
},
computed: {
classObject() {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal'
} // 这里是在 通过isActive error来判断返不返回 class 的属性
}
}
(1)我们可以把一个数组传给 v-bind:class
,以应用一个 class 列表:
<div :class="[activeClass, errorClass]">div>
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
渲染为:
<div class="active text-danger"></div>
————
(2)如果你也想根据条件切换列表中的 class,可以用三元表达式:
<div :class="[isActive ? activeClass : '', errorClass]">div>
这样写将始终添加 errorClass
,但是只有在isActive
是truthy
[1] 时才添加 activeClass
。
————
(3)不过,当有多个条件 class 时这样写有些繁琐。所以在 数组语法 中也可以使用 对象语法:
<div v-bind:class="[{ active: isActive }, errorClass]">div>
————
(4)基础的样式 class="basic"
用基础写法,变化的样式绑定写,既然是动态的样式,当然就不能写死,就应该写成动态的 :class="classArr"
【绑定class样式----数组写法】,适用于【要绑定的样式个数不确定、名字也不确定
】,因为可以动态添加 this.classArr.push()
<div class="basic" :class="classArr">{{name}}div>
data:{
classArr:['atguigu1','atguigu2','atguigu3'],
}
当在一个自定义组件上使用 class
property 时,这些 class 将被添加到该组件的根元素上面。这个元素上已经存在的 class 不会被覆盖。
例如,如果你声明了这个组件:
Vue.component('my-component', {
template: '
'})
然后在使用它的时候添加一些 class:
<my-component class="baz boo">my-component>
HTML 将被渲染为:
<p class="foo bar baz boo">Hip>
————
对于带数据绑定 class 也同样适用:
<my-component :class="{ active: isActive }">my-component>
当 isActive
为 truthy[1] 时,HTML 将被渲染成为:
<p class="foo bar active">Hip>
v-bind:style
的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。
CSS property 名可以用:
// activeColor、fontSize 是vm/vc中的属性,可以直接使用,不用写this.activeColor
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }">div>
data: {
activeColor: 'red',
fontSize: 30
}
————
(2)直接绑定到一个样式对象通常更好,这会让模板更清晰:
<div :style="styleObject">div>
data: {
styleObject: {
// 这里面是对象写法,属性值就要加引号
color: 'red',
fontSize: '13px'
}
}
————
(3)数组写法,用的很少。
<div class="basic" :style="[styleObj1, styleObj2]">testdiv>
<div class="basic" :style="styleArr">{{name}}div>
data:{
styleArr:[
{
fontSize: '40px',
color:'blue',
},
{
backgroundColor:'gray'
}
]
}
————
这里面的属性名都不能瞎写,这是CSS固有的属性!
————
同样的,对象语法常常结合返回对象的计算属性使用。
v-bind:style
的数组语法可以将多个样式对象应用到同一个元素上:
<div v-bind:style="[baseStyles, overridingStyles]">div>
当 v-bind:style
使用需要添加浏览器引擎前缀的 CSS property 时,如 transform
,Vue.js 会自动侦测并添加相应的前缀。
2.3.0+
从 2.3.0 起你可以为 style
绑定中的 property 提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }">div>
这样写 只会渲染数组中最后一个被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex
。
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。
<h1 v-if="awesome">Vue is awesome!h1>
<h1 v-else>Oh no h1>
元素上使用 v-if 条件渲染分组因为 v-if 是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个
元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 元素。
<template v-if="ok">
<h1>Titleh1>
<p>Paragraph 1p>
<p>Paragraph 2p>
template>
你可以使用 v-else
指令来表示 v-if
的“else 块”:
<div v-if="Math.random() > 0.5">
Now you see me
</div>
<div v-else>
Now you don't
</div>
v-else
元素必须紧跟在带 v-if
或者 v-else-if
的元素的后面,否则它将不会被识别。
2.1.0 新增
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-else
,v-else-if
也必须紧跟在带 v-if
或者 v-else-if
的元素之后。
Vue 会尽可能高效地渲染元素,通常会 复用已有元素 而不是从头开始渲染。
这么做除了使 Vue 变得非常快之外,还有其它一些好处。例如,如果你允许用户在不同的登录方式之间切换:
<template v-if="loginType === 'username'">
<label>Usernamelabel>
<input placeholder="Enter your username">
template>
<template v-else>
<label>Emaillabel>
<input placeholder="Enter your email address">
template>
那么在上面的代码中切换 loginType
将不会清除用户已经输入的内容。因为两个模板使用了相同的元素, 不会被替换掉——仅仅是替换了它的
placeholder
。
https://v2.cn.vuejs.org/v2/guide/conditional.html#v-else-if
这样也不总是符合实际需求,所以 Vue 为你提供了一种方式来表达 “这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key
attribute 即可:
<template v-if="loginType === 'username'">
<label>Usernamelabel>
<input placeholder="Enter your username" key="username-input">
template>
<template v-else>
<label>Emaillabel>
<input placeholder="Enter your email address" key="email-input">
template>
现在,每次切换时,输入框都将被重新渲染。
另一个用于根据条件展示元素的选项是 v-show 指令。用法大致一样:
<h1 v-show="ok">Hello!h1>
不同的是带有 v-show
的元素 始终会被渲染并保留在 DOM 中。v-show
只是简单地切换元素的 CSS property display
。
注意,
v-show
不支持元素,也不支持
v-else
。
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的 事件监听器 和 子组件 适当地 被销毁和重建。
v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。 (不展示的DOM元素直接被销毁。
)
相比之下,v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。(不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
)
一般来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
不推荐
v-if 与 v-for 一起使用
我们可以用 v-for
指令基于一个数组来渲染一个列表。v-for
指令需要使用 item in items
形式的特殊语法,其中items
是源数据数组,而item
则是被迭代的数组元素的别名。
<ul id="example-1">
<li v-for="item in items" :key="item.message">
{{ item.message }}
li>
ul>
var example1 = new Vue({
el: '#example-1',
data: {
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
————
在 v-for
块中,我们可以访问 所有父作用域的 property。v-for
还支持一个可选的第二个参数,即当前项的索引。
<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
li>
ul>
var example2 = new Vue({
el: '#example-2',
data: {
parentMessage: 'Parent',
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
结果:
————
你也可以用 of
替代 in
作为分隔符,因为它更接近 JavaScript 迭代器的语法:
<div v-for="item of items">div>
你也可以用 v-for 来遍历一个对象的 property。
<ul id="v-for-object" class="demo">
<li v-for="value in object">
{{ value }}
li>
ul>
new Vue({
el: '#v-for-object',
data: {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
})
你也可以提供第二个的参数为 property 名称 (也就是键名):
<div v-for="(value, key) in object">
{{ name }}: {{ value }}
div>
还可以用第三个参数作为索引:
<div v-for="(value, key, index) in object">
{{ index }}. {{ key}}: {{ value }}
div>
在遍历对象时,会按
Object.keys()
的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下都一致。
当 Vue 正在更新使用 v-for
渲染的元素列表时,它默认使用 “就地更新” 的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。
这个默认的模式是高效的,但是 只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
为了 给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key
attribute:
<div v-for="item in items" :key="item.id">
div>
建议尽可能在使用 v-for
时提供 key
attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
因为它是 Vue 识别节点的一个通用机制,key
并不仅与 v-for 特别关联。后面我们将在指南中看到,它还具有其它用途。
8.4 数组更新检测
– 变更方法: push()、pop()、shift()、unshift()、sort()、reverse()
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也 将会触发视图更新。这些被包裹过的方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
你可以打开控制台,然后对前面例子的 items
数组尝试调用变更方法。比如 example1.items.push({ message: 'Baz' })
。
– 替换数组: filter()、concat()、slice()
变更方法,顾名思义,会变更调用了这些方法的原始数组。相比之下,也有非变更方法,例如 filter()
、concat()
和 slice()
。它们不会变更原始数组,而总是返回一个新数组。当使用非变更方法时,可以用新数组替换旧数组:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
你可能认为这将导致 Vue 丢弃现有 DOM 并重新渲染整个列表。幸运的是,事实并非如此。Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的启发式方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。
– 注意事项:Vue 不能检测数组和对象的变化
由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。深入响应式原理中有相关的讨论。
8.5 显示过滤/排序后的结果
---- 一维数组过滤
有时,我们想要显示一个数组经过过滤或排序后的版本,而不实际变更或重置原始数据。在这种情况下,可以创建一个 计算属性,来返回过滤或排序后的数组。
例如:
<li v-for="n in evenNumbers">{{ n }}li>
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
evenNumbers() {
return this.numbers.filter((number) => {
return number % 2 === 0
})
}
}
————
例子2:
<div id="root">
<h2>人员列表h2>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<ul>
<li v-for="(value, index) of filPerons" :key="index">
{{value.name}}-{{value.age}}-{{value.sex}}
li>
ul>
div>
new Vue({
el:'#root',
data:{
keyWord:'',
persons:[
{id:'001',name:'马冬梅',age:19,sex:'女'},
{id:'002',name:'周冬雨',age:20,sex:'女'},
{id:'003',name:'周杰伦',age:21,sex:'男'},
{id:'004',name:'温兆伦',age:22,sex:'男'}
]
},
computed:{
filPerons(){
return this.persons.filter((p)=>{
return p.name.indexOf(this.keyWord) !== -1
})
}
}
})
————
例子3:
<div id="root">
<h2>人员列表h2>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<button @click="sortType = 2">年龄升序button>
<button @click="sortType = 1">年龄降序button>
<button @click="sortType = 0">原顺序button>
<ul>
<li v-for="(value, index) of filPerons" :key="value.id">
{{value.name}}-{{value.age}}-{{value.sex}}
<input type="text">
li>
ul>
div>
new Vue({
el:'#root',
data:{
keyWord:'',
sortType: 0, //0 原顺序 1降序 2升序
persons:[
{id:'001',name:'马冬梅',age:30,sex:'女'},
{id:'002',name:'周冬雨',age:31,sex:'女'},
{id:'003',name:'周杰伦',age:18,sex:'男'},
{id:'004',name:'温兆伦',age:19,sex:'男'}
]
},
computed:{
filPerons(){
const arr = this.persons.filter((p)=>{
return p.name.indexOf(this.keyWord) !== -1
})
// 先筛选,后排序
if(this.sortType){
arr.sort((p1, p2)=>{
return this.sortType === 1 ? p2.age-p1.age : p1.age-p2.age
})
}
return arr
}
}
})
---- 二维数组过滤
在计算属性不适用的情况下 (例如,在嵌套 v-for
循环中) 你可以使用一个 方法:
<ul v-for="set in sets">
<li v-for="n in even(set)">{{ n }}li>
ul>
data: {
sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]]
},
methods: {
even(numbers) {
return numbers.filter((number) => {
return number % 2 === 0
})
}
}
8.6 在 v-for 里使用范围值
v-for 也可以接受整数。在这种情况下,它会把模板重复对应次数。
<div>
<span v-for="n in 10">{{ n }} span>
div>
结果:1 2 3 4 5 6 7 8 9 10
8.7 在
上使用 v-for
类似于 v-if
,你也可以利用带有 v-for
的
来循环渲染一段包含多个元素的内容。比如:
<ul>
<template v-for="item in items">
<li>{{ item.msg }}li>
<li class="divider" role="presentation">li>
template>
ul>
8.8 v-for 与 v-if 一同使用
注意我们不推荐在同一元素上使用 v-if
和 v-for
。更多细节可查阅风格指南。
---- 为部分项渲染节点时,十分有用
当它们处于同一节点,v-for
的优先级比 v-if
更高,这意味着 v-if
将分别重复运行于每个 v-for
循环中。
当你 只想为部分项渲染节点时,这种优先级的机制会十分有用,如下:
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
li>
上面的代码将只渲染未完成的 todo。
---- 有条件地跳过循环的执行
而如果你的目的是 有条件地跳过循环的执行,那么可以将 v-if
置于外层元素 (或
) 上。如:
<ul v-if="todos.length">
<li v-for="todo in todos">
{{ todo }}
li>
ul>
<p v-else>No todos left!p>
8.9 在组件上使用 v-for
在自定义组件上,你可以像在任何普通元素上一样使用 v-for
<my-component v-for="item in items" :key="item.id">my-component>
2.2.0+ 的版本里,当在组件上使用 v-for
时,key
现在是必须的。
然而,任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要使用 prop:
<my-component
v-for="(item, index) in items"
:item="item"
:index="index"
:key="item.id"
>my-component>
不自动将 item
注入到组件里的原因是,这会使得组件与 v-for 的运作 紧密耦合。明确组件数据的来源,能够使组件在其他场合重复使用。
————
下面是一个简单的 todo 列表的完整例子:
<div id="todo-list-example">
<form @submit.prevent="addNewTodo">
<label for="new-todo">Add a todolabel>
<input
v-model="newTodoText"
id="new-todo"
placeholder="E.g. Feed the cat"
>
<button>Addbutton>
form>
<ul>
<li
is="todo-item"
v-for="(todo, index) in todos"
:key="todo.id"
:title="todo.title"
@remove="todos.splice(index, 1)"
>li>
ul>
div>
注意这里的 is="todo-item" attribute
。这种做法在使用 DOM 模板时是十分必要的,因为在
元素内只有
元素会被看作有效内容。
这样做实现的效果与
相同,但是可以避开一些潜在的浏览器解析错误。查看 [ DOM 模板解析说明 ] 来了解更多信息。
Vue.component('todo-item', {
template: '\
- \
{{ title }}\
\
\
',
props: ['title']
})
new Vue({
el: '#todo-list-example',
data: {
newTodoText: '',
todos: [
{
id: 1,
title: 'Do the dishes',
},
{
id: 2,
title: 'Take out the trash',
},
{
id: 3,
title: 'Mow the lawn'
}
],
nextTodoId: 4
},
methods: {
addNewTodo() {
this.todos.push({
id: this.nextTodoId++,
title: this.newTodoText
})
this.newTodoText = ''
}
}
})
END