一、什么是指令?
根据vue官方文档的说法:指令 (Directives) 是带有 v-
前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for
是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。简单来说,就是使用自定义属性的方式比较方便快捷的对元素进行DOM操作。
二、常用指令
-
v-text: 更新元素的 textContent。如果要更新部分的 textContent,需要使用 {{ Mustache }} 插值。
<div v-text="msg"></div> <!-- 插值表达式写法,上下两种效果一样 --> <div>{{msg}}</div>
-
v-html: 更新元素的 innerHTML。注意:内容按普通 HTML 插入 - 不会作为 Vue 模板进行编译。如果视图使用 v-html 组合模板,可以重新考虑是否通过使用组件来替代。
<div v-html="msg"></div> <!-- 需要注意的是:在网站上动态渲染任意 HTML 是非常危险的,因为容易导致 XSS 攻击。只在可信内容上使用 v-html,永不用在用户提交的内容上。 -->
-
v-show: 根据表达式之真假值,切换元素的
display
样式属性,来控制元素显示隐藏。当条件变化时该指令触发过渡效果。<div v-show="flag"></div> <!-- 当元素flag值为true时,元素展示;反之元素隐藏 -->
-
v-if: 根据表达式的值的Boolean来有条件地渲染元素。在切换时元素及它的数据绑定 / 组件被销毁并重建。如果元素是
<template>
,将提出它的内容作为条件块。当条件变化时该指令触发过渡效果。<div v-if="flag"></div> <!-- 当元素flag值为true时,元素渲染;反之元素销毁 --> <!-- v-show通过css样式控制元素显示隐藏,适用于频繁点击等业务场景,而v-if当条件为真的时候才渲染会有性能的一个提升 -->
-
v-else-if: 与v-if搭配使用,可以进行链式条件调用;与js里面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>
-
v-else: 不需要表达式,与v-if搭配使用,进行链式调用,注意:前一兄弟元素必须有
v-if
或v-else-if
;<div v-if="type === 'A'">A</div> <div v-else-if="type === 'B'">B</div> <div v-else>C</div>
-
v-for: 基于源数据多次渲染元素或模板块。可以渲染的值为(Array | Object | number | string | Iterable (2.6 新增));
<!-- v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。 --> <!-- item代表每项的值,index代表索引,items就是循环变量 --> <div v-for="(item, index) in items"></div> <!-- val代表每项的值,name代表对象的键名,index代表索引,object循环对象 --> <div v-for="(val, name, index) in object" :key="val"></div> <!-- key 的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。绑定key会提升项目渲染速度 -->
-
v-on: 缩写@,绑定事件监听器。事件类型由参数指定。表达式可以是一个方法的名字或一个内联语句,如果没有修饰符也可以省略。
-
事件修饰符: 在触发事件的同时去处理 DOM 事件细节。
.stop
- 调用event.stopPropagation()
, 阻止单击事件继续传播。.prevent
- 调用event.preventDefault()
,不再重载页面.capture
- 添加事件侦听器时使用 capture 模式。.self
- 只当事件是从侦听器绑定的元素本身触发时才触发回调。.{keyCode | keyAlias}
- 只当事件是从特定键触发时才触发回调。.native
- 监听组件根元素的原生事件。.once
- 只触发一次回调。.left
- (2.2.0) 只当点击鼠标左键时触发。.right
- (2.2.0) 只当点击鼠标右键时触发。.middle
- (2.2.0) 只当点击鼠标中键时触发。.passive
- (2.3.0) 以{ passive: true }
模式添加侦听器
<!-- 方法处理器 --> <button v-on:click="doThis"></button> <!-- 动态事件 (2.6.0+) --> <button v-on:[event]="doThis"></button> <!-- 内联语句 --> <button v-on:click="doThat('hello', $event)"></button> <!-- 缩写 --> <button @click="doThis"></button> <!-- 动态事件缩写 (2.6.0+) --> <button @[event]="doThis"></button> <!-- 停止冒泡 --> <button @click.stop="doThis"></button> <!-- 阻止默认行为 --> <button @click.prevent="doThis"></button> <!-- 阻止默认行为,没有表达式 --> <form @submit.prevent></form> <!-- 串联修饰符 --> <button @click.stop.prevent="doThis"></button> <!-- 键修饰符,键别名 --> <input @keyup.enter="onEnter"> <!-- 键修饰符,键代码 --> <input @keyup.13="onEnter"> <!-- 点击回调只会触发一次 --> <button v-on:click.once="doThis"></button> <!-- 对象语法 (2.4.0+) --> <button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
-
按键修饰符: 在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为
v-on
在监听键盘事件时添加按键修饰符;.enter
.tab
.delete
(捕获“删除”和“退格”键).esc
.space
.up
.down
等等一系列按键。.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>
-
自定义按键修饰符:
// 可以使用 `v-on:keyup.f1` f1就是自定义按键修饰符名字,112对应键盘码 Vue.config.keyCodes.f1 = 112
-
-
v-bind: 缩写
:
,动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。单向数据绑定,当绑定值发生变化,做出对应改变。<!-- 绑定一个 attribute --> <img v-bind:src="imageSrc"> <!-- 动态 attribute 名 (2.6.0+) --> <button v-bind:[key]="value"></button> <!-- 缩写 --> <img :src="imageSrc"> <!-- 动态 attribute 名缩写 (2.6.0+) --> <button :[key]="value"></button> <!-- 内联字符串拼接 --> <img :src="'/path/to/images/' + fileName"> <!-- class 绑定 --> <div :class="{ red: isRed }"></div> <div :class="[classA, classB]"></div> <div :class="[classA, { classB: isB, classC: isC }]"> <!-- style 绑定 --> <div :style="{ fontSize: size + 'px' }"></div> <div :style="[styleObjectA, styleObjectB]"></div> <!-- 绑定一个全是 attribute 的对象 --> <div v-bind="{ id: someProp, 'other-attr': otherProp }"></div> <!-- 通过 prop 修饰符绑定 DOM attribute --> <div v-bind:text-content.prop="text"></div> <!-- prop 绑定。“prop”必须在 my-component 中声明。--> <my-component :prop="someThing"></my-component> <!-- 通过 $props 将父组件的 props 一起传给子组件 --> <child-component v-bind="$props"></child-component> <!-- .camel 修饰符允许在使用 DOM 模板时将 v-bind property 名称驼峰化,例如 SVG 的 viewBox property: --> <svg :view-box.camel="viewBox"></svg>
-
v-model: 对于表单的双向数据绑定,随表单控件类型不同而不同。
<!-- .lazy - 取代 input 监听 change 事件 .number - 输入字符串转为有效的数字 .trim - 输入首尾空格过滤 --> <input v-model.trim="text" placeholder="请输入">
-
v-slot: 缩写
#
,可放置在函数参数位置的 JavaScript 表达式 (在支持的环境下可使用解构)。可选,即只需要在为插槽传入 prop 的时候使用。<!-- 具名插槽 --> <base-layout> <template v-slot:header>Header content</template> <template v-slot:footer>Footer content</template> </base-layout> <!-- 接收 prop 的具名插槽 --> <infinite-scroll> <template v-slot:item="slotProps"> <div class="item">{{ slotProps.item.text }}</div> </template> </infinite-scroll> <!-- 接收 prop 的默认插槽,使用了解构 --> <mouse-position v-slot="{ x, y }">Mouse position: {{ x }}, {{ y }}</mouse-position>
-
v-pre: 跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。(提升性能)
<span v-pre>{{ "跳过" }}</span>
-
v-cloak: 这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如
[v-cloak] { display: none }
一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。<style> [v-cloak] { display: none; } </style> <div v-cloak> {{ message }} </div>
-
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-model
和 v-show
),Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。
-
注册全局自定义指令: 页面一加载就聚焦输入框
// 注册一个全局自定义指令 `v-focus` Vue.directive('focus', { // 当被绑定的元素插入到 DOM 中时…… inserted: function (el) { // 聚焦元素 el.focus() } })
-
注册局部指令: 组件中也接受一个
directives
的选项:directives: { focus: { // 指令的定义 inserted: function (el) { el.focus() } } } // 使用:<input v-focus>
-
钩子函数: 一个指令定义对象可以提供如下几个钩子函数 (均为可选)
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。unbind
:只调用一次,指令与元素解绑时调用。
-
钩子函数参数:
el
:指令所绑定的元素,可以用来直接操作 DOM。- binding:一个对象,包含以下 property:
name
:指令名,不包括v-
前缀。value
:指令的绑定值,例如:v-my-directive="1 + 1"
中,绑定值为2
。oldValue
:指令绑定的前一个值,仅在update
和componentUpdated
钩子中可用。无论值是否改变都可用。expression
:字符串形式的指令表达式。例如v-my-directive="1 + 1"
中,表达式为"1 + 1"
。arg
:传给指令的参数,可选。例如v-my-directive:foo
中,参数为"foo"
。modifiers
:一个包含修饰符的对象。例如:v-my-directive.foo.bar
中,修饰符对象为{ foo: true, bar: true }
。
vnode
:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。oldVnode
:上一个虚拟节点,仅在update
和componentUpdated
钩子中可用。
-
实例:
// <div id="hook-arguments-example" v-demo:foo.a.b="message"></div> Vue.directive('demo', { bind: function (el, binding, vnode) { var s = JSON.stringify el.innerHTML = 'name: ' + s(binding.name) + '<br>' + 'value: ' + s(binding.value) + '<br>' + 'expression: ' + s(binding.expression) + '<br>' + 'argument: ' + s(binding.arg) + '<br>' + 'modifiers: ' + s(binding.modifiers) + '<br>' + 'vnode keys: ' + Object.keys(vnode).join(', ') } }) new Vue({ el: '#hook-arguments-example', data: { message: 'hello!' } })
四、特殊的attribute
-
**key:**key可以管理可复用的元素,减少不必要的元素的重新渲染,也要让必要的元素能够重新渲染。
-
ref:
ref
被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的$refs
对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例; -
is: 用于动态组件且基于 DOM 内模板的限制来工作。
<!-- 当 `currentView` 改变时,组件也跟着改变 --> <component v-bind:is="currentView"></component> <!-- 这样做是有必要的,因为 `<my-row>` 放在一个 --> <!-- `<table>` 内可能无效且被放置到外面 --> <table> <tr is="my-row"></tr> </table>
四、内置的组件
-
component: 渲染一个“元组件”为动态组件。依
is
的值,来决定哪个组件被渲染。<!-- 动态组件由 vm 实例的 `componentId` property 控制 --> <component :is="componentId"></component> <!-- 也能够渲染注册过的组件或 prop 传入的组件 --> <component :is="$options.components.child"></component>
-
transition:
<transition>
元素作为单个元素/组件的过渡效果。<transition>
只会把过渡效果应用到其包裹的内容上,而不会额外渲染 DOM 元素,也不会出现在可被检查的组件层级中,如果有业务需求可以去详细了解,这里就不多做阐述。 -
transition-group:
<transition-group>
元素作为多个元素/组件的过渡效果。<transition-group>
渲染一个真实的 DOM 元素。默认渲染<span>
,可以通过tag
attribute 配置哪个元素应该被渲染。 -
keep-alive:
<keep-alive>
包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和<transition>
相似,<keep-alive>
是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。当组件在<keep-alive>
内被切换,它的activated
和deactivated
这两个生命周期钩子函数将会被对应执行。<!-- 基本 --> <keep-alive> <component :is="view"></component> </keep-alive> <!-- 多个条件判断的子组件 --> <keep-alive> <comp-a v-if="a > 1"></comp-a> <comp-b v-else></comp-b> </keep-alive> <!-- 和 `<transition>` 一起使用 --> <transition> <keep-alive> <component :is="view"></component> </keep-alive> </transition>
-
slot:
<slot>
元素作为组件模板之中的内容分发插槽。<slot>
元素自身将被替换。Vue.component('alert-box', { template: ` <div class="demo-alert-box"> <strong>Error!</strong> <slot></slot> </div> ` })