Vue不仅内置了各类指令,包括条件渲染、事件处理等,还能注册自定义指令。
## 一、条件渲染
  条件渲染的指令包括v-if、v-else、v-else-if和v-show。
**1)v-if**
  该指令的功能和条件语句中的if类似,可根据表达式的计算结果,判断是否渲染分支中的元素和它所包含的子元素。在下面的示例中,当把数据对象的exist属性设为true时,\<div>和\<p>两个元素就会被添加到页面的DOM中。
~~~html
<div v-if="exist">
<p>strick</p>
</div>
<script>
var vm = new Vue({
data: {
exist: true
}
});
</script>
~~~
  当需要通过v-if指令渲染多个元素,并且不必指定包裹元素时,就得使用Vue提供的\<template>元素了,如下代码所示。其功能类似于React的Fragments,也可生成一个不可见的包裹元素,并且在最终的DOM中,不会包含\<template>元素。
~~~html
<template v-if="exist">
<p>strick</p>
<p>freedom</p>
</template>
~~~
**2)v-else**
  该指令的功能和条件语句中的else类似,需要与v-if配合使用,并且要紧跟在v-if或v-else-if之后(如下所示),否则该指令将失效。
~~~html
<p v-if="exist">strick</p>
<p v-else>freedom</p>
~~~
**3)v-else-if**
  该指令与v-else类似,也需要紧跟在v-if或v-else-if之后,但它能自定义条件表达式,如下所示。
~~~html
<p v-if="digit==1">strick</p>
<p v-else-if="digit==2">justify</p>
<p v-else>freedom</p>
~~~
**4)v-show**
  该指令能根据表达式的计算结果显示或隐藏当前元素,如果得到的结果是一个假值,那么会为元素添加内联样式“display: none;”,如下所示。
~~~html
<!-- <p style="display: none;">strick</p> -->
<p v-show="false">strick</p>
~~~
  v-if会在切换过程中创建或销毁可能包含的数据绑定和子元素,并且只有在条件为真时,才会渲染该分支中的元素。而v-show无论条件的真假都会渲染元素,很适合频繁切换,并且要注意,v-show既不能作用于\<template>元素,也无法与v-else配合。由前面的分析可知,v-if有更高的切换开销而v-show有更高的初始渲染开销。
## 二、列表渲染
  v-for是一个用于列表渲染的指令,它能接收数组、对象和整数,并且支持\<template>元素。
**1)数组**
  当基于数组来渲染一个列表时,v-for指令可迭代数组的元素和其索引。下面是一个示例,在第一组\<li>元素中,item是元素的别名,迭代语法和for-in循环语句类似;在第二组\<li>元素中,向v-for指令传递了两个参数,其中第二个参数表示元素的索引。
~~~html
<ul id="container">
<li v-for="item in array">
{{item}}
</li>
<li v-for="(item, index) in array">
{{index}} - {{item}}
</li>
</ul>
<script>
var vm = new Vue({
el: "#container",
data: {
array: ["strick", "freedom"]
}
});
</script>
~~~
  v-for指令中的in还可以替换成of,从而更接近ES6迭代器的语法,如下所示。
~~~html
<li v-for="item of array">
{{item}}
</li>
~~~
**2)对象**
  当基于对象来渲染一个列表时,v-for指令可迭代对象的属性(即键值对)和其索引。在下面的示例中,为v-for指令传递了三个参数,其中属性值在前,属性名在后。
~~~html
<li v-for="(value, key, index) in obj">
{{index}} - {{key}} - {{value}}
</li>
~~~
**3)整数**
  v-for指令接收的整数,可用于循环次数,如下所示,其中digit从1开始计数。
