遇见Vue.js

  • 关键词:Vue.js – 读书笔记

二、高级选项

AngularJS提供了几种方法能够将指令内部的隔离作用域同指令外部的作用域进行数据绑定。

Vue.js也允许注册自定义指令。自定义指令提供一种机制将数据的变化映射为DOM行为。

1.params

类似于props,指令参数的名字在javascript中是camelCase风格,在HTML中对于实验kebab-case风格。

2.deep

如果自定义指令使用在一个对象上,当对象内部属性变化时要触发update,则在指令定义对象中指定deep:true。

3.twoWay

如果指令想想Vue实例写回数据,则在指令定义对象中指定twoWay: true。该选项允许在指令中使用this.set(value)。

4.acceptStatement

可以让自定义指令接受内联语句,就想v-on那样。

5.Terminal

Vue通过递归遍历DOM树来编译模块。但是当它遇到terminal指令时会停止遍历这个元素的后代元素。

三、计算属性

1.什么是计算属性

计算属性就是当其依赖属性的值发生变化时,这个属性的值会自动更新,与之相关的DOM部分也会同步自动更新。

    <div id="app">
        <input type="text" v-model="didi"/>
        <input type="text" v-model="family"/>
        <input type="text" v-model="didiFamily"/>
        didi = 
        family = 
        didiFamily = 
        <p>原始字符串: </p>
        <p>计算后反转字符串: </p>
    </div>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                message: 'Runoob',
                didi: 'didi',
                family: 'family'
            },
            computed: {
                // 计算属性的 getter
                reversedMessage: function () {
                    // `this` 指向 vm 实例
                    return this.message.split('').reverse().join('')
                },
                didiFamily: {
                    get: function () {
                        return this.didi + " " + this.family
                    },
                    set: function (newValue) {
                        var names = newValue.split(" ");
                        this.didi = names[0];
                        this.family = names[1];
                    }
                }
            }
        })
    </script>

常见问题:

v-on可以绑定多个方法吗?(可以绑定多种类型的方法,click、focus、change等,如果绑定了多个click事件,那么只会绑定第一个click事件,其他的会被自动忽略)

一个vue实例可以绑定多个element元素吗?(el为实例提供挂载原理,值可以是css选择符或实际的HTML元素或返回HTML元素的函数)

2.计算属性缓存

默认提供了缓存开关,在计算属性对象中指定cache字段来控制是否开启缓存(默认为true)。

区别在于,设置cache为false关闭缓存之后,每次直接访问vm.example都会重新执行getter方法。

计算属性getter不执行的场景:

当包含计算属性的节点被移除并且模板中其他地方没有再应用该属性时,那么对应的计算属性的getter方法不会执行。

v-repeat,对于JSON数据集合,我们需要对单条数据应用计算属性。

3.表单控件绑定

v-model指令同步用户输入的数据到vue实例data属性中,同时会对radio,checkbox,select等原生表单组件提供一些语法糖使表单操作更加容易。

v-model用来在视图与Model之间同步数据,有时候我们需要控制同步发生的时机。

lazy源码:

    var lazy = this.params.lazy;
    //....
    this.on('change', this.rawListener)
    if(!lazy){
        this.on('input', this.listener);
    }

debounce和filter中的debounce原理相似,都是用同一个函数,核心就是setTimeout,只是debounce没有默认的wait值。

debounce源码:

    var debounce = this.params.debounce;
    //....
    this.listener = this.rawListener = function(){
        //....
        self.set(val);
        //....
    };
    if(debounce){
        this.listener = _debounce(this.listener, debounce);
    }
    //==========================
    function debounce(func, wait){
        var timeout, args, context, timestamp, result;
        var later = function(){
            var last = Date.now() - timestamp;
            if(last < wait && last >= 0){
                timeout = setTimeout(later, wait - last)
            }else{
                timeout = null;
                result = func.apply(context, args);
                if(!timeout){
                    context = args = null;
                }
            }
        }
        return function(){
            context = this;
            args = arguments;
            timestamp = Date.now();
            if(!timeout){
                timeout = setTimeout(later, wait);
            }
            return result;
        }
    }

Number源码:

    function toNumber(value){
        if(typeof value !== 'string'){
            return value;
        }else{
            var parsed = Number(value);
            return isNAN(parsed) ? value : parsed;
        }
    }

四、过滤器

过滤器,本质上都是函数。其作用在于用户输入数据后,它能够进行处理,并返回一个数据结果。语法就是使用管道符(|)进行连接。

