指令
指令 (Directives) 是带有
v-
前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for
是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
1.条件渲染
1.1 v-if
使用v-if
来对DOM元素进行条件渲染:
<!--根据awesome数据的真假值来决定是否渲染出h1-->
<h1 v-if="awesome">Vue is awesome!</h1>
可以继续使用v-else
或者 v-else-if
来进行多个选择渲染,规则类似于js中的判断:
<h1 v-if="type === 'A'">A</h1>
<h1 v-else-if="type === 'B'">B</h1>
<h1 v-else-if="type === 'C'">C</h1>
<h1 v-else>Not A/B/C</h1>
结合template
标签来实现多个标签内容的条件渲染:
<!--template标签本身不会被渲染到页面中,只有它里面的内容会渲染-->
<template v-if="awesome">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
1.2 v-show
v-show
的用法和v-if
大致一样:<h1 v-show="ok">Hello!</h1>
。不同的是带有 v-show
的元素始终会被渲染并保留在 DOM 中。v-show
只是简单地切换元素的 CSS property display
。
注意,v-show
不支持 <template>
元素,也不支持 v-else
。
1.3 v-if
vs v-show
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
2.动态标签属性
2.1 v-bind
当标签属性的值需要动态变化时,使用v-bind
:
<a v-bind:href="url">Hello Vue!</a>
当然,属性名也可以动态:
<div v-bind:[attr]="value"></div>
<!--
data : {
attr : "...",
value : "..."
}
-->
2.2 缩写
v-bind
可以直接省略,只留下:
<a :href="url">Hello Vue!</a>
2.3 class类名的绑定
基础写法:
<!--
根据数据 isRed 的真假值,来决定有没有"red"这个名字。
-->
<div :class="{red:isRed}"></div>
固定某个名字,而其他名字根据数据真假值动态:
<!--
固定拥有"aaa"名字
"bbb" "ccc"则由数据的真假来决定
-->
<div
class="aaa"
:class="{bbb : isB,ccc : isC}"
></div>
整个对象数据动态:
<div :class="classObject"></div>
<!--
data:{
classObject:{
aaa : true,
bbb : false
}
}
-->
数组语法:
<div :class="[redClass,activeClass]"></div>
<!--
数据
data:{
redClass : "red",
activeClass : "active"
}
渲染为
<div class="red active"></div>
-->
2.4 style样式的绑定
基础写法:
<div :style="{width:w,height:h}"></div>
固定属性写法:
<div
style="width:100px"
:style="{
height : h
}"
></div>
整个对象数据动态:
<div :style="styleObject"></div>
数组写法:(多个对象数据)
<div :style="[styleObject1,styleObject2]"></div>
3.事件
大部分时候数据的变化都是由事件触发的,Vue中事件的绑定通过指令v-on
完成。
3.1 监听事件
<div id="app1">
<p>点击了:{{num}}次</p>
<button v-on:click="num ++">按钮</button>
</div>
<script>
let vm = new Vue({
el : "#app1",
data : {
num : 0
}
});
</script>
v-on:
前缀可以简写为@
。例如:<button @click="num ++">按钮</button>
3.2 事件处理方式
然而许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写在 v-on
指令中是不可行的。因此 v-on
还可以接收一个需要调用的方法名称,这些方法需要配置在methods
中。
<div id="app2">
<p>点击了:{{num}}次</p>
<button @click="clickFn">按钮</button>
</div>
<script>
let vm = new Vue({
el : "#app2",
data : {num : 0},
methods:{
clickFn(e){
alert(`鼠标坐标:${e.pageX},${e.pageY}`);
this.num ++;
}
}
});
</script>
3.3 传递参数
如果需要为事件函数传递参数,那么可以直接使用函数自执行的写法:
<div id="app3">
<p>{{msg}}</p>
<button @click="fn('Hello')">Hello按钮</button>
<button @click="fn('World')">World按钮</button>
<!--
如果需要事件对象,传入第二个参数
@click="fn('World',$event)"
-->
</div>
<script>
let app3 = new Vue({
el : "#app3",
data : {msg:"Init"},
methods : {
fn(val){
this.msg = val;
}
}
});
</script>
3.4 事件修饰词
在事件处理程序中调用 event.preventDefault()
或 event.stopPropagation()
是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
为了解决这个问题,Vue.js 为 v-on
提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。
.stop
阻止事件冒泡;.prevent
阻止默认事件;.capture
使用事件捕获模式;.self
只在自身触发(子元素不能触发);.once
只触发一次;.passive
默认事件立即触发,无视preventDefault。
<!-- 阻止单击事件继续传播 -->
<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>
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
3.5 按键修饰符
在监听键盘事件时,如果想要监听具体的某个键,可以使用按键修饰符。
使用键名(JS课程学习的 event.key 属性的值)直接修饰:
<!-- enter 按下时,调用 vm.submit() -->
<input v-on:keyup.enter="submit">
<!-- pageDown按下时,调用vm.onPageDown() -->
<input v-on:keyup.page-down="onPageDown">
使用键值(event.keyCode)也可以修饰,但是这个属性已经被废弃了,不推荐使用。
3.6 系统修饰符
可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。
.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>
.exact
修饰符
.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>
鼠标按钮修饰符
.left
.right
.middle
这些修饰符会限制处理函数仅响应特定的鼠标按钮。
4.渲染HTML
{{}}
插值符号会把内容以文本的形式渲染,而v-html
指令会把内容以HTML的形式渲染。
<div id="app4">
以文本形式:<p>{{msg}}</p>
以HTML形式:<p v-html="msg"></p>
</div>
<script>
new Vue({
el : "#app4",
data : {
msg : "<span style='color:red'>Hello World!</span>"
}
});
</script>
5.列表渲染
我们可以用 v-for
指令基于一个数组来渲染一个列表。v-for
指令需要使用 item in items
形式的特殊语法,其中 items
是源数据数组,而 item
则是被迭代的数组元素的别名。
5.1 遍历数组
<ul>
<li v-for="item in items">{{item.msg}}</li>
</ul>
<!--
data : {
item : [{msg:"Foo"},{msg:"Bar"}]
}
解析为
<ul>
<li>Foo</li>
<li>Bar</li>
</ul>
-->
v-for
还支持一个可选的第二个参数,即当前项的索引。
<li v-for="(value,index) in items">{{item.msg}}</li>
5.2 遍历对象
<!--只遍历值-->
<!--<p v-for="value in obj">{{value}}</p>-->
<!--遍历属性与值-->
<p v-for="(key,value) in obj">{{name}}:{{value}}</p>
5.3 key
属性
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。
key
的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key
attribute:
<div id="app5">
<h4>输入框填入内容后,点击排序查看区别</h4>
<h6>绑定key</h6>
<ul>
<li v-for="item in item1" :key="item">
{{item}}:<input type="text">
</li>
</ul>
<button @click="item1Sort">排序</button>
<h6>没有绑定key</h6>
<ul>
<li v-for="item in item2">
{{item}}:<input type="text">
</li>
</ul>
<button @click="item2Sort">排序</button>
</div>
<script>
new Vue({
el : "#app5",
data : {
item1:["A","B","C","D","E"],
item2:["A","B","C","D","E"]
},
methods:{
item1Sort(){
this.item1.sort(()=>Math.random()-.5);
},
item2Sort(){
this.item2.sort(()=>Math.random()-.5);
}
}
});
</script>
key
与v-for
并不是一定要配套出现,在任何时候,如果希望区分两个不同的内容、不希望复用,都可以通过绑定不同key
来实现。
建议尽可能在使用
v-for
时提供key
属性,除非遍历输出的 DOM 内容非常简单,或者确实需要没有key
时的渲染特性。
5.4 结合template
同样的,template
标签本身并不会被渲染出来,而是将内部的多个节点循环渲染。
<ul>
<template v-for="item in items">
<li>{{item.id}}</li>
<li>{{item.msg}}</li>
</template>
</ul>
5.5 对象与数组的响应式监听
Vue不能监听数组/对象属性的直接变化。例如:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
使用 Vue.set()
(或者 vm.$set()
) 来更新数据,这样才能做到响应式:
vm.$set(vm.items,1,"x"); //这是响应式的
对象的属性新增,也不能直接使用.
操作符,那样不是响应式的。如果要为实例新增属性,使用vm.$set()
。
另外,一些数组方法是响应式的,可以直接使用:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
6.表单输入绑定
你可以用 v-model
指令在表单 <input>
、<textarea>
及 <select>
元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。
v-model
会忽略所有表单元素的value
、checked
、selected
attribute 的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的data
选项中声明初始值。
6.1 文本框
<input v-model="message">
<p>Message is: {{ message }}</p>
6.2 多行文本
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br>
<textarea v-model="message"></textarea>
6.3 复选框
单个复选框,绑定到布尔值:
<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>
6.4 单选按钮
<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>
6.5 单选下拉
<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>
6.6 多选下拉
绑定到数组
<div id="example-6">
<select v-model="selected" multiple style="width: 50px;">
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<br>
<span>Selected: {{ selected }}</span>
</div>
6.7 修饰词
.lazy
在默认情况下,v-model
在每次 input
事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy
修饰符,从而转为在 change
事件_之后_进行同步:
<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg">
.number
如果想自动将用户的输入值转为数值类型,可以给 v-model
添加 number
修饰符:
<input v-model.number="age" type="number">
这通常很有用,因为即使在 type="number"
时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat()
解析,则会返回原始的值。
.trim
如果要自动过滤用户输入的首尾空白字符,可以给 v-model
添加 trim
修饰符:
<input v-model.trim="msg">
7.自定义指令
除了核心功能默认内置的指令 (v-model
和 v-show
),Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
举个聚焦输入框的例子,如下:
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
<input v-focus>
如果想注册局部指令,组件中也接受一个 directives
的选项:
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
通常来讲,自定义指令使用的场景并不多。了解更多