Vue学习记录(二)
一、指令
指令是Vue.js中一个重要的特性,主要提供了一种机制将数据的变化映射为DOM行为。当数据变化时,指令会依据设定好的操作对DOM进行修改,这样就可以只关注数据的变化,而不用去管理DOM的变化和状态,使得逻辑更加清晰,可维护性更好。
1、内置指令
1.1、v-bind
v-bind主要用于动态绑定DOM元素属性(attribute),即元素属性实际的值由vm实例中的data属性提供的。例:
<img v-bind:src='avatar' /> new Vue({
data: {
avatar: 'http://...'
}
});
v-bind可以简写为:,即可简写为<img :src='avatar' />。
v-bind还拥有三种修饰符,分别为.sync、.once、.camel,作用分别如下:
.sync:用于组件props属性,进行双向绑定,即父组件绑定传递给子组件的值,无论在哪个组件中对其进行了修改,其他组件中的这个值也会随之更新。但一般不推荐子组件直接修改父组件数据,这样会导致耦合且组件内的数据不容易维护。
.once:同.sync一样,用于组件props属性,但进行的是单次绑定。和双向绑定刚好相反,单次绑定是将绑定数据传递给子组件后,子组件单独维护这份数据,和父组件的数据再无关系,父组件的数据发生变化不影响子组件中的数据。
.camel:将绑定的特性名字转回驼峰命名。只能用于普通HTML属性的绑定,通常会用于svg标签下的属性。例:
<svg width='400' height='300' :view-box.camel='viewBox'></svg> 输出结果即为: <svg width='400' height='300' viewBox='...'></svg>
1.2、v-model
v-model指令主要用于input、select、textarea标签中,具有lazy、number、debounce(2.0废除)、trim(2.0新增)这些修饰符。
1.3、v-if/v-else/v-show
v-if/v-else/v-show这三个指令主要用于根据条件展示对应的模板内容。v-if在条件为false的情况下并不进行模板的编译,而v-show则会在模板编译好之后将元素隐藏掉。v-if的切换消耗要比v-show高,但初始条件为false的情况下,v-if的初始渲染要稍快。
1.4、v-for
v-for也是用于模板渲染的指令,在Vue.js 2.0中做了细微调整,大致包含:
1、参数顺序变化
当包含参数index或key时,对象参数修改为(item, index)或(value, key),这样与JSArray对象的新方法forEach和map,以及一些对象迭代器(例如lodash)的参数能保持一致。
2、v-bind:key
属性track-by被b-bind:key代替。
<div v-for="item in items" track-by="id"></div> 需改为 <div v-for="item in items" v-bind:key="item.id"></div>
3、n in 10
v-for="n in 10"中的n由原来的0~9迭代变成0~10迭代。
1.5、v-on
v-on指令主要用于事件绑定。例:
<button v-on:click="onClick"></button> v-on可以简写为: <button @click="onClick"></button>
修饰符包括.stop、.prevent、.capture、.self以及指定按键.{keyCode | keyAlias}。
在Vue.js 2.0中,在组件上使用v-on指令只监听自定义时间,即使用$emit触发的事件;如果要监听原生事件,需要使用修饰符.native,例如<my-component v-on:click.native="onClick"></my-component>。
1.6、v-text
v-text,参数类型为String,作用是更新元素的textContent。{{}}文本插值本身也会被编译成textNode的一个v-text指令。与{{}}不同,v-text需要绑定在某个元素上,能避免未编译前的闪现问题。例:
<span v-text="msg"></span>
1.7、v-HTML
v-HTML,参数类型为String,作用为更新元素的innerHTML,接受的字符串不会进行编译等操作,按普通HTML处理。同v-text类似,{{{}}}插值也会编译为节点的v-HTML指令,v-HTML也需要在某个元素上且能避免编译前闪现问题。例:
<div>{{ HTML }}</div>
<div v-HTML="HTML"></div>
1.8、v-el
v-el指令为DOM元素注册了一个索引,使得我们可以直接访问DOM元素。语法上来说,可以通过所属实例的$els属性调用。例:
<div v-el:demo>there is a el demo</div> vm.$els.demo.innerText // there is a el demo
或者在vm内部通过this进行调用。
另外,由于HTML不区分大小写,在v-el中如果使用了驼峰式命名,系统会自动转成小写。但可以使用“-”来连接你期望大写的字母。例:
<div v-el:camelCase>There is a camelcase</div> <div v-el:camel-case>There is a camelCase</div> vm.$els.camelcase.innerText // There is a camelcase vm.$els.camelCase.innerText // There is a camelCase
1.9、v-ref
v-ref指令与v-el类似,只不过v-ref作用域组件上,实例可以通过$refs访问子组件。命名方式也类似,想使用驼峰式命名的话用“-”来做连接。例:
<message v-ref:title content="title"></message> <message v-ref:sub-title content="subTitle"></message> var Message = Vue.extend({
props: ['content'],
template: '<h1>{{ content }}</h1>'
}); Vue.component('message', Message);
从理论上来说,可以通过父组件对子组件进行任意的操作,但实际上尽量还是会采用props数据绑定,用组件间通信的方式去进行逻辑上的交互,尽量让组件只操作自己内部的数据和状态,如果组件间有通信,也通过调用组件暴露出来的接口进行通信,而不是直接跨组件修改数据。
1.10、v-pre
v-pre指令相对简单,就是跳过编译这个元素和子元素,显示原始的{{}}Mustache标签,用来减少编译时间。例:
<div v-pre>{{ uncompiled }}</div> var vm = new Vue({
el: '#app',
data: {
uncompiled: 'This is an uncompiled element'
}
});
最后输出:
<div>{{ uncompiled }}</div>
1.11、v-cloak
v-cloak指令相当于在元素上添加了一个[v-cloak]的属性,直到关联的实例结束编译。官方推荐可以和css规则[v-cloak]{display: none; }一起使用,可以隐藏未编译的Mustache标签直到实例准备完毕。例:
<div v0cloak>{{ msg }}</div>
1.12、v-once
v-once指令是Vue.js 2.0中新增的内置指令,用于标明元素或组件只渲染一次,即使随后发生绑定数据的变化或更新,该元素或组件及包含的子元素都不会被再次编译和渲染。使用方式:
<span v-once>{{ msg }}</span> <my-component v-once:msg="msg"></my-component>
2、自定义指令基础
除了内置指令外,Vue.js也提供了方法让我们可以注册自定义指令,以便封装对DOM元素的重复处理行为,提高代码复用率。
2.1、指令的注册
通过Vue.directive(id, definition)方法注册一个全局自定义指令,接收参数id和定义对象。id是指令的唯一标识,definition则是指令的相关属性及钩子函数。例:
Vue.directive('global-directive', definition); // 只注册了这个命令,并没有赋予这个指令任何功能
可以在模板中这样使用:
<div v-global-directive></div>
而除了全局注册指令外,我们也可以通过在组件的directives选项注册一个局部的自定义指令。例:
var comp = new Vue({
directives: {
'localDirective': {} // 可以采用驼峰式命名
}
});
该指令就只能在当前组件内通过v-local-directive的方式调用,而取法被其他组件调用。
2.2、指令的定义对象
在注册指令的同时,可以传入definition对象,对指令赋予一些特殊的功能。这个定义对象主要包含三个钩子函数:bind、update和unbind。
bind:只被调用一次,在指令第一次绑定到元素上时调用。
update:指令在bind之后以初始值为参数进行第一次调用,之后每次当绑定值发生变化时调用,update接收到的参数为newValue和oldValue。
unbind:指令从元素上解绑时调用,只调用一次。
<div v-if="isExist" v-my-directive="param"></div> Vue.directive('my-directive', {
bind: function() {
console.log('bind', arguments);
},
update: function(newValue, oldValue) {
console.log('update', newValue, oldValue);
},
unbind: function() {
console.log('unbind', arguments);
}
}); var vm = new Vue({
el: '#app',
data: {
param: 'first',
isExist: true
}
});
在控制台先后输入vm.param = 'second'和vm.isExist = false,整体输出如下:
bind []
update first undefined
vm.param = 'second'
update first second
'second'
vm.isExist = false
unbind []
false // 手打运行结果,具体可自己运行看输出
另外,如果我们只需要使用update函数时,可以直接传入一个函数代替定义对象:
Vue.directive('my-directive', function(value) {
// 该函数即为update函数
});
上述例子中,可以使用my-directive指令绑定的值是data中的param属性。也可以直接绑定字符串常量,或使用字面修饰符,但这样的话需要注意update方法将只调用一次,因为普通字符串不能响应数据变化。例:
<div v-my-directive="constant string"></div> //value为undefined,因为data中没有对应的属性 <div v-my-directive="'constant string'"></div> //value为constant,绑定字符串需要加单引号 <div v-my-directive.literal="constant string"></div> //value为constant,利用字面修饰符后无需使用单引号
除了字符串外,指令也接受对象字面量或任意合法的JavaScript表达式。例:
<div v-my-directive="{ title : 'Vue.js', author: 'You' }"></div>
<div v-my-directive="isExist ? 'yes' : 'no'"></div>
注意此时对象字面量不需要用单引号括起来,这和字符串常量不一样。
2.3、指令实例属性
除了了解指令的生命周期外,还需要知道指令中能调用的相关属性,以便对相关DOM进行操作。在指令的钩子函数内,可以通过this来调用指令实例。
el:指令绑定的元素。
vm:该指令的上下文ViewModel,可以为new Vue()的实例,也可以为组件实例。
expression:指令的表达式,不包括参数和过滤器。
arg:指令的参数。
name:指令的名字,不包括v-前缀。
modifiers:一个对象,包含指令的修饰符。
descriptor:一个对象,包含指令的解析结果。
<duv v-my-msg:console.log="content"></div> Vue.directive('my-msg', {
bind: function() {
console.log('~~~~~~~~bind~~~~~~~~~~~');
console.log('el', this.el);
console.log('name', name);
console.log('vm', this.vm);
console.log('expression', this.expression);
console.log('arg', this.arg);
console.log('modifiers', this.modifiers);
console.log('descriptor', this.descriptor);
},
update: function(newValue, oldValue) {
var keys = Object.keys(this.modifiers);
window[this.arg][keys[0]](newValue);
},
unbind: function() {
}
}); var vm = new Vue({
el: '#app',
data: {
content: 'there is the content'
}
});
2.4、元素指令
元素指令是Vue.js的一种特殊指令,普通指令需要绑定在某个具体的DOM元素上,但元素指令可以单独存在,从使用方式上看更像是一个组件,但本身内部的实例属性和钩子函数是和指令一样的。例:
<div v-my-directive></div> // 普通指令使用方式
<my-directive></my-directive> // 元素指令使用方式
元素指令的注册方式和普通指令类似,也有全局注册和局部注册两种。
Vue.elementDirective(;my-ele-directive'); // 全局注册方式 var Comp = Vue.extend({
... // 省略了其他参数
elementDirective: {
'eleDirective': {}
}
}); Vue.component('comp', Comp);
元素指令不能接受参数或表达式,但可以读取元素特性从而决定行为。而且当编译过程中遇到一个元素指令时,Vue.js将忽略该元素及其子元素,只有元素指令本身才可以操作该元素及其子元素。
Vue.js 2.0中取消了这个特性,推荐使用组件来实现需要的业务。
3、指令的高级选项
Vue.js指令定义对象中除了钩子函数外,还有一些其他选项。
3.1、params
定义对象中可以接受一个params数组,Vue.js编译器将自动提取自定义指令绑定元素上的这些属性。例:
<div v-my-advance-directive a="paramA"></div> Vue.directive('my-advance-directive', {
params: ['a'],
bind: function() {
console.log('params', this.params);
除了直接传入数值外,params支持绑定动态数据,并且可以设定一个watcher监听,但是护具变化时,会调用这个回调函数。例:
<div v-my-advance-directive v-bind:a="a"></div> // 当然可以简写成<div v-my-advance-directive :a="a"></div> Vue.directive('my-advance-directive', {
params: ['a'],
paramWatchers: {
a: function(val, oldVal) {
console.log('watcher:', val, oldVal);
}
},
bind: function() {
console.log('params', this.params);
}
}); var vm = new Vue({
el: '#app',
data: {
a: 'dynamic data'
}
});
输出结果为:
params Object{a: "dynamic data"}
vm.a = 123
watcher:123 dynamic data
123
3.2、deep
当自定义指令作用域一个对象上时,可以使用deep选项来监听对象内部发生的变化。例:
<div v-my-deep-directive="obj"></div>
<div v-my-nodeep-directive="obj"></div> Vue.directive('my-deep-directive', {
deep: true,
update: function(newValue, oldValue) {
console.log('deep', newValue.a.b);
}
}); Vue.directive('my-nodeep-directive', {
update: function(newValue, oldValue) {
console.log('nodeep', newValue.a.b);
}
}); var vm = new Vue({
el: '#app',
data: {
obj: {
a: {
b: 'inner'
}
}
}
});
运行后,在控制台中输入vm.obj.a.b = 'inner changed',只有my-deep-directive调用了update函数,输出了改变后的值。
输出结果为:
deep inner
nodeep inner
vm.obj.a.b = 'inner changed'
deep inner changed
Vue.js 2.0中废弃了该选项。
3.3、twoWay
在自定义指令中,如果需要向Vue实例写回数据,就需要在定义对象中使用twoWay: true,这样可以在指令中使用this.set(value)来写回数据。
<input type="text" v-my-twoway-directive="param" /> Vue.directive('my-twoway-directive', {
twoWay: true,
bind: function() {
this.handler = function() {
console.log('value changed:', this.el.value);
this.set(this.el.value);
}.bind(this)
this.el.addEventListener('input', this.handler)
},
unbind: function() {
this.el.removeEventListener('input', this.handler)
}
}); var vm = new Vue({
el: '#app',
data: {
param: 'first'
}
});
此时在input中输入文字,然后在控制台中输入vm.param即可观察到实例的param属性已被改变。
需要注意的是, 如果没有设定twoWay: true,就在自定义指令中调用this.set(),Vue.js会抛出异常。
3.4、acceptStatement
选项acceptStatement: true可以允许自定义指令接受内联语句,同时update函数接收的值是一个函数,在调用该函数时,它将在所属实例作用域内运行。
<div v-my-directive="i++"></div> Vue.directive('my-directice', {
acceptStatement: true,
update: function(fn) {
}
}); var vm = new Vue({
el:'#app',
data: {
i: 0
}
});
如果在update函数中,运行fn(),则会执行内联语句i++,此时vm.i = 1.但更改vm.i并不会触发update函数。
需要当心的是,如果此时没有设定acceptSatement: true,该指令会陷入一个死循环中。v-my-directive接受到i的值 每次都在变化,会重复调用update函数,最终导致Vue.js抛出异常。
3.5、terminal
选项terminal的作用是阻止Vue.js便利这个元素及其内部元素,并由该指令本身去编译绑定元素及其内部元素。内置的指令v-if和v-for都是terminal指令。
使用terminal选项是一个相对复杂的过程,需要对Vue.js的编译过程有一定的了解。
<div id="modal"></div>
...
<div v-inject:modal>
<h1>header</h1>
<p>body</p>
<p>footer</p>
</div>
var FragmentFactory = Vue.FragmentFactory // Vue.js全局API,用来创造fragment的工厂函数,fragment中包含了具体的scope和DOM元素,可以看成一个独立的组件或者实例。
var remove = Vue.util.remove // Vue.js工具类函数,移除DOM元素 var createAnchor = Vue.util.createAnchor // 创建锚点,锚点在debug模式下是注释节点,非debug模式下是文本节点,主要作用是标记DOM元素的插入和移除 Vue.directive('inject', {
terminal: true,
bind: function() {
var container = document.getElementById(this.arg) // 获取需要注入到的DOM元素
this.anchor = createAnchor('v-inject') // 创建v-inject锚点
container.appendChild(this.anchor) // 锚点挂载到注入节点中
remove(this.el) // 移除指令绑定的元素
var factory = new FragmentFactory(this.vm, this.el) // 创建fragment
this.frag = factory.create(this._host, this._scope, this._frag)
// this._host 用于表示存在内容分发时的父组件
// this._scope 用于表示存在v-for时的作用域
// this._frag 用于表示该指令的父fragment
this.frag.before(this.anchor)
},
unbind: function() {
this.frag.remove()
remove(this.anchor)
}
});
3.6、priority
选项priority即为指定指令的优先级。普通指令默认是1000,terminal指令默认为2000.同一元素上优先级高的指令会比其他指令处理的早一些,相同优先级则按出现顺序依次处理。以下为内置指令的优先级顺序:
export const ON = 700
export const MODEL = 800
export const BIND = 850
export const TRANSITION = 1100
export const EL =1500
export const COMPONENT = 1500
export const PARTIAL = 1750
export const IF = 2100
export const FOR = 2200
export const SLOT = 2300
4、 指令在Vue.js 2.0中的变化
指令在Vue.js2.0中发生了较大的变化。总的来说,Vue.js2.0中的指令功能更为单一,很多和组件重复的功能和作用都进行了删除,指令也更专注于本身作用域的操作,而尽量不去影响指令外的DOM元素及数据。
4.1、新的钩子函数
钩子函数增加了一个componentUpdated,当整个组件都完成了update状态后即所有的DOM都更新后调用该钩子函数,无论指令接受的参数是否发生变化。
4.2、钩子函数实例和参数变化
在Vue.js2.0中取消了指令实例这一概念,即在钩子函数中的this并不能指向指令的相关属性。指令的相关属性均通过参数的形式传递给钩子函数。
Vue.directive('my-directive', {
bind: function(el, binding, vnode) {
console.log('~~~~~~~~~~~~~~~~~bind~~~~~~~~~~');
console.log('el', el);
console.log('binding', binding);
console.log('vnode', vnode);
},
update: function(el, binding, vnode, lodVNode) {
...
},
componentUpdated(el, binding, vnode, oldVNode) {
...
},
unbind: function(el, binding, vnode) {
...
}
});
在Vue.js1.0中的实例中的属性大部分都能在binding中找到,vnode则主要包含了节点的相关信息,有点类似于fragment的作用。
4.3、update函数触发变化
钩子函数update对比Vue.js1.0也有以下两个变化:
(1)指令绑定bind函数执行后不直接调用update函数。
(2)只要组件发生重绘,无论指令接受的值是否发生变化,均会调用update函数。如果需要过滤不必要的更新,则可以使用binding.value == binding.olbValue来判断。
4.4、参数binding对象
钩子函数接受的参数binding对象为不可更改,强行设定binding.value的值并不会引起实际的改动。如果非要通过这种方式进行修改的话,只能通过el直接修改DOM元素。
二、过滤器
Vue.js允许在表达式后面添加可选的过滤器,以管道符表示,例:
{{ message | capitalize }}
过滤器的本质是一个函数,接受管道符前面的值作为初始值,同事也能接受额外的参数,返回值为经过处理后的输出值。多个过滤器也可以进行串联。例:
{{ message | filterA 'arg1' 'arg2' }}
{{ message | filterA | filterB }}
1、过滤器注册
Vue.js提供了全局方法Vue.filter()注册一个自定义过滤器,接受过滤器ID和过滤器函数两个参数。例:
Vue.filter('date', function(value) {
if(!value instanceof Date) return value;
return value.toLocaleDateString();
});
这样注册之后,就可以在vm实力的模板中使用这个过滤器了。
<div>
{{ date | date }}
</div> var vm = new Vue({
el: '#app',
data: {
date: new Date()
}
});
除了初始值之外,过滤器也能接受任意数量的参数。例:
Vue.filter('date', function(value, format) {
var o = {
"M+":value.getMonth() +1, // 月份
"d+":value.getDate(), // 日
"h+":value.getHours(), // 小时
"m+":value.getMinutes(), // 分
"s+":value.getSeconds(), // 秒
}; if(/(y+)/.test(format))
format = format.replace(RegExp.$1, (value.getFullYear() + "").substr(4 - RegExp.$1.length));
for(var k in o)
if(new RegExp("(" + k + ")").test(format))
format = format.replace(RegExp.$1, (RegExp.$1.length == 1)
? (o[k])
: (("00" + o[k]).substr(("" + o[k]).length)));
return format;
});
使用方式即为:
<div>
{{ date | date 'yyyy-MM-dd hh:mm:ss' }} // -> 2018-04-25 10:46:46 即可按格式输出当前时间
</div>
2、双向过滤器
之前提及的过滤器都是在数据输出到视图之前,对数据进行转化显示,但不影响数据本身。Vue.js也提供了在改变视图中数据的值,写回data绑定属性中的过滤器,称为双向过滤器。例:
<input type="text" v-model="price | cents" /> // 该过滤器的作用是处理价钱的转化,一般数据库中保存的单位都为分,避免浮点运算 Vue.filter('cents', {
read: function(value) {
return (value / 100).toFixed(2);
},
write: function(value) {
return value * 100;
}
}); var vm = new Vue({
el: '#app',
data: {
price: 150
}
});
3、动态参数
过滤器除了能接受单引号('')括起来的参数外,也支持接受在vm实例中绑定的数据,称之为动态参数。使用区别就在于不需要单引号将参数括起来。例:
<input type="text" v-model="price" />
<span>{{ data | dynamic price }}</span> Vue.filter('dynamic', function(data, price) {
return date.toLocaleDateString() + ':' + price;
}); var vm = new Vue({
el: '#app',
data: {
date: new Date(),
price: 150
}
});
过滤器中接受到的price参数即为vm.price。
4、过滤器在Vue.js2.0中的变化
(1)取消了所有内置过滤器,即capitalize,json等。建议尽量使用单独的插件来按需假如你所需要的过滤器。
(2)取消了对v-model和v-on的支持,过滤器只能使用在{{}}标签中。
(3)修改了过滤器参数的使用方式,采用函数的形式而不是空格来标记参数。例如:{{ date | date('yyyy-MM-dd') }}。
三、过渡
过渡系统是Vue.js为DOM动画效果提供的一个特性,它能在元素从DOM中插入或移除时触发CSS过渡(transition)和动画(animation),也就是说在DOM元素发生变化时为其添加特定的class类名,从而产生过渡效果。除了CSS过渡外,Vue.js的过渡系统也支持javascript的过渡,通过暴露过渡系统的钩子函数,可以在DOM变化的特定时机对其进行属性的操作,产生动画效果。
1、CSS过渡
1.1、CSS过渡的用法
<div v-if="show" transition="my-startup"></div> var vm = new Vue({
el: '#app',
data: {
show: false
}
});
首先在模板中用transition绑定一个DOM元素,并且使用v-if指令元素先处于未被编译状态。然后在控制台内手动调用vm.show = true,就可以看到DOM元素最后输出为:
<div class="my-startup-transition"></div>
DOM元素完成编译后,过渡系统自动给元素添加了一个my-startup-transition的class类名。为了让这个效果更明显一点,可以提前给这个类名添加一点CSS样式:
.my-startup-transition {
transition: all 1s ease;
width: 100px;
height: 100px;
background: black;
opacity:;
}
此时再重新刷新并手动运行vm.show = true,发现最终样式效果是加载上去了,但并没有出现transition效果。这是由于在编译v-if后,div直接挂载到body并添加my-startup-transition类名这两个过程中浏览器仅进行了一次重绘,这对于div来说并没有产生属性的更新,所以没有执行css transition的效果。为了解决这个问题,Vue.js的过渡系统给元素插入及移除时分别添加了2个类名:*-enter和*-leave,*即为transition绑定的字符串,本例中即为m-startup。所以在上述例子中,还需要添加两个类名样式,即my-startup-enter,my-startup-leave:
.my-startup-enter, .my-startup-leave {
height:;
opacity:;
}
此时再重复之前的操作,就可以看到过渡效果了。需要注意的是,这两个类名的优先级要高于.my-startup-transition,不然被my-startup-transition覆盖后就失效了。
同样,也可以通过CSS的animation属性来实现过渡的效果,例:
<style>
.my-animation-transition {
animation: increase 1s ease 0s 1;
width: 100px;
height: 100px;
background: black;
} .my-animation-enter, .my-animation-leave {
height: 0px;
} @keyframes increase {
from {
height: 0px;
} to {
height: 100px;
}
}
</style>
<div v-if="animation" transition="my-animation">animation</div> var vm = new Vue({
el: '#app',
data: {
animation: false
}
});
同样,更高vm.animation为true后即可看到过渡效果。
除了直接在元素上添加transition = "name"外,Vue.js也支持动态绑定CSS名称,可用于多个元素需要多个过渡效果的场景。例:
<div v-if="show" v-bind:transition="transitionName"></div> // 也可以简写成‘ <div v-if="show" :transition="transitionName"></div> var vm = new Vue({
el: '#app',
data: {
show: false,
transitionName: 'fade'
}
});
Vue.js本身并不提供内置的过渡CSS样式,仅仅是提供了过渡需要使用的加载或移除时机,这样更便于灵活地按需去设计过渡样式。
1.2、CSS过渡钩子函数
Vue.js提供了在插入或DOM元素时类名变化的钩子函数,可以通过Vue.transition('name', {})的方式来执行具体的函数操作。例:
Vue.transition('my-startup', {
beforeEnter: function(el) {
console.log('boforeEnter', el.className);
},
enter: function(el) {
console.log('enter', el.className);
},
afterEnter: function(el) {
console.log('afterEnter', el.className);
},
enterCancelled: function(el) {
console.log('enterCancelled', el.className);
},
beforeEnter: function(el) {
console.log('boforeEnter', el.className);
},
enter: function(el) {
console.log('enter', el.className);
},
afterEnter: function(el) {
console.log('afterEnter', el.className);
},
enterCancelled: function(el) {
console.log('enterCancelled', el.className);
}
在控制台里执行vm.show = true,输出结果如下:
vm.show = true
beforeEnter my-startup-transition
enter my-startup-transition my-startup-enter
true
afterEnter my-startup-transition
这样,我们能很清楚地看到钩子函数执行的顺序以及元素类名的变化。同样的,还可以再次更改vm.show的值置为false,结果如下:
vm.show = false
beforeLeave my-startup-transition
leave my-startup-transition my-startup-leave
false
afterLeave my-startup-transition
由于元素在使用CSS的transition和animation时,系统的流程不完全一样。所以先以transition为例,总结下过渡系统的流程。
当vm.show = true时,
(1)调用beforeEnter函数。
(2)添加enter类名到元素上。
(3)将元素插入到DOM中。
(4)调用enter函数。
(5)强制reflow一次,然后移除enter类名,触发过渡效果。
(6)如果此时元素被删除,则触发enterCancelled函数。
(7)监听transitionend事件,过渡结束后调用afterEnter函数。
当vm.show = false时,
(1)调用beforeLeave函数。
(2)添加v-leave类名,触发过渡效果。
(3)调用leave函数。
(4)如果此时元素被删除,则触发leaveCancelled函数。
(5)监听transitionend事件,删除元素及*-leave类名。
(6)调用afterLeave函数。
如果使用animation作为过渡的话,在DOM插入时,*-enter类名不会立即删除,而是在animationend事件触发时删除。
另外,enter和leave函数都有第二个可选的毁掉参数,用于控制过渡何时结束,而不是监听transitionend和animationend事件,例:
<style>
.my-done-transition {
transition: all 2s ease;
width: 100px;
height: 100px;
background: black;
opacity:;
} .my-done-enter, .my-done-leave {
height:;
opacity:;
}
</style>
Vue.transition('my-done', {
enter: function(el, done) {
this.enterTime = new Date();
setTimeout(done, 500);
},
afterEnter: function(el) {
console.log('afterEnter', new Date() - this.enterTime);
}
}); var vm = new Vue({
el: '#app',
data: {
done: false
}
});
输出结果如下:
vm.done = true
true
afterEnter 500
此时afterEnter函数执行的事件就不是my-done-transition样式中的2s之后,而是done调用的500ms之后。需要注意的是 ,如果在enter和leave中声明了形参done,但没有调用,则不会触发afterEnter函数。
1.3 显示声明过渡类型
Vue.js可以指定过渡元素监听的结束事件的类型,例:
Vue.transition('done-type', {
type: 'animation'
});
此时Vue.js就只监听元素的animationend事件,避免元素上还存在transition时导致的结束事件触发不一样。
1.4 自定义过渡类名
除了使用默认的类名*-enter、*-leave外,Vue.js也允许我们自定义过渡类名,例:
Vue.transition('my-startup', {
enterClass: 'fadeIn',
leaveClass: 'fadeOut'
});
我们可以通过上述钩子函数的例子,观测元素的类名变化:
vm.show = true
beforeEnter my-startup-transition
enter my-startup-transition my-startup-enter
true
afterEnter my-startup-transition vm.show = false
beforeLeave my-startup-transition
leave my-startup-transition my-startup-leave
false
afterLeave my-startup-transition
Vue.js官方推荐了一个CSS动画库,animate.css,配合自定义过渡类名使用,可以达到非常不错的效果。只需要引入一个CSS文件,http://cdn.bootcss.com/animate.css/3.5.2/animate.min.css,就可以使用里面的预设动画。例:
Vue.transition('bounce', {
enterClass: 'bounceIn',
leaveClass: 'bounceOut'
});
<div v-if="animateShow" class="animated" transition="bounce">bounce effect</div>
在使用animate.css时,需要先给元素附上animated类名,然后再添加预设的动效类名,例如上例中的bounceIn、bounceOut,这样就能看到动画效果。这个库提供了多种强调展示(例如弹性、抖动)、渐入渐出、翻转、旋转、放大缩小等效果。所有的效果可以访问官网地址http://daneden.github.io/animate.css/在线观看。
2. JavaScript过渡
Vue.js也可以和一些JavaScript动画库配合使用,这里只需要调用JavaScript钩子函数,而不需要定义CSS样式。transition接受选项css: false,将直接跳过CSS检测,避免CSS规则干扰过渡,而且需要在enter和leave钩子函数中调用done函数,明确过渡结束事件。此处将引入Velocity.js来配合使用JavaScript过渡。
2.1 Velocity.js
Velocity.js是一款搞笑的动画引擎,可以单独使用也可以配合jQuery使用。它拥有和jQuery的animate一样的api接口,但比jQuery在动画处理方面更强大、更流畅,以及模拟了一些现实世界的运动,例如弹性动画等。
Velocity.js可以当做jQuery的插件使用,例:
$element.velocity({
left: "100px"
}, 500, "swing", function() {
console.log("done");
}); $element.velocity({
left: "100px"
}, {
duration: 500,
easing: "swing",
complete: function() {
console.log("done");
}
});
也可以单独使用,例:
var el = document.getElementById(id);
Velocity(el, {
left: '100px'
}, 500, 'swing', done);
2.2 JavaScript过渡使用
可以通过以下方式注册一个自定义的JavaScript过渡:
<style>
.my-velocity-transition {
position: absolute;
top:;
width: 100px;
height: 100px;
background: black;
}
</style>
<div v-if="velocity" transition="my-velocity"></div>
Vue.transition('my-velocity', {
css: false,
enter: fumction(el, done) {
Velocity(el, { left: '100px' }, 500, 'swing', done);
},
enterCancelled: function(el) {
Velocity(el, 'stop');
},
leave: fumction(el, done) {
Velocity(el, { left: '0px' }, 500, 'swing', done);
},
leaveCancelled: function(el) {
Velocity(el, 'stop');
}
});
运行上述代码,在设置vm.velocity = true后,过渡系统即会调用enter钩子函数,通过Velocity对DOM操作展现动画效果,然后强制调用done函数,明确结束过渡效果。
3.过渡系统在Vue.js 2.0中的变化
过渡系统在Vue.js 2.0中也发生了比较大的变化,借鉴了ReactJS CSSTransitionGroup的一些相关设定和命名。
3.1 用法变化
新的过渡系统中取消了v-transition这个指令,新增了名为transition的内置标签,用法变更为:
<transition name="fade">
<div class="content" v-if="show">content</div>
</transition>
transition标签为一个抽象组件,并不会额外渲染一个DOM元素,仅仅是用于包裹过渡元素及触发过渡行为。v-if、v-show等指令仍旧标记在内容元素上,并不会作用于transition标签上。
transition标签能接受的参数与Vue.js 1.0中注册的transition接受的选项类似。
1.name
同v-transition中接受的参数,自动生成对应的name-enter,name-enter-active类名。
2.appear
元素首次渲染的时候是否启用transition,默认值为false。即v-if绑定值初始为true时,首次渲染时是否调用transition效果。在Vue.js 1.0中,v-if如果初始值为true的话,首次渲染时无法使用transition效果的,只有v-show能使用。
3.css
同Vue.js 1.0的CSS选项,如果设置为true,则只监听钩子函数的调用。
4.type
同Vue.js 1.0的type选项,设置监听的CSS动画结束事件的类型。
5.mode
控制过渡插入/移除的先后顺序,主要用于元素切换时。可供选择的值有“out-in”、“in-out”,如果不设置,则同时调用。例:
<transition name="fade" mode="out-in">
<p :key="ok">{{ ok }}</p> // 这里的:key="ok"主要用于强制替换元素,展现出in-out/out-in效果
</transition>
当ok在true和false切换时,mode="out-in"决定先移除<p>false</p>,等过渡结束后,再插入<p>true</p>元素,mode="in-out"则相反。
6.钩子函数
enterClass、leaveClass、enterActiveClass、leaveActiveClass、appearClass、appearActiveClass,可以分别自定义各阶段的class类名。
总的来说,在Vue.js 2.0中我们可以直接使用transition标签并设定其属性来定义一个过渡效果,而不需要像在Vue.js 1.0中通过Vue.transition()语句来定义。例:
Vue学习记录(二)的更多相关文章
- 2.VUE前端框架学习记录二
VUE前端框架学习记录二:Vue核心基础2(完结)文字信息没办法描述清楚,主要看编码实战里面,有附带有一个完整可用的Html页面,有需要的同学到脑图里面自取.脑图地址http://naotu.baid ...
- Material Calendar View 学习记录(二)
Material Calendar View 学习记录(二) github link: material-calendarview; 在学习记录一中简单翻译了该开源项目的README.md文档.接下来 ...
- Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客
==他的博客应该不错,没有细看 Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客 http://blog.csdn.net/u012706811/article/det ...
- JavaScript学习记录二
title: JavaScript学习记录二 toc: true date: 2018-09-13 10:14:53 --<JavaScript高级程序设计(第2版)>学习笔记 要多查阅M ...
- Vue学习记录第一篇——Vue入门基础
前面的话 Vue中文文档写得很好,界面清爽,内容翔实.但文档毕竟不是教程,文档一上来出现了大量的新概念,对于新手而言,并不友好.个人还是比较喜欢类似于<JS高级程序设计>的风格,从浅入深, ...
- Vue学习记录(一)
一.引入js文件(直接采用CDN): http://cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js 二.简单实例: (1)HTML代码: &l ...
- vue学习【二】vue结合axios动态引用echarts
大家好,我是一叶,本篇是vue学习的第二篇,本篇将要讲述vue结合axios动态引用echarts. 在vue中,异步刷新使用的是axios,类似于大家常用的ajax,其实axios已经是vue的第二 ...
- Vue学习记录(二)-打包问题
由于项目需要,vue项目在build打包 之后,希望有一个类似wbeconfig的配置文件.方便判断应用所处的环境.进行相应的逻辑处理. 这边暂时记录一下思路,具体请看友情链接. 方案一:从环境变量下 ...
- [20190618]日常学习记录(二)-flex属性及vue实战
早上在看flex属性,总结一下它的优缺点 为什么使用flex, 她和浮动相比,代码更少.浮动要考虑左浮动右浮动,有时还要去清除浮动.flex一行代码就搞定了. 她更灵活,实现平均分配,根据内容大小分配 ...
随机推荐
- Java单播、广播、多播(组播)---转
一.通信方式分类 在当前的网络通信中有三种通信模式:单播.广播和多播(组播),其中多播出现时间最晚,同时具备单播和广播的优点. 单播:单台主机与单台主机之间的通信 广播:当台主机与网络中的所有主机通信 ...
- 3个问题:MySQL 中 character set 与 collation 的理解;utf8_general_ci 与 utf8_unicode_ci 区别;uft8mb4 默认collation:utf8mb4_0900_ai_ci 的含义
MySQL 中 character set 与 collation 的理解 出处:https://www.cnblogs.com/EasonJim/p/8128196.html 推荐: 编码使用 uf ...
- PHP实现IP访问限制及提交次数的方法详解
一.原理 提交次数是肯定要往数据库里写次数这个数据的,比如用户登陆,当用户出错时就忘数据库写入出错次数1,并且出错时间,再出错写2,当满比如5次时提示不允许再登陆,请明天再试,然后用DateDiff计 ...
- linux centos 安装php的memcache扩展
一.centos6.5 yum安装php的memcache扩展 搜索memcache yum search memcache 有了,现在可以安装了 yum -y install memcached m ...
- .NET设计模式 第二部分 创建型模式(2)—抽象工厂模式(Abstract Factory)
抽象工厂模式(Abstract Factory) ——探索设计模式系列之三 Terrylee,2005年12月12日 概述 在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作:同时由于需求的变 ...
- oracle rac的启动与停止
引言:这写篇文章的出处是因为我的一名学生最近在公司搭建RAC集群,但对其启动与关闭的顺序和原理不是特别清晰,我在教学工作中也发现了很多学员对RAC知识了解甚少,因此我在这里就把RAC里面涉及到的最常用 ...
- EXPLAIN执行计划中要重点关注哪些要素
MySQL的EXPLAIN当然和ORACLE的没法比,不过我们从它输出的结果中,也可以得到很多有用的信息. 总的来说,我们只需要关注结果中的几列: 列名 备注 type 本次查询表联接类型,从这里可以 ...
- VS2012打开项目——已停止工作
VS2012打开项目——已停止工作 解决方法如下: 1. 开始-->所有程序-->Microsoft Visual Studio 2012-->Visual Studio Tools ...
- 利用curl 模拟多线程
所谓多线程就是多个 程序同时运行,单线程:执行一段逻辑,等待完成后 在执行另外一个. 多线程:几个逻辑同时进行处理,不需要相互等待,提高了总的执行时间 接下来就用curl实现多线程 实现逻辑 1. f ...
- 使用R语言-为矩阵(表格)的行列命名
转自:http://www.dataguru.cn/article-2217-1.html R语言中经常进行矩阵(表格)数据的处理,在纷繁复杂的数据中,为其行列定义一个名字变得尤为重要.在处理巨量数据 ...