image

    abc
    //内置的过滤器
    // uppercase 全部大写
    fuction uppercase(value){
        return (value || value === 0) ? value.toString().toUppercase() : '';
    }
    // capitalize 首字母大写
    function capitalize(value){
        if(!value && value !== 0) return '';
        value = value.toString();
        return value.charAt(0).toUpperCase() + value.slice[1];
    }
    // lowercase 全部小写
    // JSON 参数(缩进距离)
    // limitBy 10   显示10个元素
    // limitBy 10 5  显示第5到15个元素
    // filterBy
    <div v-for="item in items | filterBy 'hello'"></div>
    <div v-for="item in items | filterBy 'hello' in 'name'"></div>
    <div v-for="item in items | filterBy 'hello' in 'name' 'nickname'"></div>
    // currency 将数字值转换为货币的形式输出
    12345
    // $12,345.000

1.自定义过滤器

    //在Model数据输出到View层之前进行数据转化的。
    Vue.filter("reverse", function(value){
        return value.split('').reverse().join('');
    });
    Vue.filter("wrap", function(value, begin, end){
        return begin + value + end;
    });
    <span v-text="message | wrap 'before' 'after'"></span>
    'hello' -> 'before hello after'
    //实际上,Vue.js还支持把来自视图(input元素)的值在写回模型前进行转化,即双向过滤器。
    Vue.filter(id, {
        // model -> view
        // read可选
        read: function(value){},
        // view -> model
        //write函数将在数据被写入Model之前调用
        //两个参数分别为表达式的新值和旧值
        write: function(newVal, oldVal){}
    });

2.filter注意事项

需要给定过滤器一个唯一标识。如果后注册的过滤器和之前的过滤器冲突,则之前注册的过滤器层被覆盖。

过滤器函数的作用是输入表达式的值,经过处理后输出。所以函数最好返回有意义的值。

3.源码解析(管道实现)

image

管道实现的原理:Vue.prototype.$eval接受类型为String的后跟过滤器及其参数的表达式(‘message | filterExample args1’)。

运用正则表达式检测传入的字符串中是否存在管道符(|),如果存在,则调用parseDirective函数将传入的字符串整理输出为一个对象。

    //比如 'a+1 | uppercase'经过parseDirective处理后:
    {
        expression: 'a + 1',
        filters: [
            {name: 'uppercase', args: null}
        ]
    }
    //在得到parseDirective处理好的对象之后,Vue.prototype.$eval将调用Vue.prototype._applyFilters函数。

parseDirective调用了pushFilter和processFilterArg两个函数。

pushFilter是将一个过滤器函数加入对象内的filters数组中。

processFilterArg是检查一个参数是否为动态参数,并且去掉静态参数上的引号。

Vue.prototype._applyFilters将表达式的值作为参数输入并依次调用filters数组中的过滤器函数,最后输出经过所有过滤器处理的数据结构,达到链式调用过滤器的效果。

4.常见问题

在使用filterBy/orderBy过滤后,$index将会根据表达式数组或对象过滤后的值进行索引。

filter可以写在全局Vue下,也可以写在实例当中,比如与el、data同级写filters。

五、Class与Style绑定

1.Class对象语法和数组语法

    <div id="example" class="static" v-bind:class="{'classA': isRipe, 'classB': isNotRipe}"></div>
    <script>
        var vmClass = new Vue({
            el: 'example',
            data: {
                isRipe: true,
                isNotRipe: false
            }
        });
    </script>
    //渲染为:<div id="example" class="static classA"></div>
    //或者利用计算属性
    <div id="example" class="static" v-bind:class="ddfe"></div>
    <script>
        var vmClass = new Vue({
            el: 'example',
            data: {
                didiage: 4,
                didiMember: 6000
            },
            computed: {
                ddfe: function(){
                    return {
                        'didiAge': this.didiAge > 3 ? true: false,
                        'didiMember': this.didiAge > 1000 ? true: false
                    }
                }
            }
        });
    </script>
    //数组语法
    <div id="example" class="static" v-bind:class="[classA, classB]"></div>

2.绑定内联样式-对象语法和数组语法

    //对象语法
    <div id="example" class="static" v-bind:style="{color: didiColor, fontSize: fontSize + 'px'}"></div>
    <script>
        var vmClass = new Vue({
            el: 'example',
            data: {
                didiColor: 'orange',
                fontSize: 30
            }
        });
    </script>
    //数组语法
    <div id="example" class="static" v-bind:style="[styleA, styleB]"></div>

自动添加前缀

image

五、过渡

JavaScript钩子函数,以下为简单示例:

image

1.内置Class类名

