安装
介绍
Vue实例
模板语法
插值
文本
插值语法又叫mustach语法
原始HTML
属性值的绑定
使用JS表达式
指令
参数
动态参数
修饰符
缩写
v-bind缩写
v-on缩写
计算属性和侦听器
计算属性
提示 为什么需要计算属性,计算属性是什么,如何使用计算属性
基础例子
计算属性缓存VS方法
计算属性和方法的区别,使用场景
计算属性VS侦听器
计算属性和侦听器的区别,使用场景
计算属性的setter
seeter的使用,使用场景
侦听器
为什么要有侦听器
Class 与 Style 绑定
因为class和style是标签属性,标签的属性的绑定可以使用v-bind指令,指令的值是单个表达式,单个表达式计算的值是字符串即可。但是使用字符串的拼接麻烦而且容易错,所以v-bind用在style和class上时,vue做了增强,表达式结果的类型可以是字符串、数组和对象。v-bind:class可以和普通的class属性共存。即定义了v-bind:class也可以添加class的属性
绑定HTML Class
对象语法
对象的键是class的名称,对象的值会转换成Boolean格式,如果值为true的话,则当前的键名在编译之后会展示在class名中。否则就不展示在class类名中。
对象可以写在v-bind:class后面,也可以写在data中,在v-bind:class中引入data中定义的对象。也可以写在computed中,在v-bind:class中引入computed这个对象。
数组语法
数组中的每一个数组元素都是一个JS单个表达式。
// 数组中的元素是data中定义的可以进行双向数据绑定的对象
<div v-bind:class="[activeClass, errorClass]"></div>
// 数组中的元素可以是三元表达式
<div v-bind:class="[activeClass ? 'test' : '', errorClass]"></div>
数组中也可以使用对象语法
<div :class="[{active: isActive}, errorClass]"></div>
// active是类名,isActive是data中的定义的进行双向数据绑定的数据
用在组件上
在封装的组件上可以使用class也可以使用vue绑定的class,最后封装组件的class都会被添加到根元素上。
Vue.component('my-component', {
template: '<p class="foo bar">Hi !!</p>'
})
<my-component class="test1" :class="'aaa'"></my-component>
// 渲染的结果
/*
<p class="foo bar test1 aaa">Hi !!</p>
*/
绑定内联样式
对象语法
对象的键是style的css的属性的名称,CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,需要用引号括起来) 来命名
<div v-bind:style="{fontSize:fontSize + 'px', color: activeColor }"></div>
data: {
activeColor: 'red',
fontSize: 30
}
直接绑定到一个样式对象通常更好,这会让模板更清晰:
<div v-bind:style="styleObject"></div>
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
对象的语法常常会结合返回对象的计算属性使用
数组语法
数组元素是单个JS表达式v-bind:style
的数组语法可以将多个样式对象应用到同一个元素上
<div v-bind:style="[baseStyles, overridingStyles]"></div>
data: {
baseStyles: {
color: 'green',
fontSize: '30px'
},
overridingStyles: {
'font-weight': 'bold'
}
}
自动添加前缀
当 v-bind:style 使用需要添加浏览器引擎前缀的 CSS property 时,如 transform,Vue.js 会自动侦测并添加相应的前缀。
多重值
从 2.3.0 起你可以为 style 绑定中的 property 提供一个包含多个值的数组,常用于提供多个带前缀的值或者兼容不同浏览器的值,例如:
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
可以将style属性的值设置为一个数组,数组中放多个带有前缀的属性的值
条件渲染
v-if
v-if指令用于条件性的渲染一块内容,这块内容只会在指令表达式返回truthy(即boolean值为true)的时候被渲染,也可以使用v-else来添加一个前面条件不成立的的情况下渲染的模块
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
data: {
awesome: true,
}
在元素上使用v-if条件渲染分组因为v-if是一个指令,所以必须要添加到一个元素上。使用元素的话,会在页面中出现冗余的元素。这个时候可以使用,元素可以当做不可见的包裹元素,并在上面使用相应指令。使用了元素之后,最终的渲染结果是不包含元素。不能在中添加class属性等,因为不会生效
<template v-if="test">
<div>test is good</div>
</template>
data: {
test: true,
}
v-else
可以使用v-else指令来表示前面条件不成立之后需要展示的模块
v-else指令必须紧跟在带有v-if指令或者v-else-if指令元素的后面,否则v-else指令将不被识别
<div v-if="Math.random() > 0.5">
Now you see me
</div>
<div v-else>
Now you don't
</div>
v-else-if
在2.1.0 新增 v-else-if 指令。
v-else-if 指令是在v-if指令的条件不成立之后判断的。
v-else-if指令必须紧跟在带有v-if或者v-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>
data: {
type: 'B'
}
用key管理可复用的元素
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 将不会清除用户已经输入的内容。因为两个模板使用了相同的元素, 不会被替换掉——仅仅是替换了它的 placeholder。
但是上面自动实现的复用原来的元素不是总是符合实际需求的,所以Vue提供了一中表达方式来表达“这两个元素是完全独立的,不要复用这两个元素”。这个方式就是在不需要重复利用的元素上添加一个具有唯一值的key的属性即可。
<template v-if="loginType === 'username'">
<label>Username</label>
// 添加了key
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
// 添加了key
<input placeholder="Enter your email address" key="email-input">
</template>
注意,
v-show
v-show的指令是根据条件是否展示元素,用法差不多
<h1 v-show="ok">Hello!</h1>
data: {
ok: true
}
注意:v-show不支持添加在元素,也不支持添加了v-show指令的元素后面紧跟v-else指令,v-show只是单个指令
v-if VS v-show
v-if是真正的条件渲染,因为它会确保切换过程中的条件块内的事件监听器和子组件适当地被销毁和重建。
v-if也是惰性的:如果在初始渲染时条件为假时,则什么也不做,直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show就简单的多。不管初始条件是什么,元素总是会被渲染,并且只是简单地基于CSS进行切换。
一般来说,v-if的切换开销更高,而v-show的初始渲染开销更高。因此,如果需要非常频繁的切换,则使用v-show较好;如果在运行时条件很少改变时,则使用v-if较好。
v-if 与 v-for 一起使用
不推荐同时使用 v-if 和 v-for 。风格指南
当 v-if 和 v-show 一起使用时, v-for 具有比 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>
data: {
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
在 v-for 模块中,可以访问所有父作用域的属性,即可以访问data中的任意数据。 v-for 还支持一个可选的第二个参数,表示当前数组元素的索引。
<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
data: {
parentMessage: 'Parent',
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
也可以使用 of 替代 in 作为分隔符,因为它更接近JS迭代器的语法:
<div v-for="item of items"></div>
在 v-for 里使用对象
可以使用 v-for 来遍历一个对象的属性。
<ul id="v-for-object" class="demo">
<li v-for="value in object">
{{ value }}
</li>
</ul>
data: {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
也可以提供第二个参数,第二个参数的值是属性名称即对象的键名:
<div v-for="(value, name) in object">
{{ name }}: {{ value }}
</div>
data: {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
也使用第三个参数,第三个参数存放的是对象的索引
<div v-for="(value, name, index) in object">
{{ index }}. {{ name }}: {{ value }}
</div>
data: {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
在遍历对象时,会按照Object.keys()的结果遍历,但是不能保证它的结果在不同的JS引擎下都一致
维护状态
当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。这个类似 Vue 1.x 的 track-by=”$index”。
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性:
<div v-for="item in items" v-bind:key="item.id">
<!-- 内容 -->
</div>
建议尽可能在使用 v-for 时提供 key 属性,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
因为key是 Vue 识别节点的一个通用机制,key 并不仅与 v-for 特别关联。后面我们将在指南中看到,它还具有其它用途。key的文档
注意:不要使用对象或数组之类的非基本类型值作为 v-for 的 key。请用字符串或数值类型的值。
数组更新检测
变更方法
vue将被侦听的数组的变更过方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
可以打开控制台,然后对前面例子的 items 数组尝试调用变更方法。比如 example1.items.push({ message: ‘Baz’ })。
替换数组
上面的变更方法是会变更调用了变更方法的原始数组。相对而言,也有非变更的方法,例如:filter()、concat()、slice()。它们不会变更原始数组,而总是返回一个新数组。当使用非变更方法时,可以使用新数组替换旧数组:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
这不会导致 Vue 丢弃现有 DOM 并重新渲染整个列表。Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的启发式方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。
注意事项
由于JS的限制,Vue不能检测数组和对象的变化。深入响应式原理
显示过滤/排序后的结果
想要显示一个数组经过过滤或排序后的版本,而不实际变更或重置原始数据。在这种情况下,可以创建一个计算属性,来返回过滤或排序后的数组。
<li v-for="n in evenNumbers">{{ n }}</li>
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
evenNumbers: function () {
return this.numbers.filter(function (number) {
return number % 2 === 0
})
}
}
在计算属性不适用的情况下 (例如,在嵌套 v-for 循环中) 你可以使用一个方法methods:
<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: function (numbers) {
return numbers.filter(function (number) {
return number % 2 === 0
})
}
}
在 v-for 里使用值范围
v-for 也可以接受整数。在这种情况下,它会把模板重复对应次数。
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
结果:1 2 3 4 5 6 7 8 9 10
在上使用 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>
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-for 循环中的某个节点不展示,则可以将 v-for 和 v-if 同时使用。
而如果目的是判断是否跳过循环的执行,那么可以将 v-if 置于外层元素(或)上。如:
<ul v-if="todos.length">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
<p v-else>No todos left!</p>
在组件上使用 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"
v-bind:item="item"
v-bind:index="index"
v-bind:key="item.id"
></my-component>
不自动将 item 注入到组件里的原因是,这会使得组件与 v-for 的运作紧密耦合。明确组件的来源能够使组件在其他场合重复使用。
案例:
<div id="todo-list-example">
<form v-on:submit.prevent="addNewTodo">
<label for="new-todo">Add a todo</label>
<input
v-model="newTodoText"
id="new-todo"
placeholder="E.g. Feed the cat"
>
<button>Add</button>
</form>
<ul>
<li
is="todo-item"
v-for="(todo, index) in todos"
v-bind:key="todo.id"
v-bind:title="todo.title"
v-on:remove="todos.splice(index, 1)"
></li>
</ul>
</div>
注意这里的 is=”todo-item” 属性。这种做法在使用 DOM 模板时是十分必要的,因为在
元素内只有 - 元素会被看作有效内容。这样做实现的效果与
相同,但是可以避开一些潜在的浏览器解析错误。查看 DOM 模板解析说明
Vue.component('todo-item', {
template: '\
<li>\
{{ title }}\
<button v-on:click="$emit(\'remove\')">Remove</button>\
</li>\
',
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: function () {
this.todos.push({
id: this.nextTodoId++,
title: this.newTodoText
})
this.newTodoText = ''
}
}
})
事件处理
监听事件
可以使用 v-on 指令来监听DOM事件,并在触发时运行一些JS代码。
示例:
<div id="example-1">
<button v-on:click="counter += 1">Add 1</button>
<p>The button above has been clicked {{ counter }} times.</p>
</div>
var example1 = new Vue({
el: '#example-1',
data: {
counter: 0
}
})
事件处理方法
很多事件执行的逻辑是复杂的,所以直接将JS代码放在 v-on 的指令中是不可行的。因此 v-on 还可以接收一个需要调用的方法的名称,即在methods中定义的方法的名称。
<div id="example-2">
<!-- `greet` 是在下面定义的方法名 -->
<button v-on:click="greet">Greet</button>
</div>
var example2 = new Vue({
el: '#example-2',
data: {
name: 'Vue.js'
},
// 在 `methods` 对象中定义方法
methods: {
greet: function (event) {
// `this` 在方法里指向当前 Vue 实例
alert('Hello ' + this.name + '!')
// `event` 是原生 DOM 事件
if (event) {
alert(event.target.tagName)
}
}
}
})
// 也可以用 JavaScript 直接调用方法
example2.greet() // => 'Hello Vue.js!'
内联处理器中的方法
除了直接绑定到一个方法,也可以在内联JS语句中调用方法:
<div id="example-3">
<button v-on:click="say('hi')">Say hi</button>
<button v-on:click="say('what')">Say what</button>
</div>
new Vue({
el: '#example-3',
methods: {
say: function (message) {
alert(message)
}
}
})
有时也需要在内联语句处理器中访问原始的DOM事件。可以用特殊变量 $event 把它传入方法:
<button v-on:click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
methods: {
warn: function (message, event) {
// 现在我们可以访问原生事件对象
if (event) {
event.preventDefault()
}
alert(message)
}
}
事件修饰符
在事件处理程序中调用 event.preventDefault 或 event.stopPropagation() 是非常常见的需求。尽管可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理DOM事件的细节。
为了解决上面的问题,Vue为 v-on 提供了事件修饰符。修饰符是由点开头的指令后缀来表示的。
- .stop
- .prevent
- .capture
- .self
- .once
- .passive
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
注意:使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
2.1.4新增的修饰符
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
不像其它只能对原生的 DOM 事件起作用的修饰符,.once 修饰符还能被用到自定义的组件事件上。
2.3.0新增
Vue 还对应 addEventListener 中的 passive 选项提供了 .passive 修饰符。
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
这个 .passive 修饰符尤其能够提升移动端的性能。
不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。
按键修饰符
在监听键盘事件时,经常需要检查详细的按键。Vue允许为 v-on 在监听键盘事件时添加按键修饰符:
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">
你可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符。
<input v-on:keyup.page-down="onPageDown">
// 在上述示例中,处理函数只会在 $event.key 等于 PageDown 时被调用
按键码
keyCode 的事件用法已经被废弃了并可能不会被最新的浏览器支持。
使用 keyCode 属性也是允许的:
<input v-on:keyup.13="submit">
为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名:
- .enter
- .tab
- .delete (捕获“删除”和“退格”键)
- .esc
- .space
- .up
- .down
- .left
- .right
有一些按键 (.esc 以及所有的方向键) 在 IE9 中有不同的 key 值, 如果你想支持 IE9,这些内置的别名应该是首选。
你还可以通过全局 config.keyCodes 对象自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
系统修饰键
2.1.0新增
可以使用下面的修饰符来实现仅在按下相应键时才触发鼠标或键盘事件的监听器
- .ctrl
- .alt
- .shift
- .meta
注意:在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。在 Sun 操作系统键盘上,meta 对应实心宝石键 (◆)。在其他特定键盘上,尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品,比如 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。在 Symbolics 键盘上,meta 被标记为“META”或者“Meta”。
例如:
<!-- Alt + C -->
<input v-on:keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div v-on:click.ctrl="doSomething">Do something</div>
请注意修饰键与常规按键不同,在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住 ctrl 的情况下释放其它按键,才能触发 keyup.ctrl。而单单释放 ctrl 也不会触发事件。如果你想要这样的行为,请为 ctrl 换用 keyCode:keyup.17。
.exact修饰符
2.5.0新增
.exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button v-on:click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button v-on:click.ctrl.exact="onCtrlClick">A</button>
<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button v-on:click.exact="onClick">A</button>
鼠标按钮修饰符
2.2.0新增
- .left
- .right
- .middle
这些修饰符会限制处理函数仅响应特定的鼠标按钮。
为什么在HTML中监听事件?
你可能注意到这种事件监听的方式违背了关注点分离 (separation of concern) 这个长期以来的优良传统。但不必担心,因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护上的困难。实际上,使用 v-on 有几个好处:
- 扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
- 因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。
- 当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何清理它们。
表单输入绑定
基础用法
可以使用 v-model 指令在表单、) 并不会生效,应用 v-model 来代替。
复选框
单个复选框,绑定到布尔值:
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
多个复选框,绑定到同一个数组:
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
new Vue({
el: '...',
data: {
checkedNames: []
}
})
单选按钮
<div id="example-4">
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: {{ picked }}</span>
</div>
new Vue({
el: '#example-4',
data: {
picked: ''
}
})
选择框
单选时:
<div id="example-5">
<select v-model="selected">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
</div>
new Vue({
el: '...',
data: {
selected: ''
}
})
如果 v-model 表达式的初始值未能匹配任何选项, 必须:
- 将其 value 属性绑定到一个名叫 value 的 prop 上
- 在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出
Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
现在 v-model 就应该可以在这个组件上完美地工作起来了:
<custom-input v-model="searchText"></custom-input>
自定义事件的其他知识
总结:
讲述了v-model的语法糖
通过插槽分发内容
和 HTML 元素一样,我们经常需要向一个组件传递内容,像这样:
<alert-box>
Something bad happened.
</alert-box>
需要渲染出下面的样子:
Vue 自定义的 元素让这变得非常简单:
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})
只要在需要的地方加入插槽就行了
插槽的其他知识
总结:
讲了插槽的使用
动态组件
在不同组件之间进行动态切换,比如在一个多标签的界面里:
上述内容可以通过 Vue 的 元素加一个特殊的 is 属性来实现:
<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>
在上述示例中,currentTabComponent 可以包括
- 已注册组件的名字,或
- 一个组件的选项对象
你可以在这里查阅并体验完整的代码,或在这个版本了解绑定组件选项对象,而不是已注册组件名的示例。
请留意,这个 属性 可以用于常规 HTML 元素,但这些元素将被视为组件,这意味着所有的 属性 都会作为 DOM 属性 被绑定。对于像 value 这样的 属性 ,若想让其如预期般工作,你需要使用 .prop 修饰器。
其他知识动态和异步组件
解析DOM模板时的注意事项
有些 HTML 元素,诸如
、、 和 和
<template v-if="test">
<div>test is good</div>
</template>
data: {
test: true,
}
v-else
可以使用v-else指令来表示前面条件不成立之后需要展示的模块
v-else指令必须紧跟在带有v-if指令或者v-else-if指令元素的后面,否则v-else指令将不被识别
<div v-if="Math.random() > 0.5">
Now you see me
</div>
<div v-else>
Now you don't
</div>
v-else-if
在2.1.0 新增 v-else-if 指令。
v-else-if 指令是在v-if指令的条件不成立之后判断的。
v-else-if指令必须紧跟在带有v-if或者v-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>
data: {
type: 'B'
}
用key管理可复用的元素
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 将不会清除用户已经输入的内容。因为两个模板使用了相同的元素, 不会被替换掉——仅仅是替换了它的 placeholder。
但是上面自动实现的复用原来的元素不是总是符合实际需求的,所以Vue提供了一中表达方式来表达“这两个元素是完全独立的,不要复用这两个元素”。这个方式就是在不需要重复利用的元素上添加一个具有唯一值的key的属性即可。
<template v-if="loginType === 'username'">
<label>Username</label>
// 添加了key
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
// 添加了key
<input placeholder="Enter your email address" key="email-input">
</template>
注意,
v-show
v-show的指令是根据条件是否展示元素,用法差不多
<h1 v-show="ok">Hello!</h1>
data: {
ok: true
}
注意:v-show不支持添加在元素,也不支持添加了v-show指令的元素后面紧跟v-else指令,v-show只是单个指令
v-if是真正的条件渲染,因为它会确保切换过程中的条件块内的事件监听器和子组件适当地被销毁和重建。 相比之下,v-show就简单的多。不管初始条件是什么,元素总是会被渲染,并且只是简单地基于CSS进行切换。 一般来说,v-if的切换开销更高,而v-show的初始渲染开销更高。因此,如果需要非常频繁的切换,则使用v-show较好;如果在运行时条件很少改变时,则使用v-if较好。 不推荐同时使用 v-if 和 v-for 。风格指南 当 v-if 和 v-show 一起使用时, v-for 具有比 v-if 更高的优先级。列表渲染指南 v-for指令是基于一个数组渲染一个列表。v-for指令需要使用 在 v-for 模块中,可以访问所有父作用域的属性,即可以访问data中的任意数据。 v-for 还支持一个可选的第二个参数,表示当前数组元素的索引。 也可以使用 of 替代 in 作为分隔符,因为它更接近JS迭代器的语法: 可以使用 v-for 来遍历一个对象的属性。 也可以提供第二个参数,第二个参数的值是属性名称即对象的键名: 也使用第三个参数,第三个参数存放的是对象的索引 在遍历对象时,会按照Object.keys()的结果遍历,但是不能保证它的结果在不同的JS引擎下都一致 当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。这个类似 Vue 1.x 的 track-by=”$index”。 这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。 为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性: 建议尽可能在使用 v-for 时提供 key 属性,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。 因为key是 Vue 识别节点的一个通用机制,key 并不仅与 v-for 特别关联。后面我们将在指南中看到,它还具有其它用途。key的文档 注意:不要使用对象或数组之类的非基本类型值作为 v-for 的 key。请用字符串或数值类型的值。 vue将被侦听的数组的变更过方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括: 可以打开控制台,然后对前面例子的 items 数组尝试调用变更方法。比如 example1.items.push({ message: ‘Baz’ })。 上面的变更方法是会变更调用了变更方法的原始数组。相对而言,也有非变更的方法,例如:filter()、concat()、slice()。它们不会变更原始数组,而总是返回一个新数组。当使用非变更方法时,可以使用新数组替换旧数组: 这不会导致 Vue 丢弃现有 DOM 并重新渲染整个列表。Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的启发式方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。 由于JS的限制,Vue不能检测数组和对象的变化。深入响应式原理 想要显示一个数组经过过滤或排序后的版本,而不实际变更或重置原始数据。在这种情况下,可以创建一个计算属性,来返回过滤或排序后的数组。 在计算属性不适用的情况下 (例如,在嵌套 v-for 循环中) 你可以使用一个方法methods: v-for 也可以接受整数。在这种情况下,它会把模板重复对应次数。 类似于 v-if,可以使用带有 v-for 的 来循环渲染一段包含多个元素的内容。
不推荐在同一元素上使用 v-if 和 v-for。风格指南 当它们处于同一节点,v-for 的优先级比 v-if 的更高,这意味着 v-if 将分别重复运行于每个 v-for 的循环中。当你只想为部分项渲染节点时,这种优先级的机制会十分有用,如下: 上面的代码将只渲染未完成的todo。即如果想要 v-for 循环中的某个节点不展示,则可以将 v-for 和 v-if 同时使用。 而如果目的是判断是否跳过循环的执行,那么可以将 v-if 置于外层元素(或)上。如:
需要了解组件的知识。组件 在自定义组件上,也可以像在普通元素上一样使用 v-for 指令。 2.2.0+ 的版本里,当在组件上使用 v-for 时,key 是必须的。 然而,任何数据都不会被自动传递到组件中,因为组件有自己独立的作用域。为了将迭代的数据传递到组件中,需要使用prop: 不自动将 item 注入到组件里的原因是,这会使得组件与 v-for 的运作紧密耦合。明确组件的来源能够使组件在其他场合重复使用。 注意这里的 is=”todo-item” 属性。这种做法在使用 DOM 模板时是十分必要的,因为在 可以使用 v-on 指令来监听DOM事件,并在触发时运行一些JS代码。 很多事件执行的逻辑是复杂的,所以直接将JS代码放在 v-on 的指令中是不可行的。因此 v-on 还可以接收一个需要调用的方法的名称,即在methods中定义的方法的名称。 除了直接绑定到一个方法,也可以在内联JS语句中调用方法: 有时也需要在内联语句处理器中访问原始的DOM事件。可以用特殊变量 $event 把它传入方法: 在事件处理程序中调用 event.preventDefault 或 event.stopPropagation() 是非常常见的需求。尽管可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理DOM事件的细节。 为了解决上面的问题,Vue为 v-on 提供了事件修饰符。修饰符是由点开头的指令后缀来表示的。 注意:使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。 2.1.4新增的修饰符 不像其它只能对原生的 DOM 事件起作用的修饰符,.once 修饰符还能被用到自定义的组件事件上。 2.3.0新增 这个 .passive 修饰符尤其能够提升移动端的性能。 不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。 在监听键盘事件时,经常需要检查详细的按键。Vue允许为 v-on 在监听键盘事件时添加按键修饰符: 你可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符。 keyCode 的事件用法已经被废弃了并可能不会被最新的浏览器支持。 使用 keyCode 属性也是允许的: 为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名: 有一些按键 (.esc 以及所有的方向键) 在 IE9 中有不同的 key 值, 如果你想支持 IE9,这些内置的别名应该是首选。 你还可以通过全局 config.keyCodes 对象自定义按键修饰符别名: 2.1.0新增 注意:在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。在 Sun 操作系统键盘上,meta 对应实心宝石键 (◆)。在其他特定键盘上,尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品,比如 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。在 Symbolics 键盘上,meta 被标记为“META”或者“Meta”。 例如: 请注意修饰键与常规按键不同,在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住 ctrl 的情况下释放其它按键,才能触发 keyup.ctrl。而单单释放 ctrl 也不会触发事件。如果你想要这样的行为,请为 ctrl 换用 keyCode:keyup.17。 2.5.0新增 .exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。 2.2.0新增 这些修饰符会限制处理函数仅响应特定的鼠标按钮。 你可能注意到这种事件监听的方式违背了关注点分离 (separation of concern) 这个长期以来的优良传统。但不必担心,因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护上的困难。实际上,使用 v-on 有几个好处: 可以使用 v-model 指令在表单、) 并不会生效,应用 v-model 来代替。 单个复选框,绑定到布尔值: 多个复选框,绑定到同一个数组: 单选时: 如果 v-model 表达式的初始值未能匹配任何选项, 必须: 自定义事件的其他知识 总结:
讲述了v-model的语法糖 和 HTML 元素一样,我们经常需要向一个组件传递内容,像这样: 需要渲染出下面的样子: 只要在需要的地方加入插槽就行了 总结:
讲了插槽的使用 在不同组件之间进行动态切换,比如在一个多标签的界面里: 在上述示例中,currentTabComponent 可以包括 你可以在这里查阅并体验完整的代码,或在这个版本了解绑定组件选项对象,而不是已注册组件名的示例。 请留意,这个 属性 可以用于常规 HTML 元素,但这些元素将被视为组件,这意味着所有的 属性 都会作为 DOM 属性 被绑定。对于像 value 这样的 属性 ,若想让其如预期般工作,你需要使用 .prop 修饰器。 其他知识动态和异步组件 有些 HTML 元素,诸如 v-if VS v-show
v-if也是惰性的:如果在初始渲染时条件为假时,则什么也不做,直到条件第一次变为真时,才会开始渲染条件块。v-if 与 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>
data: {
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
data: {
parentMessage: 'Parent',
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
<div v-for="item of items"></div>
在 v-for 里使用对象
<ul id="v-for-object" class="demo">
<li v-for="value in object">
{{ value }}
</li>
</ul>
data: {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
<div v-for="(value, name) in object">
{{ name }}: {{ value }}
</div>
data: {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
<div v-for="(value, name, index) in object">
{{ index }}. {{ name }}: {{ value }}
</div>
data: {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
维护状态
<div v-for="item in items" v-bind:key="item.id">
<!-- 内容 -->
</div>
数组更新检测
变更方法
替换数组
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
注意事项
显示过滤/排序后的结果
<li v-for="n in evenNumbers">{{ n }}</li>
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
evenNumbers: function () {
return this.numbers.filter(function (number) {
return number % 2 === 0
})
}
}
<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: function (numbers) {
return numbers.filter(function (number) {
return number % 2 === 0
})
}
}
在 v-for 里使用值范围
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
结果:1 2 3 4 5 6 7 8 9 10
在上使用 v-for
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
v-for 与 v-if 一同使用
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>
<ul v-if="todos.length">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
<p v-else>No todos left!</p>
在组件上使用 v-for
<my-component v-for="item in items" :key="item.id"></my-component>
<my-component
v-for="(item, index) in items"
v-bind:item="item"
v-bind:index="index"
v-bind:key="item.id"
></my-component>
案例:
<div id="todo-list-example">
<form v-on:submit.prevent="addNewTodo">
<label for="new-todo">Add a todo</label>
<input
v-model="newTodoText"
id="new-todo"
placeholder="E.g. Feed the cat"
>
<button>Add</button>
</form>
<ul>
<li
is="todo-item"
v-for="(todo, index) in todos"
v-bind:key="todo.id"
v-bind:title="todo.title"
v-on:remove="todos.splice(index, 1)"
></li>
</ul>
</div>
元素内只有
Vue.component('todo-item', {
template: '\
<li>\
{{ title }}\
<button v-on:click="$emit(\'remove\')">Remove</button>\
</li>\
',
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: function () {
this.todos.push({
id: this.nextTodoId++,
title: this.newTodoText
})
this.newTodoText = ''
}
}
})
事件处理
监听事件
示例:<div id="example-1">
<button v-on:click="counter += 1">Add 1</button>
<p>The button above has been clicked {{ counter }} times.</p>
</div>
var example1 = new Vue({
el: '#example-1',
data: {
counter: 0
}
})
事件处理方法
<div id="example-2">
<!-- `greet` 是在下面定义的方法名 -->
<button v-on:click="greet">Greet</button>
</div>
var example2 = new Vue({
el: '#example-2',
data: {
name: 'Vue.js'
},
// 在 `methods` 对象中定义方法
methods: {
greet: function (event) {
// `this` 在方法里指向当前 Vue 实例
alert('Hello ' + this.name + '!')
// `event` 是原生 DOM 事件
if (event) {
alert(event.target.tagName)
}
}
}
})
// 也可以用 JavaScript 直接调用方法
example2.greet() // => 'Hello Vue.js!'
内联处理器中的方法
<div id="example-3">
<button v-on:click="say('hi')">Say hi</button>
<button v-on:click="say('what')">Say what</button>
</div>
new Vue({
el: '#example-3',
methods: {
say: function (message) {
alert(message)
}
}
})
<button v-on:click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
methods: {
warn: function (message, event) {
// 现在我们可以访问原生事件对象
if (event) {
event.preventDefault()
}
alert(message)
}
}
事件修饰符
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
Vue 还对应 addEventListener 中的 passive 选项提供了 .passive 修饰符。<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
按键修饰符
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">
<input v-on:keyup.page-down="onPageDown">
// 在上述示例中,处理函数只会在 $event.key 等于 PageDown 时被调用
按键码
<input v-on:keyup.13="submit">
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
系统修饰键
可以使用下面的修饰符来实现仅在按下相应键时才触发鼠标或键盘事件的监听器
<!-- Alt + C -->
<input v-on:keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div v-on:click.ctrl="doSomething">Do something</div>
.exact修饰符
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button v-on:click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button v-on:click.ctrl.exact="onCtrlClick">A</button>
<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button v-on:click.exact="onClick">A</button>
鼠标按钮修饰符
为什么在HTML中监听事件?
表单输入绑定
基础用法
复选框
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
new Vue({
el: '...',
data: {
checkedNames: []
}
})
单选按钮
<div id="example-4">
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: {{ picked }}</span>
</div>
new Vue({
el: '#example-4',
data: {
picked: ''
}
})
选择框
<div id="example-5">
<select v-model="selected">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
</div>
new Vue({
el: '...',
data: {
selected: ''
}
})
现在 v-model 就应该可以在这个组件上完美地工作起来了:Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
<custom-input v-model="searchText"></custom-input>
通过插槽分发内容
<alert-box>
Something bad happened.
</alert-box>
Vue 自定义的 Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})
插槽的其他知识
动态组件
上述内容可以通过 Vue 的 <!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>
解析DOM模板时的注意事项
、
、 和 和