Vue躬行记(2)——指令
Vue不仅内置了各类指令,包括条件渲染、事件处理等,还能注册自定义指令。
一、条件渲染
条件渲染的指令包括v-if、v-else、v-else-if和v-show。
1)v-if
该指令的功能和条件语句中的if类似,可根据表达式的计算结果,判断是否渲染分支中的元素和它所包含的子元素。在下面的示例中,当把数据对象的exist属性设为true时,<div>和<p>两个元素就会被添加到页面的DOM中。
<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>元素。
<template v-if="exist">
<p>strick</p>
<p>freedom</p>
</template>
2)v-else
该指令的功能和条件语句中的else类似,需要与v-if配合使用,并且要紧跟在v-if或v-else-if之后(如下所示),否则该指令将失效。
<p v-if="exist">strick</p>
<p v-else>freedom</p>
3)v-else-if
该指令与v-else类似,也需要紧跟在v-if或v-else-if之后,但它能自定义条件表达式,如下所示。
<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;”,如下所示。
<!-- <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指令传递了两个参数,其中第二个参数表示元素的索引。
<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迭代器的语法,如下所示。
<li v-for="item of array">
{{item}}
</li>
2)对象
当基于对象来渲染一个列表时,v-for指令可迭代对象的属性(即键值对)和其索引。在下面的示例中,为v-for指令传递了三个参数,其中属性值在前,属性名在后。
<li v-for="(value, key, index) in obj">
{{index}} - {{key}} - {{value}}
</li>
3)整数
v-for指令接收的整数,可用于循环次数,如下所示,其中digit从1开始计数。
<li v-for="digit in 3">
{{digit}}
</li>
4)key特性
Vue为元素提供了一个能标识其身份的key特性,利用该特性可让diff算法快速找到变化的节点,并且高效的将其插入到新位置,而不用渲染无变化的元素。可以像下面这样为每个<li>元素设置一个key特性,其值为peoples数组中的元素对象的id属性。
<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重复,从而造成渲染错误。
<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指令进行切换时,其内容都会被清空。
<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()方法,就会将新增的对象渲染到页面中。
vm.peoples.push({id:3, name:"justify"});
对于那些filter()、concat()、slice()等非变异方法,可通过替换数组来触发视图的更新,如下所示。
vm.peoples = vm.peoples.slice(0, 1);
三、事件处理
Vue提供了用于注册事件的v-on指令,它的参数是事件类型,接收的表达式既可以是方法名,也可以是内联语句,并且能与多个修饰符配合使用。
1)v-on
当接收一个方法名时,默认会将事件对象作为第一个参数传递给事件处理程序,例如下面dot()方法中的e参数。
<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变量传递事件对象,并且可声明在形参列表的任意位置,如下所示。
<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:”,如下代码所示,后文的示例都会使用该形式。
<button @click="dot">点击</button>
如果要在同一个事件类型中执行多个事件处理程序,那么可以像下面这样用逗号把两条内联语句隔开。
<button @click="dot1(),dot2()">点击</button>
2)修饰符
v-on支持多种类型的修饰符(包括事件、按键、鼠标等),在事件细节的处理方面,提供了更多的选择,例如可用的事件修饰符如表1所列。
表1 事件修饰符
| 修饰符 | 作用 | 
| .stop | 调用事件对象的stopPropagation()方法 | 
| .prevent | 调用事件对象的preventDefault()方法 | 
| .capture | 采用捕获的事件传播形式 | 
| .self | 只有当事件是由注册的元素触发的,即event.target等于绑定该事件的元素,才执行回调 | 
| .once | 事件只会触发一次 | 
| .passive | 永不调用preventDefault()方法,注意,不能与.prevent一起使用 | 
当串联多个修饰符时,会按顺序影响事件的处理,即不同的顺序会产生不同的效果。下面用一个示例来演示不同排列所造成的差异,其中print()方法用于打印接收的参数,<section>元素添加了.stop和.self两个修饰符。
<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)的修饰符形式。
<input @keyup.arrow-down="enter" />
不仅如此,通过事件对象的keyCode属性得到的建码也能作为修饰符使用,例如ArrowDown的建码是40,可以像下面这样声明。
<input @keyup.40="enter" />
由于键码不便记忆,因此Vue提供了常用键码的别名,例如.enter、.tab、.space、.down等。对于那些不常用的建码,可通过Vue.config.keyCodes自定义,例如为F12键创建别名,如下所示。
Vue.config.keyCodes.f12 = 123;
有一点要注意,keyCode已从Web标准中移除,在未来可能也会被最新的浏览器废弃,因此要慎用该类修饰符。
Vue 2.1.0新增了四个特殊的系统修饰键:.ctrl、.alt、.shift和.meta,其中.meta会随着操作系统的不同而对应不同的键,例如在Mac系统中对应command键,而在Windows系统中对应Windows图标键。与按键修饰符不同,在与keyup事件配合时,系统修饰键必须处于按下状态,否则无法触发事件。以下面的文本框为例,如果要执行回调函数enter(),不仅要按一下ArrowDown键,还得同时按住Ctrl键。
<input @keyup.arrow-down.ctrl="enter" />
Vue 2.5.0新增了一个特殊的.exact修饰符,用于控制系统修饰键的组合。在下面的示例中,第一个文本框中的事件,可通过单独按下Ctrl键或与其它修饰键组合触发;第二个文本框添加了.exact修饰符,只有按下Ctrl键才会触发事件,无法与其它修饰键组合。
<input @keyup.ctrl="enter" />
<input @keyup.ctrl.exact="enter" />
Vue 2.2.0新增了三个用于鼠标事件的修饰符:.left、.right和.middle,分别对应三种鼠标按键状态:左键,右键和中键。
四、自定义指令
由于内置指令无法满足所有场景,因此Vue允许用户注册自定义的指令,以便封装特殊的DOM行为,提升代码复用率。
1)注册
可在实例的directives选项中注册局部指令,例如为文本框自动输入一段字符,如下代码所示。其中autoEnter是指令名称,它的值是一个指令的定义对象,注意,在元素中使用该自定义指令时,不能采用驼峰的方式。
<input v-auto-enter/>
<script>
var vm = new Vue({
directives: {
autoEnter: {
inserted: function(el) {
el.value = "strick";
}
}
}
});
</script>
也可以在创建Vue实例之前,通过Vue.directive()方法注册全局指令,如下所示。
Vue.directive("autoEnter", {
  inserted: function(el) {
    el.value = "strick";
  }
});
var vm = new Vue({...});
2)定义对象
在指令的定义对象中,有5个钩子函数,其使用方法如表2所述。
表2 钩子函数
| 钩子函数 | 调用时机 | 调用次数 | 
| bind() | 指令首次绑定到元素时调用 | 一次 | 
| inserted() | 绑定元素插入到父节点时调用,此时不能保证元素已在DOM中 | 一次 | 
| update() | 绑定该指令的组件更新时调用,其子组件此时可能还未更新,并且绑定值可能有变化,也可能保持不变 | 多次 | 
| componentUpdated() | 绑定该指令的组件及其子组件都完成更新后调用 | 多次 | 
| unbind() | 指令与元素解绑时调用 | 一次 | 
接下来自定义一个v-hooks指令,并为其添加4个钩子函数,作用于<div>元素,如下所示,其中<p>是<div>的子元素。
<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"。
vm.name = "freedom";
此时再修改子元素<p>中的插值(如下代码所示),那么仍然会调用update()函数,只是指令的新值和旧值都是“freedom”。
vm.age = 30;
当通过v-if指令销毁元素时(如下代码所示),就会调用unbind()函数,并输出“unbind”。
vm.display = false;
当bind()和update()触发的行为相同时,Vue允许将指令的定义对象声明成一个函数,如下所示。
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()函数中。
表3 binding对象
| 属性 | 描述 | 
| name | 指令名称,不包含“v-”前缀,例如hooks | 
| rawName | 指令的原始名称,包含“v-”前缀、参数和修饰符,例如v-hooks:click.once | 
| value | 指令接收的值,例如在v-hooks="name"中,读取name属性得到的值为“strick” | 
| oldValue | 指令接收的旧值,即上一个值,只在update()和componentUpdated()函数中可用 | 
| expression | 字符串类型的指令表达式,例如v-hooks="name"中的“name” | 
| arg | 传给指令的参数,例如v-hooks:click中的“click” | 
| oldArg | 传给指令的上一个参数,与oldValue一样,只在update()和componentUpdated()函数中可用 | 
| modifiers | 包含修饰符的对象,例如在v-hooks.once.self中,修饰符对象为{once: true, self: true} | 
自定义指令可接收任意合法的JavaScript表达式,包括对象,并且还支持动态参数,如下所示。
<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躬行记(2)——指令的更多相关文章
- Vue躬行记(3)——样式和表单
		