~~~html
<li v-for="digit in 3">
{{digit}}
</li>
~~~
**4)key特性**
  Vue为元素提供了一个能标识其身份的[key特性](https://cn.vuejs.org/v2/api/#key),利用该特性可让diff算法快速找到变化的节点,并且高效的将其插入到新位置,而不用渲染无变化的元素。可以像下面这样为每个\<li>元素设置一个key特性,其值为peoples数组中的元素对象的id属性。
~~~html
<ul id="container">
<li v-for="item in peoples" v-bind:key="item.id">
{{item}}
</li>
</ul>
<script>
var vm = new Vue({
el: "#container",
data: {
peoples: [
{ id: 1, name: "strick" },
{ id: 2, name: "freedom" }
]
}
});
</script>
~~~
  注意,兄弟元素之间的key特性要保持唯一性,即相同父元素的子元素,其key特性要独一无二。下面这样会让key重复,从而造成渲染错误。
~~~html
<ul id="container">
<li v-for="item in peoples" v-bind:key="item.id">
{{item}}
</li>
<li v-for="item in peoples" v-bind:key="item.id">
{{item}}
</li>
</ul>
~~~
  为了渲染高效,Vue通常会复用已有元素而不是重新渲染,但如果要强制替换元素,那么可以使用key特性。例如有两个文本框,如下代码所示,由于两个文本框都声明了key特性,因此每次通过v-if指令进行切换时,其内容都会被清空。
~~~html
<input placeholder="enter name" key="name" v-if="display"/>
<input placeholder="enter age" key="age" v-else/>
~~~
**5)数组更新检测**
  Vue包装了多个会修改原始数组的变异方法(Mutation Method),例如push()、shift()、splice()等,每当调用这些方法时,都会触发视图更新。在下面的示例中,vm是一个Vue实例,peoples是一个数组,一旦调用push()方法,就会将新增的对象渲染到页面中。
~~~js
vm.peoples.push({id:3, name:"justify"});
~~~
  对于那些filter()、concat()、slice()等非变异方法,可通过替换数组来触发视图的更新,如下所示。
~~~js
vm.peoples = vm.peoples.slice(0, 1);
~~~
## 三、事件处理
Vue提供了用于注册事件的[v-on](https://cn.vuejs.org/v2/api/#v-on)指令,它的参数是事件类型,接收的表达式既可以是方法名,也可以是内联语句,并且能与多个修饰符配合使用。
**1)v-on**
  当接收一个方法名时,默认会将事件对象作为第一个参数传递给事件处理程序,例如下面dot()方法中的e参数。
~~~html
<button v-on:click="dot">点击</button>
<script>
var vm = new Vue({
data: {
name: "strick"
},
methods: {
dot: function(e) {
console.log(e, this.name);
}
}
});
</script>
~~~
  当接收一条内联语句时,可通过一个特殊的$event变量传递事件对象,并且可声明在形参列表的任意位置,如下所示。
~~~html
<button v-on:click="dot(28, $event, 'university')">点击</button>
<script>
var vm = new Vue({
data: {
name: "strick"
},
methods: {
dot: function(age, e, school) {
console.log(e, this.name, age, school);
}
}
});
</script>
~~~
  v-on还有一种简写形式,用“@”符号替代“v-on:”,如下代码所示,后文的示例都会使用该形式。
~~~html
<button @click="dot">点击</button>
~~~
  如果要在同一个事件类型中执行多个事件处理程序,那么可以像下面这样用逗号把两条内联语句隔开。
~~~html
<button @click="dot1(),dot2()">点击</button>
~~~
**2)修饰符**
  v-on支持多种类型的修饰符(包括事件、按键、鼠标等),在事件细节的处理方面,提供了更多的选择,例如可用的事件修饰符如表1所列。
