zoukankan      html  css  js  c++  java
  • 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: 1;
    }

         此时再重新刷新并手动运行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: 0;
      opacity: 0;    
    }

        此时再重复之前的操作,就可以看到过渡效果了。需要注意的是,这两个类名的优先级要高于.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: 1;
        }
    
        .my-done-enter, .my-done-leave {
            height: 0;
            opacity: 0;
        }
    </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: 0;
            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()语句来定义。例:

        

  • 相关阅读:
    模拟测试20190815(已更新)
    [JLOI2015]管道连接
    [BJWC2018]最长上升子序列
    [CQOI2012]局部极小值
    [HNOI2010]Bus 公交线路
    [BZOJ4903/CTSC2017]吉夫特
    [BZOJ3717/PA2014]Pakowanie
    [NOI2015]寿司晚宴
    [BZOJ4145/AMPPZ2014]The Prices
    [Usaco2013 Nov]No Change
  • 原文地址:https://www.cnblogs.com/minozMin/p/8855208.html
Copyright © 2011-2022 走看看