Vue对DOM元素的class和style两个特性做了专门的增强,即对CSS类和内联样式做了一层封装,通过v-bind指令来处理它们,而接收的表达式既可以是简单的字符串.对象或数组,也可以是复杂的计算 ...
 - Vue躬行记(1)——数据绑定
		
Vue.js的核心是通过基于HTML的模板语法声明式地将数据绑定到DOM结构中,即通过模板将数据显示在页面上,如下所示. <div id="container">{{c ...
 - Vue躬行记(4)——组件
		
组件是可复用的Vue实例,拥有属于自己的数据.模板.脚本和样式,可避免繁重的重复性开发.由于组件都是独立的,因此其内部代码不会影响其它组件,但可以包含其它组件,并且相互之间还能通信. 一.注册 在使用 ...
 - Vue躬行记(5)——组件通信
		
组件之间除了保持独立之外,还需要相互通信,本章将介绍几种通信的方式. 一.直接访问 Vue提供了三个实例属性,可直接访问父组件.子组件和根实例,如下所列. (1)$parent:父组件. (2)$ro ...
 - Vue躬行记(6)——内容分发
		
Vue提供了一种内容分发技术,可将父组件中的内容传递给子组件的模板,实现方式参照了Web组件规范草案. 一.插槽 Vue内置了一个<slot>元素,能作为插槽(slot)存在,而插槽内可包 ...
 - Vue躬行记(7)——渲染函数和JSX
		
除了可通过模板创建HTML之外,Vue还提供了渲染函数和JSX,前者的编码自由度很高,后者对于开发过React的人来说会很熟悉.注意,Vue的模板最终都会被编译成渲染函数. 一.渲染函数 虽然在大部分 ...
 - Vue躬行记(9)——Vuex
		
Vuex是一个专为Vue.js设计的状态管理库,适用于多组件共享状态的场景.Vuex能集中式的存储和维护所有组件的状态,并提供相关规则保证状态的独立性.正确性和可预测性,这不仅让调试变得可追踪,还让代 ...
 - Vue躬行记(8)——Vue Router
		
虽然Vue.js未提供路由功能,但是官方推出了Vue Router(即vue-router库),以插件的形式支持.它与Vue.js深度集成,可快速的创建单页应用(Single Page Applica ...
 - ES6躬行记(1)——let和const
		
古语云:“纸上得来终觉浅,绝知此事要躬行”.的确,不管看了多少本书,如果自己不实践,那么就很难领会其中的精髓.自己研读过许多ES6相关的书籍和资料,平时工作中也会用到,但在用到时经常需要上搜索引擎中查 ...
 
随机推荐
- CodeForces 779D. String Game(二分答案)
			
题目链接:http://codeforces.com/problemset/problem/779/D 题意:有两个字符串一个初始串一个目标串,有t次机会删除初始串的字符问最多操作几次后刚好凑不成目标 ...
 - Java集合:LinkedList (JDK1.8 源码解读)
			
LinkedList介绍 还是和ArrayList同样的套路,顾名思义,linked,那必然是基于链表实现的,链表是一种线性的储存结构,将储存的数据存放在一个存储单元里面,并且这个存储单元里面还维护了 ...
 - Taro框架下qq小程序开发体验
			
qq小程序发布了,作为第一批体验者 .还是发现了和微信小程序很多不同的地方. 最新的小程序我这里都是用Taro开发的,体验较为不错.数据管理用的是redux.JS用的ES6加async等. 微信小程序 ...
 - SVN更新失败
			
一.svn更新失败 使用svn遇到的问题是,更新失败,代码被锁定. 解决办法: 在项目上右键,如图所示: 图一:  图二:  之后再更新,基本上都没有问题了.如果还有问题,看下面. 二.工具清理 ...
 - HTML图片死活不显示
			
图片不显示: 1.路径 2.名称 3.少写了" ... " 正确的例子:“../images/dd.png” 4.多写了一个“/” ,或者少写了一个“ . ” ,没错.不是三个点, ...
 - 【3】Decision tree(决策树)
			
前言 Decision tree is one of the most popular classification tools 它用一个训练数据集学到一个映射,该映射以未知类别的新实例作为输入,输出 ...
 - Spring MVC中返回JSON数据的几种方式
			
我们都知道Spring MVC 的Controller方法中默认可以返回ModeAndView 和String 类型,返回的这两种类型数据是被DispatcherServlet拿来给到视图解析器进行继 ...
 - Go从入门到放弃
			
Go语言介绍 为什么你应该学习Go语言? 开发环境准备 从零开始搭建Go语言开发环境 VS Code配置Go语言开发环境 Go语言基础 Go语言基础之变量和常量 Go语言基础之基本数据类型 Go语言基 ...
 - Redis小白入门系列
			
一.从NoSQL说起 NoSQL 是 Not only SQL 的缩写,大意为"不只是SQL",说明这项技术是传统关系型数据库的补充而非替代.在整个NoSQL技术栈中 MemCac ...
 - GC垃圾收集算法
			
JVM中的垃圾收集算法实现涉及大量的程序细节,而且各个平台的虚拟机操作内存的方法又各不相同,这里介绍几种垃圾收集算法的思想. 1.标记-清除算法 这是最基础的垃圾收集算法,分为“标记”和“清除”两个阶 ...