:-: ![](https://img.kancloud.cn/b6/41/b641dbf4468d66bbc721f2ba35d19dd7_720x321.jpg)
表1 事件修饰符
  当串联多个修饰符时,会按顺序影响事件的处理,即不同的顺序会产生不同的效果。下面用一个示例来演示不同排列所造成的差异,其中print()方法用于打印接收的参数,\<section>元素添加了.stop和.self两个修饰符。
~~~html
<div @click="print('div')">
<section @click.stop.self="print('section')">
<button @click="print('button')">点击</button>
</section>
</div>
~~~
  当将@click.stop.self作用于\<section>元素时,点击按钮只会输出“button”,因为先执行.stop就会阻止所有元素发起的事件传播;当将@click.self.stop作用于\<section>元素时,点击按钮会依次输出“button”和“div”,因为先执行.self就只会阻止\<section>元素发起的事件传播。
  除了事件修饰符之外,Vue还提供了按键修饰符,只有当按下特定的键时,才会触发监听的键盘事件。在下面的示例中,.arrow-down修饰符表示导航键中的向下键(ArrowDown)。注意,只要是KeyboardEvent.key提供的有效键名,都能转换成连字符分隔(kebab-case)的修饰符形式。
~~~html
<input @keyup.arrow-down="enter" />
~~~
  不仅如此,通过事件对象的keyCode属性得到的建码也能作为修饰符使用,例如ArrowDown的建码是40,可以像下面这样声明。
~~~html
<input @keyup.40="enter" />
~~~
  由于键码不便记忆,因此Vue提供了常用键码的别名,例如.enter、.tab、.space、.down等。对于那些不常用的建码,可通过Vue.config.keyCodes自定义,例如为F12键创建别名,如下所示。
~~~js
Vue.config.keyCodes.f12 = 123;
~~~
  有一点要注意,keyCode已从Web标准中移除,在未来可能也会被最新的浏览器废弃,因此要慎用该类修饰符。
  Vue 2.1.0新增了四个特殊的系统修饰键:.ctrl、.alt、.shift和.meta,其中.meta会随着操作系统的不同而对应不同的键,例如在Mac系统中对应command键,而在Windows系统中对应Windows图标键。与按键修饰符不同,在与keyup事件配合时,系统修饰键必须处于按下状态,否则无法触发事件。以下面的文本框为例,如果要执行回调函数enter(),不仅要按一下ArrowDown键,还得同时按住Ctrl键。
~~~html
<input @keyup.arrow-down.ctrl="enter" />
~~~
  Vue 2.5.0新增了一个特殊的.exact修饰符,用于控制系统修饰键的组合。在下面的示例中,第一个文本框中的事件,可通过单独按下Ctrl键或与其它修饰键组合触发;第二个文本框添加了.exact修饰符,只有按下Ctrl键才会触发事件,无法与其它修饰键组合。
~~~html
<input @keyup.ctrl="enter" />
<input @keyup.ctrl.exact="enter" />
~~~
  Vue 2.2.0新增了三个用于鼠标事件的修饰符:.left、.right和.middle,分别对应三种鼠标按键状态:左键,右键和中键。
## 四、自定义指令
  由于内置指令无法满足所有场景,因此Vue允许用户注册自定义的指令,以便封装特殊的DOM行为,提升代码复用率。
**1)注册**
  可在实例的directives选项中注册局部指令,例如为文本框自动输入一段字符,如下代码所示。其中autoEnter是指令名称,它的值是一个指令的定义对象,注意,在元素中使用该自定义指令时,不能采用驼峰的方式。
~~~html
<input v-auto-enter/>
<script>
var vm = new Vue({
directives: {
autoEnter: {
inserted: function(el) {
el.value = "strick";
}
}
}
});
</script>
~~~
  也可以在创建Vue实例之前,通过Vue.directive()方法注册全局指令,如下所示。
~~~js
Vue.directive("autoEnter", {
inserted: function(el) {
el.value = "strick";
}
});
var vm = new Vue({...});
~~~
**2)定义对象**
  在指令的定义对象中,有5个钩子函数,其使用方法如表2所述。