Vue.js内置了一套过渡系统,可以在源从DOM中插入或移除时自动应用过渡效果。Vue.js在适当的时机触发CSS过渡或动画,用户也可以提供相应的JavaScript钩子函数在过渡过程中执行自定义DOM操作。

    //应用过渡效果,需要在目标元素上使用transition特性
    <div v-if="show" transition="my-transition"></div>

    //transition特性可以与一下资源一起搭配使用
    //v-if
    //v-show
    //v-for(只在插入和删除时触发,使用vue-animated-list插件)
    //动态组件
    //在组件的根节点上,并且被Vue实例的DOM方法触发

当插入或者删除带有transition特性的元素时,Vue.js将执行以下操作:

尝试以ID:”my-transitions”查找JavaScript过渡钩子对象,该对象通过Vue.transition(id, hooks)或transitions选项注册。如果找到了,将在过渡的不同阶段调用相应的钩子。

自动嗅探目标元素是否有CSS过渡或动画,并在合适时添加/删除CSS类名,免去了用户自己进行相关操作的繁琐。

如果没有找到JavaScript钩子并且也没有检测到CSS过渡/动画,DOM操作将在下一帧中立即执行。

    //expand-enter定义进入的开始状态。只应用一帧,然后立即删除。
    //expand-leave定义离开的结束状态。在离开过渡开始时生效,在它结束后删除。
    //expand-transition始终保留在元素上
    <style type="text/css">
        .expand-transition {
            transition: all .3s ease;
            height: 30px;
            padding: 10px;
            background-color: #eee;
            overflow: hidden;
        }
        .expand-enter, .expand-leave {
            height: 0;
            padding: 0 10px;
            opacity: 0;
        }
    </style>
    //可以在同一个元素上通过动态绑定实现不同的过渡
    <div v-if="show" :transition="transitionName"></div>

值得注意的是,如果transition没有指定值,即id为空,则使用默认类名:.v-transition/.v-enter/.vleave

image

2.自定义CSS类名

    <div v-show="ok" class="animated" transition="bounce"></div>
    <script>
        Vue.transition('bounce', {
           enterClass: 'bounceInLeft',
           leaveClass: 'bounceOutRight'
        });
    </script>

3.过渡流程

Vue.js内部是如何处理过渡的:

    <div v-show="show" transition="">Transition example</div>

当show属性改变时,Vue.js将相应地插入或删除<div>元素,按照如下规则改变过渡的CSS类名(以下每一项在用户没有设置时都将会跳过):

image

另外,如果进入过渡还在进行中时删除元素,将调用enterCancelled钩子,以清理变动或enter创建的计时器;反之,对于离开过渡也是如此。

当多个元素一起过渡时,Vue.js会批量处理,只强制一次布局。

4.JavaScript过渡

当只使用JavaScript过渡时,enter和leave钩子需要调用done回调,否则它们将被同步调用,过渡将立即结束。

使用Jquery来注册一个自动以的JavaScript过渡,代码如下:

    Vue.transition('fade', {
        css: false,
        enter: function (el, done) {
            //元素已被插入DOM中
            //在动画结束后调用done
            $(el)
                .css('opacity', 0)
                .animate({opacity: 1}, 1000, done)
        },
        enterCancelled: function(el) {
            $(el).stop();
        },
        leave: function (el, done) {
            //与enter相同
            $(el).animate({opacity: 0}, 1000, done)
        },
        leaveCancellde: function(el) {
            $(el).stop();
        }
    });
    //=========================================
    //完整地注册一个JavaScript过渡的代码如下:
    <p transition="fade"></p>
    Vue.transition(ID, {
        enter: function(){},
        leave: function(){}
    })

Vue.transition方法接受两个参数,其中一个参数是过渡ID,另一个参数是一个对象hooks:

image

5.渐进过渡

transition与v-for一起使用时可以创建渐进过渡,即让v-for中的每个过渡项目可以依次产生过渡效果,而不是一次性同步产生过渡效果。

给过渡元素添加一个特性:

    .myStaggeredTransition-transition{
        transition: all .5s ease;
        overflow: hidden;
        margin: 0;
        height: 20px;
    }
    .myStaggeredTransition-enter .myStaggeredTransition-leave{

    }

    <div v-for="item in list" transition="myStaggeredTransition" stagger="100"></div>

    //或者提供一个钩子stagger、enter-stagger或leave-stagger,以便更好地控制。
    Vue.transition('myStaggeredTransition', {
        stagger: function (index) {
            //每个过渡项目增加50ms延时
            //但是最大延时限制为300ms
            return Math.min(300, index * 50)
        }
    });