:-: ![](https://img.kancloud.cn/5b/01/5b01d6fa8c6ba5760a095bd73698eef6_1623x444.png)
表2 钩子函数
  接下来自定义一个v-hooks指令,并为其添加4个钩子函数,作用于\<div>元素,如下所示,其中\<p>是\<div>的子元素。
~~~html
<div v-if="display" v-hooks="name">
<p>{{age}}</p>
</div>
<script>
var vm = new Vue({
data: {
name: "strick",
display: true,
age: 28
},
directives: {
hooks: {
bind: function() {
console.log("bind");
},
inserted: function() {
console.log("inserted");
},
update: function(el, binding) {
console.log("update", binding.value, binding.oldValue);
},
unbind: function() {
console.log("unbind");
}
}
}
});
</script>
~~~
  当在浏览器中首次加载时,控制台会依次输出“bind”和“inserted”。
  如果修改v-hooks指令的值(如下代码所示),那么就会调用update()函数,并输出指令的新值(value)和旧值(oldValue),分别是"freedom"和"strick"。
~~~js
vm.name = "freedom";
~~~
  此时再修改子元素\<p>中的插值(如下代码所示),那么仍然会调用update()函数,只是指令的新值和旧值都是“freedom”。
~~~js
vm.age = 30;
~~~
  当通过v-if指令销毁元素时(如下代码所示),就会调用unbind()函数,并输出“unbind”。
~~~js
vm.display = false;
~~~
  当bind()和update()触发的行为相同时,Vue允许将指令的定义对象声明成一个函数,如下所示。
~~~js
Vue.directive("func", function (el, binding) {
console.log("function");
});
~~~
**3)参数**
  钩子函数可接收4个参数:el、binding、vnode和oldVnode,参数说明如下所列。注意,除了el之外,其它参数都是只读的。
  (1)el:指令所绑定的元素。
  (2)binding:一个对象,其属性如表3所列,举例参照的是自定义的v-hooks指令。
  (3)vnode:Vue编译生成的虚拟节点。
  (4)oldVnode:上一个虚拟节点,只存在于update()和componentUpdated()函数中。
:-: ![](https://img.kancloud.cn/44/93/4493570eb24d6cbafc2899feb174f173_1279x664.png)
表3 binding对象
  自定义指令可接收任意合法的JavaScript表达式,包括对象,并且还支持动态参数,如下所示。
~~~html
<p v-calculate:[arg]="{left:10, right:20}"></p>
<script>
var vm = new Vue({
data: {
arg: "click"
},
directives: {
calculate: {
bind: function(el, binding) {
console.log(binding.arg); //"click"
console.log(binding.value); //{left: 10, right: 20}
}
}
}
});
</script>
~~~
*****
> 原文出处:
[博客园-Vue躬行记](https://www.cnblogs.com/strick/category/1512864.html)
[知乎专栏-Vue躬行记](https://zhuanlan.zhihu.com/pwvue)
已建立一个微信前端交流群,如要进群,请先加微信号freedom20180706或扫描下面的二维码,请求中需注明“看云加群”,在通过请求后就会把你拉进来。还搜集整理了一套[面试资料](https://github.com/pwstrick/daily),欢迎浏览。
![](https://box.kancloud.cn/2e1f8ecf9512ecdd2fcaae8250e7d48a_430x430.jpg =200x200)
推荐一款前端监控脚本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不仅能监控前端的错误、通信、打印等行为,还能计算各类性能参数,包括 FMP、LCP、FP 等。
- ES6
- 1、let和const
- 2、扩展运算符和剩余参数
- 3、解构
- 4、模板字面量
- 5、对象字面量的扩展
- 6、Symbol
- 7、代码模块化
- 8、数字
- 9、字符串
- 10、正则表达式
- 11、对象
- 12、数组
- 13、类型化数组
- 14、函数
- 15、箭头函数和尾调用优化
- 16、Set
- 17、Map
- 18、迭代器
- 19、生成器
- 20、类
- 21、类的继承
- 22、Promise
- 23、Promise的静态方法和应用
- 24、代理和反射
- HTML
- 1、SVG
- 2、WebRTC基础实践
- 3、WebRTC视频通话
- 4、Web音视频基础
- CSS进阶
- 1、CSS基础拾遗
- 2、伪类和伪元素
- 3、CSS属性拾遗
- 4、浮动形状
- 5、渐变
- 6、滤镜
- 7、合成
- 8、裁剪和遮罩
- 9、网格布局
- 10、CSS方法论
- 11、管理后台响应式改造
- React
- 1、函数式编程
- 2、JSX
- 3、组件
- 4、生命周期
- 5、React和DOM
- 6、事件
- 7、表单
- 8、样式
- 9、组件通信
- 10、高阶组件
- 11、Redux基础
- 12、Redux中间件
- 13、React Router
- 14、测试框架
- 15、React Hooks
- 16、React源码分析
- 利器
- 1、npm
- 2、Babel
- 3、webpack基础
- 4、webpack进阶
- 5、Git
- 6、Fiddler
- 7、自制脚手架
- 8、VSCode插件研发
- 9、WebView中的页面调试方法
- Vue.js
- 1、数据绑定
- 2、指令
- 3、样式和表单
- 4、组件
- 5、组件通信
- 6、内容分发
- 7、渲染函数和JSX
- 8、Vue Router
- 9、Vuex
- TypeScript
- 1、数据类型
- 2、接口
- 3、类
- 4、泛型
- 5、类型兼容性
- 6、高级类型
- 7、命名空间
- 8、装饰器
- Node.js
- 1、Buffer、流和EventEmitter
- 2、文件系统和网络
- 3、命令行工具
- 4、自建前端监控系统
- 5、定时任务的调试
- 6、自制短链系统
- 7、定时任务的进化史
- 8、通用接口
- 9、微前端实践
- 10、接口日志查询
- 11、E2E测试
- 12、BFF
- 13、MySQL归档
- 14、压力测试
- 15、活动规则引擎
- 16、活动配置化
- 17、UmiJS版本升级
- 18、半吊子的可视化搭建系统
- 19、KOA源码分析(上)
- 20、KOA源码分析(下)
- 21、花10分钟入门Node.js
- 22、Node环境升级日志
- 23、Worker threads
- 24、低代码
- 25、Web自动化测试
- 26、接口拦截和页面回放实验
- 27、接口管理
- 28、Cypress自动化测试实践
- 29、基于Electron的开播助手
- Node.js精进
- 1、模块化
- 2、异步编程
- 3、流
- 4、事件触发器
- 5、HTTP
- 6、文件
- 7、日志
- 8、错误处理
- 9、性能监控(上)
- 10、性能监控(下)
- 11、Socket.IO
- 12、ElasticSearch
- 监控系统
- 1、SDK
- 2、存储和分析
- 3、性能监控
- 4、内存泄漏
- 5、小程序
- 6、较长的白屏时间
- 7、页面奔溃
- 8、shin-monitor源码分析
- 前端性能精进
- 1、优化方法论之测量
- 2、优化方法论之分析
- 3、浏览器之图像
- 4、浏览器之呈现
- 5、浏览器之JavaScript
- 6、网络
- 7、构建
- 前端体验优化
- 1、概述
- 2、基建
- 3、后端
- 4、数据
- 5、后台
- Web优化
- 1、CSS优化
- 2、JavaScript优化
- 3、图像和网络
- 4、用户体验和工具
- 5、网站优化
- 6、优化闭环实践
- 数据结构与算法
- 1、链表
- 2、栈、队列、散列表和位运算
- 3、二叉树
- 4、二分查找
- 5、回溯算法
- 6、贪心算法
- 7、分治算法
- 8、动态规划
- 程序员之路
- 大学
- 2011年
- 2012年
- 2013年
- 2014年
- 项目反思
- 前端基础学习分享
- 2015年
- 再一次项目反思
- 然并卵
- PC网站CSS分享
- 2016年
- 制造自己的榫卯
- PrimusUI
- 2017年
- 工匠精神
- 2018年
- 2019年
- 前端学习之路分享
- 2020年
- 2021年
- 2022年
- 2023年
- 日志
- 2020