Vue.js快速入门与专业应用

第1章--Vue.js简介

1、有关于Vue.js特性的例子css

1.第一个特性是数据绑定。绑定用户输入的数据,视图会随着用户的输入而变化。前端

<div id="app">
    <h1>Your input is {{message}}</h1>
    <input type="text" v-model="message">
</div>

2.第二个特性是组件化,简单来讲咱们能够本身定义HTML标签,并在模板中使用它。vue

<div id="app">
    <message content="Hello World"></message>
</div>
<script>
    var Message = Vue.extend({
        props: ['content'],
        template: '<h1>{{content}}</h1>'
    })
    Vue.component('message', Message);
    new Vue({
        el: '#app',
    })
</script>
HTML结果为:
    <div id="app">
        <h1>Hello World</h1>
    </div>

第2章--基础特性

1、数据node

Vue.js实例中能够经过data属性定义数据,若是传入data的是一个对象,Vue实例会代理起data对象里的全部属性,而不会对传入的对象进行深拷贝。另外,咱们能够引用Vue实例vm中的$data来获取声明的数据,例如:webpack

var data = {
    a: 1
};
var vm = new Vue({
    data: data
});
console.log(vm.$data === data); // true
console.log(vm.$data.a === data.a); // true
console.log(vm.a === data.a); // true
// 设置属性也会影响到原始数据
vm.a = 2;
console.log(data.a);    // 2
// 反之亦然
data.a = 3;
console.log(vm.a);  // 3

在模板中使用{{a}}就会输出vm.a的值,而且修改vm.a的值,模板中的值会随之改变。咱们称这个数据为响应式数据。
注意:只有初始化时传入的对象才是响应式的,即在生命完实例后,再加上vm.$data.b = '2',并在模板中使用{{b}},是不会输出字符串'2'的。例如:web

<div id="app">
    <p>{{a}}</p>
    <p>{{b}}</p>
</div>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            a: 1
        }
    });
    vm.$data.b = 2; // 报错
</script>

若是须要在实例化以后加入响应式变量,须要调用实例方法$set,例如:vm.$set('b',2);不过Vue.js并不推荐这么作,会抛出异常。因此咱们应尽可能在初始化的时候,把全部的变量都设定好,若是没有值,能够用undefined或null占位。
另外,组件类型的实例也能够经过props获取数据,同data同样,也须要在初始化时预设好。例如:ajax

<div id="app">
    <my-component title="myTitle" content="myContent"></my-component>
</div>
<script>
    var MyComponent = Vue.component('my-component', {
        props: ['title', 'content'],
        template: '<h1>{{title}}</h1><p>{{content}}</p>'
    });
</script>
也能够在上述组件类型实例中同时使用data,但需注意:
1.data的值必须是一个函数,而且返回值不是原始对象。若是传给组件的data是一个原始对象,则在建议多个组件实例时它们就会共用这个data对象,修改其中一个组件实例的数据就会影响到其余组件实例的数据,这显然不是咱们所指望的。
2.data中的属性和props中的不能重名。
这二者均会抛出异常。
正确使用方法:
var MyComponent = Vue.component('my-component', {
        props: ['title', 'content'],
        data: function () {
            return {
                desc: '描述'
            }
        },
        template: `<div>
                        <h1>{{title}}</h1>
                        <p>{{content}}</p>
                        <p>{{desc}}</p>
                    </div>`
    });

2、生命周期
经过一个简单的demo来清楚地了解内部的运行机制,代码以下:正则表达式

var vm = new Vue({
    el: '#app',
    init: function () {
        // 在实例开始初始化时同步调用。此时数据观测、事件等都还没有初始化。2.0中改名为beforeCreate
        console.log('init');
    },
    created: function () {
        // 在实例建立以后调用。此时已完成数据绑定、事件方法,但还没有开始DOM编译,即未挂载到document中。
        console.log('created');
    },
    beforeCompile: function () {
        // 在DOM编译前调用。2.0废弃了该方法,推荐使用created。
        console.log('beforeCompile');
    },
    beforeMount: function () {
        // 2.0新增的生命周期钩子,在mounted以前运行。
        console.log('beforeMount');
    },
    compiled: function () {
        // 在编译结束时调用。此时全部指令已生效,数据变化已能触发DOM更新,但不保证$el已插入文档。2.0中改名为mounted。
        console.log('compiled');
    },
    mounted: function () {
        // 2.0中compiled改名。
        console.log('mounted');
    },
    attached: function () {
        // 在vm.$el插入DOM时调用,ready会在第一次attached后调用。操做$el必须使用指令或实例方法(例如$appendTo()),直接操做$el不会触发这个钩子。2.0废弃了该方法,推荐在其它钩子中自定义方法检查是否已挂载。
        console.log('attached');
    },
    dettached: function () {
        // 同attached相似,该钩子在vm.$el从DOM删除时调用,并且必须是指令或实例方法。2.0中一样废弃了该方法。
        console.log('dettached');
    },
    beforeDestroy: function () {
        // 在开始销毁实例时调用,此时实例仍然有效。
        console.log('beforeDestory');
    },
    destroyed: function () {
        // 在实例被销毁以后调用。此时全部绑定和实例指令都已经解绑,子实例也被销毁。
        console.log('destroyed');
    },
    ready: function () {
        // 在编译结束和$el第一次插入文档以后调用。2.0废弃了该方法,推荐使用mounted。这个变化其实已经改变了ready这个生命周期状态,至关于取消了在$el首次插入文档后的钩子函数。
        console.log('ready');
        // 组件完成后调用$destroy()函数,进行销毁
        this.$destroy();
    },
    beforeUpdate: function () {
        // 2.0新增的生命周期钩子,在实例挂载以后,再次更新实例(例如更新data)时会调用该方法,此时还没有更新DOM结构。
        console.log('beforeUpdate');
    },
    update: function () {
        // 2.0新增的生命周期钩子,在实例挂载以后,再次更新实例并更新完DOM结构后调用。
        console.log('update');
    },
    activated: function () {
        // 2.0新增的生命周期钩子,须要配合动态组件keep-live属性使用。在动态组件初始化渲染的过程当中调用该方法。
        console.log('activated');
    },
    deactivated: function () {
        // 2.0新增的生命周期钩子,须要配合动态组件keep-live属性使用。在动态组件移出的过程当中调用该方法。
        console.log('deactivated');
    }
});

3、数据绑定
Vue.js做为数据驱动视图的框架,核心是一个响应式的数据绑定系统,创建绑定后,DOM将和数据保持同步,这样就无需手动维护DOM,使代码能给更加简洁易懂、提高效率。
数据绑定语法:
js代码:express

var vm = new Vue({
    el: '#app',
    data: {
        id: 1,
        index: 0,
        name: 'Vue',
        avatar: 'http://...',
        count: [1, 2, 3, 4, 5],
        names: ['Vue1.0', 'Vue2.0'],
        items: [
            {name: 'Vue1.0', version: '1.0'},
            {name: 'Vue2.0', version: '2.0'}
        ]
    }
});

1.文本插值npm

<span>Hello {{name}}</span>    // Hello Vue

修改数据对象中的name属性,DOM也会随之更新。// console中修改name属性为Vue2.0后输出 Hello Vue2.0
模板语法同时也支持单次插值,即首次赋值后再更改vm实例属性值不会引发DOM变化,例如:

<span>Hello {{* name}}</span>

Vue.js 2.0去除了{{*}}这种写法,采用v-once代替。

<span>Hello {{* name}}</span>    // console中修改name属性后依然输出 Hello Vue

2.HTML属性

Mustache标签也一样适用于HTML属性中,例如:

<div id="id-{{id}}"></div>    // <div id="id-1"></div>

Vue.js 2.0废弃了这种写法,用v-bind指令代替。

<div v-bind:id="'id-' + id"></div>    // <div id="id-1"></div>
或简写为:
<div :id="'id-' + id"></div>    // <div id="id-1"></div>

3.绑定表达式

放在Mustache标签内的文本内容称为绑定表达式。除了直接输出属性值以外,一段绑定表达式能够由一个简单的JavaScript表达式和可选的一个或多个过滤器构成。例如:

<div>{{index + 1}}</div>    // <div>1</div>
<div>{{index == 0 ? 'a' : 'b'}}</div>    // <div>a</div>    
<div>{{name.split('').join('|')}}</div>    // <div>V|u|e</div>

每一个绑定中只能包含单个表达式,并不支持JavaScript语句,不然Vue.js就会抛出warning异常。而且绑定表达式里不支持正则表达式,若是须要进行复杂的转换,可使用过滤器或者计算属性来进行处理。

4.过滤器

Vue.js容许在表达式后添加可选的过滤器,以管道符""指示。示例:

<div>{{name | uppercase}}</div>    // VUE

Vue.js将name的值传入给uppercase这个过滤器中(本质是一个函数),返回字符串中的大写值。同时也容许多个过滤器链式使用。例如:

<div>{{name | filterA | filterB}}</div>    // VUE

也容许入多个参数,例如:

<div>{{name | filterA arg1 arg2}}</div>    // VUE

注意:Vue.js 2.0中已经去除了内置的过滤器,不过能够声明自定义过滤器。

5.指令

修饰符:修饰符(Modifiers)是以半角句号.开始的特殊后缀,用于表示指令应该以特殊方式绑定。

<button v-on:click.stop="doClick"></button>

v-on的做用是在对应的DOM元素上绑定事件监听器,doClick为函数名,而stop即为修饰符,做用是中止冒泡,至关于调用了e.stopPropagation()。

2、计算属性

在项目开发中,展现的数据每每须要通过一些处理。除了在模板中绑定表达式或者利用过滤器外,Vue.js还提供了计算属性方法。

1.基础例子

<div id="app">
    <div>{{firstName}}</div> 
    <div>{{lastName}}</div> 
    <div>{{fullName}}</div>
</div>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            firstName: 'Gavin',
            lastName: 'CLY'
        },
        computed: {
            fullName: function () {
                return this.firstName + ' ' + this.lastName
            }
        }
    });
</script>
输出结果:
Gavin
CLY
Gavin CLY

对vm.firstName和vm.lastName进行修改,始终会影响vm.fulllName。

2.Setter
计算属性的Setter方法,在更新属性时带来了便利。例如:

<div id="app">
    <div>&yen;{{price}}</div> 
</div>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            cents: 100
        },
        computed: {
            price: {
                set: function (newValue) {
                    this.cents = newValue * 100;
                },
                get: function () {
                    return (this.cents / 100).toFixed(2);
                }
            }
        }
    });
</script>
输出结果:¥1.00

在处理商品价格的时候,后端每每会把价钱定义成以分为单位的整型,避免在处理浮点类型数据时产生的问题。而前端则须要把价钱再转成元进行展现,若是须要对价钱进行修改,要把输入的价格再恢复到分传给后端,很繁琐。使用Vue.js的计算属性,能够将vm.cents设置为后端所存的数据,计算属性price为前端展现和更新的数据。
此时更改vm.price=2,vm.cents会被更新为200,在传递给后端时无需再手动转化一遍数据。

3、表单控件

Vue.js中提供v-model指令对表单元素进行双向数据绑定。
js代码:

var vm = new Vue({
    el: '#app',
    data: {
        message: '',
        gender: '',
        checked: '',
        multiChecked: [],
        selected: '',
        multiSelected: [],
        a: 'checked',
        b: 'checked'
    }
});

1.Text 输入框示例,用户输入的内容和vm.message直接绑定:

<input type="text" v-model="message"> 
<div>Your input is : {{message}}</div>

图片描述

2.Radio 单选框示例:

<label><input type="radio" value="male" v-model="gender">男</label>
<label><input type="radio" value="female" v-model="gender">女</label> 
<div>{{gender}}</div>

gener值即为选择的radio元素的value值。

3.Checkbox 分两种状况:单个勾选框和多个勾选框。
单个勾选框,v-model即为布尔值,此时input的value值并不影响v-model的值。

<input type="checkbox" v-model="checked">
<div>checked: {{ checked }}</div>

图片描述

多个勾选框,v-model使用相同的属性名称,且属性为数组。

<label><input type="checkbox" value="1" v-model="multiChecked">1</label>
<label><input type="checkbox" value="2" v-model="multiChecked">2</label> 
<label><input type="checkbox" value="3" v-model="multiChecked">3</label> 
<div>multiChecked: {{ multiChecked }}</div>

图片描述

请输入代码

4.Select 同checkbox元素同样,也分单选和多选两种,多选的时候也须要绑定到一个数组。
单选:

<select v-model="selected">
    <option>A</option>
    <option>B</option>
    <option>C</option>
</select>
<div>selected: {{ selected }}</div>

图片描述

多选:

<select v-model="multiSelected" multiple>
    <option>A</option>
    <option>B</option>
    <option>C</option>
</select>
<div>multiSelected: {{ multiSelected }}</div>

图片描述

注意:multiple 属性规定可同时选择多个选项。
在不一样操做系统中,选择多个选项的差别:

对于 windows:按住 Ctrl 按钮来选择多个选项
对于 Mac:按住 command 按钮来选择多个选项

因为上述差别的存在,同时因为须要告知用户可使用多项选择,对用户更友好的方式是使用复选框。
提示:能够把 multiple 属性与 size 属性配合使用,来定义可见选项的数目。

5.参数特性
默认状况下,v-model在input事件中同步输入框值与数据,加lazy属性后会在change事件中同步。number会自动将用户输入转为Number类型,若是原值转换结果为NaN则返回原值。不过Vue.js 2.0中取消了lazy和number做为参数,用修饰符来代替。新增了trim修饰符,去掉输入值首尾空格。

<input type="text" v-model.lazy="message" @change="changeFn">
<input type="text" v-model.number="message">
<input type="text" v-model.trim="message">

4、模板渲染

1.先后端渲染对比:两种方式各有本身的优缺点,须要根据本身的业务场景来选择技术方案。
前端渲染的优势在于:
① 业务分离,后端只须要提供数据接口,前端在开发时也不须要部署对应的后端环境,经过一些代理服务器工具就能远程获取后端数据进行开发,可以提高开发效率。
② 计算量转移,本来须要后端渲染的任务转移给了前端,减轻了服务器的压力。
后端渲染的优势在于:
① 对搜索引擎友好。
② 首页加载时间短,后端渲染加载完成后就直接显示HTML,但前端渲染在加载完成后还须要有段js渲染的时间。
Vue.js 2.0开始支持服务端渲染,从而让开发者在使用上有了更多的选择。

2.v-if vs v-show
当v-if和v-show的条件发生变化时,v-if引发了dom操做级别的变化,而v-show仅发生了样式的变化,从切换的角度考虑v-show消耗的性能要比v-if小。
除此以外,v-if切换时,Vue.js会有一个局部编译/卸载的过程,由于v-if中的模板也可能包括数据绑定或子组件。v-if会确保条件块在切换当中适当地销毁与中间内部的事件监听器和子组件。并且v-if是惰性的,若是在初始化条件为假时,v-if自己什么都不会作,而v-show则仍会进行正常的操做,而后吧css样式设置为display:none。
总的来讲,v-if有更高的切换消耗,而v-show有更高的初始渲染消耗,咱们须要根据实际的使用场景来选择合适的指令。

3.列表渲染v-for指令
Vue.js对data中数组的原生方法进行了封装,因此在改变数时能触发视图更新,但如下两种状况是没法触发视图更新的:
① 经过索引直接修改数组元素,例如 vm.items[0] = { title: 'title-changed' };

对于这种状况,Vue.js提供了$set方法,在修改数据的同时进行视图更新,能够写成:vm.items.$set(0, {title: 'title-changed'}) 或者 vm.$set('items[0]', {title: 'title-changed'}),两种方式皆能够达到效果。

② 没法直接修改“修改数组”的长度,例如 vm.items.length = 0;

在列表渲染的时候,有个性能方面的小技巧,若是数组中有惟一标识id,经过track-by给数组设定惟一标识,这样Vue.js在渲染过程当中会尽可能复用原有对象的做用域及DOM元素。

<li v-for="item in items" track-by="id"></li>

track-by已经替换为key,它的工做方式与其余属性同样,没有v-bind或者:前缀,它会被做为一个字符串处理。多数状况下,须要使用具备完整表达式的动态绑定来替换静态的key。如今应写为:

<li v-for="item in items" v-bind:key="item.id"></li>

5、事件绑定与监听

1.在直接绑定methods函数和内联JavaScript语句时,都有可能须要获取原生DOM事件对象,如下两种方式均可以获取:

<div id="app">
    <button v-on:click="showEvent">showEvent</button>
    <button v-on:click="showEvent($event)">showEvent</button>
    <!--下面这样写获取不到event showEvent方法中输出undefined-->
    <button v-on:click="showEvent()">showEvent</button>
</div>
<script>
    var vm = new Vue({
        el: '#app',         
        methods: {
            showEvent(event) {
                console.log(event);
            }
        }
    });
</script>

同一个元素上也能够经过v-on绑定多个相同事件函数,执行顺序为顺序执行,例如:

<div v-on:click="sayFrom('first')" v-on:click="sayFrom('second')">say</div>    // 执行参数为first的

2.修饰符
Vue.js为指令v-on提供了多个修饰符,方便出来一些DOM时间的细节,而且修饰符能够串联使用。主要修饰符以下:
.stop 等同于调用event.stopPropagation()
.prevent 等同于调用event.preventDefault()
.capture 使用capture模式添加事件监听器
.self 只当事件是从监听元素自己触发时才触发回调
使用方式以下:

<a v-on:click.stop="doThis">say</a>
<!--阻止表单默认提交事件-->
<form v-on:submit.prevent="onSubmit"></form>
<!--阻止默认提交事件且阻止冒泡-->
<form v-on:submit.stop.prevent="onSubmit"></form>
<!--也能够只有修饰符,并不绑定事件-->
<form v-on:submit.stop.prevent></form>

尝试运行如下这个例子,更好地理解修饰符的做用。

<div id="app">
    <!--只点击红色div,弹出'click from inner',点击蓝色div,弹出'click from inner'和'click from self'-->
    <div v-on:click="saySelf('click from inner')" v-on:click.self="saySelf('click from self')" style="width:600px;border:1px solid blue;">
        <div style="width:100px;border:1px solid red;">div</div>
    </div>
    <!--点击button 触发button以及父元素div的saySelf事件 弹出'just button click'和'div'-->
    <div v-on:click="saySelf('div')">
        <button v-on:click="saySelf('button click')">button</button>
    </div>
    <!--点击button 触发button的saySelf事件 弹出'just button click'-->
    <div v-on:click="saySelf('div')">
        <button v-on:click.stop="saySelf('just button click')">button</button>
    </div>
</div>
<script>
    var vm = new Vue({
        el: '#app',       
        methods: {
            saySelf(msg) {
                alert(msg);
            }
        }
    });
</script>

除了事件修饰符以外,v-on还提供了按键修饰符,方便咱们监听键盘事件中的按键。例如:

<div id="app">
    <!--监听input的输入,当输入回车时触发saySelf函数(回车的keyCode值为13),用于处理常见的用户输入完直接按回车键提交-->
    <input type="text" v-on:keyup.13="saySelf('回车键')">
</div>
<script>
    var vm = new Vue({
        el: '#app',
        methods: {
            saySelf(msg) {
                alert(msg);
            }
        }
    });
</script>

Vue.js给一些经常使用的按键名提供了别称,这样就省去了记一些keyCode。所有按键别名为:enter,tab,delete,esc,space,up,down,left,right。例如:

<input type="text" v-on:keyup.enter="saySelf('回车键')">

Vue.js也容许咱们本身定义按键别名,例如:

<input type="text" v-on:keyup.f1="saySelf(112)">
Vue.directive('on').keyCodes.f1 = 112;

Vue.js 2.0中能够直接在Vue.config.keyCodes里添加自定义按键别名,无需修改v-on指令,例如:

<input type="text" v-on:keyup.13="saySelf('回车键')">
Vue.config.keyCodes.f1 = 13;

<input type="text" @keyup.media-play-pause="method">
Vue.config.keyCodes = {
  v: 86,
  f1: 112,
  // camelCase 不可用
  mediaPlayPause: 179,
  // 取而代之的是 kebab-case 且用双引号括起来
  "media-play-pause": 179,
  up: [38, 87]
}

第3章 指令

1、内置指令

1.v-bind
拥有三种修饰符,分别为.sync .once .camel。在Vue.js 2.0中修饰符.sync .once均被废弃,规定组件间仅能单向传递,若是子组件须要修改父组件,则必须使用事件机制来进行处理。
.camel 将绑定的特性名字转回驼峰命名。之鞥呢用于普通HTML属性的绑定,一般会用于svg标签下的属性,例如:<svg width='400' height='300' :view-box.camel='viewBox'></svg>,输出结果即为<svg width='400' height='300' :viewBox='viewBox'></svg>

2.v-for
Vue.js 2.0中作了些细微的调整,大体包括如下几个方面:
① 参数顺序变化

当包含参数index或key时,对象参数修改成(item, index)或(value, key),这样与JS Array对象的新方法forEach和map,以及一些对象迭代器(例如lodash)的参数能保持一致。

② v-bind:key

属性track-by被v-bind:key代替,<li v-for="item in items" track-by="id"></li>须要改写为<li v-for="item in items" v-bind:key="item.id"></li>

③ n in 10

v-for="n in 10"中的n由原来的0 ~ 9迭代变成1 ~ 10迭代。

3.v-el 和 v-ref
v-el指令为DOM元素注册了一个索引,使得咱们能够直接访问DOM元素。语法上说能够经过所属实例的$els属性调用。
v-ref指令与v-el类型,只不过v-ref做用于子组件上,实例能够经过$refs访问子组件。命名方式也类型,想使用驼峰命名的话用“-”来作链接。
注意:Vue.js 2.0中,v-el和v-ref合并为一个ref属性了,能够在组件实例中经过$refs来调用。这意味着 v-el:my-element 将写成这样:ref="myElement",v-ref:my-component 变成了这样:ref="myComponent"。绑定在通常元素上时,ref 指 DOM 元素,绑定在组件上时,ref 为一组件实例。

<div id="app">
    <div ref="demo">there is a el demo</div>
</div>
<script>
    var vm = new Vue({
        el: '#app',
        methods: {
            saySelf(msg) {
                alert(msg);
            },
        }
    });
    console.log(vm.$refs.demo.innerText);   // there is a el demo
</script>

由于 v-ref 再也不是一个指令了而是一个特殊的属性,它也能够被动态定义了。这样在和v-for 结合的时候是颇有用的:

<p v-for="item in items" v-bind:ref="'item' + item.id"></p>

之前 v-el/v-ref 和 v-for 一块儿使用将产生一个 DOM 数组或者组件数组,由于无法给每一个元素一个特定名字。如今你还仍然能够这样作,给每一个元素一个一样的ref:

<p v-for="item in items" ref="items"></p>

和 1.x 中不一样,$refs 不是响应的,由于它们在渲染过程当中注册/更新。只有监听变化并重复渲染才能使它们响应。

另外一方面,设计$refs主要是提供给 js 程序访问的,并不建议在模板中过分依赖使用它。由于这意味着在实例以外去访问实例状态,违背了 Vue 数据驱动的思想。

4.v-pre
v-pre指令就是跳过编译这个元素和子元素,显示原始的{{}}Mustache标签,用来减小编译时间。例如:

<div id="app">
    <div v-pre>{{ uncompiled }}</div>
</div>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            uncompiled: 'There is an uncompiled element'
        }
    });
</script>
输出结果为:<div>{{ uncompiled }}</div>

5.v-cloak
v-cloak指令至关于在元素上添加了一个[v-cloak]的属性,直到关联的实例结束编译。官方推荐能够和css规则[v-cloak]{display:none}一块儿使用,能够隐藏未编译的Mustache标签直到实例准备完毕。例如:

<div v-cloak>{{ msg}}</div>

6.v-once
v-once指令是Vue.js 2.0中新增的内置指令,用于标明元素或组件只渲染一次,即便随后发生绑定数据的变化或更新,该元素或组件以及包含的子元素都不会再次被编译和渲染。至关于明确标注了这些元素不须要被更新,因此v-once的做用是最大程度地提高了更新行为中页面的性能,能够略过一些明确不须要变化的步骤。使用方式以下:

<div v-once>{{ uncompiled }}</div>
<my-component v-once :msg="msg"></my-component>

2、自定义指令基础

一、指令的注册
能够经过Vue.directive(id, definition)方法注册一个全局自定义指令,接收参数id和定义对象。id是指令的惟一标识,定义对象则是指令的相关属性及钩子函数。例如:

Vue.directive('global-directive', definition);    // 只注册了这个指令,并无赋予这个指令任何功能
<div v-global-directive></div>    // 能够在模板中这么使用

除了在全局注册指令外,也能够经过在组件的directives选项注册一个局部的自定义指令。例如:

var comp = Vue.extend({
    directives: {
        'localDirective': {}    // 能够采用驼峰式命名
    }
});
// 该指令就只能在当前组件内经过v-local-directive的方式调用,而没法被其余组件调用。

2.指令的定义对象
注册指令的同时,能够传入definition定义对象,对指令赋予一些特殊功能。一个指令定义对象主提供以下几个钩子函数(均为可选):
bind:只被调用一次,在指令第一个绑定到元素上时调用。
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不必定已被插入文档中)。
update:Vue.js 1.0指令在bind以后以初始值为参数进行第一次调用,以后每次当绑定值发生变化时调用,update接收到的参数为newValue和oldValue。Vue.js 2.0中变化:

指令绑定bind函数执行后不直接调用update函数。 
只要组件发生重绘,不管指令接受的值是否发生变化,均会调用update函数。若是须要过滤没必要要的更新,则可使用binding.value == binding.oldValue来判断。

componentUpdated:指令所在组件的 VNode 及其子 VNode 所有更新后调用。
unbind:指令从元素上解绑时调用,只调用一次。

钩子函数参数(即 el、binding、vnode 和 oldVnode)
指令钩子函数会被传入如下参数:
el:指令所绑定的元素,能够用来直接操做 DOM 。
binding:一个对象,包含如下属性:
name:指令名,不包括 v- 前缀。
value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。不管值是否改变均可用。
expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
除了 el 以外,其它参数都应该是只读的,切勿进行修改。若是须要在钩子之间共享数据,建议经过元素的 dataset 来进行。
使用了这些属性的自定义钩子样例:

<div id="hook-arguments-example" v-demo:foo.a.b="message"></div>
Vue.directive('demo', {
  bind: function (el, binding, vnode) {
    var s = JSON.stringify
    el.innerHTML =
      'name: '       + s(binding.name) + '<br>' +
      'value: '      + s(binding.value) + '<br>' +
      'expression: ' + s(binding.expression) + '<br>' +
      'argument: '   + s(binding.arg) + '<br>' +
      'modifiers: '  + s(binding.modifiers) + '<br>' +
      'vnode keys: ' + Object.keys(vnode).join(', ')
  }
})

new Vue({
  el: '#hook-arguments-example',
  data: {
    message: 'hello!'
  }
})
输出结果:
    name: "demo"
    value: "hello!"
    expression: "message"
    argument: "foo"
    modifiers: {"a":true,"b":true}
    vnode keys: tag, data, children, text, elm, ns, context, functionalContext, key, componentOptions, componentInstance, parent, raw, isStatic, isRootInsert, isComment, isCloned, isOnce

钩子函数实例和参数变化

<div id="demo" v-my-msg:console.log="content"></div>
<script>
    Vue.directive('my-msg', {
        bind: function (el, binding, vnode) {
            console.log('el', el);
            console.log('binding', binding);
            console.log('vnode', vnode);
        }
    });
    var vm = new Vue({
        el: '#demo',
        data: {
            content: 'there is the content'
        }
    });
</script>

函数简写
在不少时候,可能想在bind和update时触发相同行为,不关心其它的钩子。能够直接传入一个函数代替定义对象:

<div id="demo" v-color-swatch="red">ddd</div>
<div v-color-swatch="red">ddd</div>
<script>
    Vue.directive('color-swatch', function (el, binding) {
        el.style.backgroundColor = binding.value;
    });
    var vm = new Vue({
        el: '#demo',
        data: {
            red: 'red'
        }
    });
</script>

对象字面量
若是指令须要多个值,能够传入一个 JavaScript 对象字面量。指令函数可以接受全部合法的 JavaScript 表达式。

<div v-demo="{ color: 'white', text: 'hello!' }"></div>
Vue.directive('demo', function (el, binding) {
  console.log(binding.value.color) // => "white"
  console.log(binding.value.text)  // => "hello!"
});

第4章 过滤器

Vue.js 容许自定义过滤器,可被用于一些常见的文本格式化。过滤器能够用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示:

<!-- 在双花括号中 -->
{{ message | capitalize }}

<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>

能够在一个组件的选项中定义本地的过滤器:

filters: {
  capitalize: function (value) {
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
}

或者在建立 Vue 实例以前全局定义过滤器:

Vue.filter('capitalize', function (value) {
  if (!value) return ''
  value = value.toString()
  return value.charAt(0).toUpperCase() + value.slice(1)
})

new Vue({
  // ...
})

过滤器函数总接收表达式的值 (以前的操做链的结果) 做为第一个参数。在上述例子中,capitalize 过滤器函数将会收到 message 的值做为第一个参数。

过滤器能够串联:

{{ message | filterA | filterB }}

在这个例子中,filterA 被定义为接收单个参数的过滤器函数,表达式 message 的值将做为参数传入到函数中。而后继续调用一样被定义为接收单个参数的过滤器函数 filterB,将 filterA 的结果传递到 filterB 中。

过滤器是 JavaScript 函数,所以能够接收参数:

{{ message | filterA('arg1', arg2) }}

这里,filterA 被定义为接收三个参数的过滤器函数。其中 message 的值做为第一个参数,普通字符串 'arg1' 做为第二个参数,表达式 arg2 的值做为第三个参数。

日期格式化过滤器:

<div id="app">
    {{date | date('yyyy-MM-dd hh:mm:ss')}}
</div>
<script>
    // 日期格式化
    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;
    });
    var vm = new Vue({
        el: '#app',
        data: {
            date: new Date()
        }
    });
    // 显示结果:2019-04-10 17:19:17
</script>

第5章 css过渡

概述
Vue 在插入、更新或者移除 DOM 时,提供多种不一样方式的应用过渡效果。
包括如下工具:

  • 在 CSS 过渡和动画中自动应用 class
  • 能够配合使用第三方 CSS 动画库,如 Animate.css
  • 过渡钩子函数中使用 JavaScript 直接操做 DOM
  • 能够配合使用第三方 JavaScript 动画库,如 Velocity.js

1、单元素/组件的过渡
Vue 提供了 transition 的封装组件,在下列情形中,能够给任何元素和组件添加进入/离开过渡

  • 条件渲染 (使用 v-if)
  • 条件展现 (使用 v-show)
  • 动态组件
  • 组件根节点

直接使用transition标签并设定其属性来定义一个过渡效果。例如:

<div id="demo">
  <button v-on:click="show = !show">
    Toggle
  </button>
  <transition name="fade">
    <p v-if="show">hello</p>
  </transition>
</div>
new Vue({
  el: '#demo',
  data: {
    show: true
  }
})
.fade-enter-active, .fade-leave-active {
  transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
}

1.过渡的类名

在进入/离开的过渡中,会有 6 个 class 切换。

  1. v-enter:定义进入过渡的开始状态。在元素被插入以前生效,在元素被插入以后的下一帧移除。
  2. v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入以前生效,在过渡/动画完成以后移除。这个类能够被用来定义进入过渡的过程时间,延迟和曲线函数。
  3. v-enter-to: 2.1.8版及以上 定义进入过渡的结束状态。在元素被插入以后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成以后移除。
  4. v-leave: 定义离开过渡的开始状态。在离开过渡被触发时马上生效,下一帧被移除。
  5. v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时马上生效,在过渡/动画完成以后移除。这个类能够被用来定义离开过渡的过程时间,延迟和曲线函数。
  6. v-leave-to: 2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发以后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成以后移除。

图片描述

对于这些在过渡中切换的类名来讲,若是你使用一个没有名字的 <transition>,则 v- 是这些类名的默认前缀。若是你使用了 <transition name="my-transition">,那么 v-enter 会替换为 my-transition-enter。

经常使用的过渡都是使用 CSS 过渡。下面是一个简单例子:

<div id="example-1">
  <button @click="show = !show">
    Toggle render
  </button>
  <transition name="slide-fade">
    <p v-if="show">hello</p>
  </transition>
</div>
new Vue({
  el: '#example-1',
  data: {
    show: true
  }
})
/* 能够设置不一样的进入和离开动画 */
/* 设置持续时间和动画函数 */
.slide-fade-enter-active {
  transition: all .3s ease;
}
.slide-fade-leave-active {
  transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.slide-fade-enter, .slide-fade-leave-to
/* .slide-fade-leave-active for below version 2.1.8 */ {
  transform: translateX(10px);
  opacity: 0;
}

2.CSS 动画
CSS 动画用法同 CSS 过渡,区别是在动画中 v-enter 类名在节点插入 DOM 后不会当即删除,而是在 animationend 事件触发时删除。

<div id="example-2">
  <button @click="show = !show">Toggle show</button>
  <transition name="bounce">
    <p v-if="show">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris facilisis enim libero, at lacinia diam fermentum id. Pellentesque habitant morbi tristique senectus et netus.</p>
  </transition>
</div>
new Vue({
  el: '#example-2',
  data: {
    show: true
  }
})
.bounce-enter-active {
  animation: bounce-in .5s;
}
.bounce-leave-active {
  animation: bounce-in .5s reverse;
}
@keyframes bounce-in {
  0% {
    transform: scale(0);
  }
  50% {
    transform: scale(1.5);
  }
  100% {
    transform: scale(1);
  }
}

3.自定义过渡的类名
能够经过如下特性来自定义过渡类名:

  • enter-class
  • enter-active-class
  • enter-to-class (2.1.8+)
  • leave-class
  • leave-active-class
  • leave-to-class (2.1.8+)

他们的优先级高于普通的类名,这对于 Vue 的过渡系统和其余第三方 CSS 动画库,如 Animate.css 结合使用十分有用。示例:

<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">

<div id="example-3">
  <button @click="show = !show">
    Toggle render
  </button>
  <transition
    name="custom-classes-transition"
    enter-active-class="animated tada"
    leave-active-class="animated bounceOutRight"
  >
    <p v-if="show">hello</p>
  </transition>
</div>

new Vue({
  el: '#example-3',
  data: {
    show: true
  }
})

JavaScript 钩子
能够在属性中声明 JavaScript 钩子

<transition
  v-on:before-enter="beforeEnter"
  v-on:enter="enter"
  v-on:after-enter="afterEnter"
  v-on:enter-cancelled="enterCancelled"

  v-on:before-leave="beforeLeave"
  v-on:leave="leave"
  v-on:after-leave="afterLeave"
  v-on:leave-cancelled="leaveCancelled"
>
  <!-- ... -->
</transition>
// ...
methods: {
  // --------
  // 进入中
  // --------

  beforeEnter: function (el) {
    // ...
  },
  // 当与 CSS 结合使用时
  // 回调函数 done 是可选的
  enter: function (el, done) {
    // ...
    done()
  },
  afterEnter: function (el) {
    // ...
  },
  enterCancelled: function (el) {
    // ...
  },

  // --------
  // 离开时
  // --------

  beforeLeave: function (el) {
    // ...
  },
  // 当与 CSS 结合使用时
  // 回调函数 done 是可选的
  leave: function (el, done) {
    // ...
    done()
  },
  afterLeave: function (el) {
    // ...
  },
  // leaveCancelled 只用于 v-show 中
  leaveCancelled: function (el) {
    // ...
  }
}

这些钩子函数能够结合 CSS transitions/animations 使用,也能够单独使用。
注意:

  • 当只用 JavaScript 过渡的时候,在 enter 和 leave 中必须使用 done 进行回调。不然,它们将被同步调用,过渡会当即完成。
  • 推荐对于仅使用 JavaScript 过渡的元素添加 v-bind:css="false",Vue 会跳过 CSS 的检测。这也能够避免过渡过程当中 CSS 的影响。

一个使用 Velocity.js 的简单例子:

<!--
Velocity是一款高校的动画引擎,能够单独使用也能够配合jQuery使用。 和 jQuery.animate 的工做方式相似,也是用来实现 JavaScript 动画的一个很棒的选择
-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>

<div id="example-4">
  <button @click="show = !show">
    Toggle
  </button>
  <transition
    v-on:before-enter="beforeEnter"
    v-on:enter="enter"
    v-on:leave="leave"
    v-bind:css="false"
  >
    <p v-if="show">
      Demo
    </p>
  </transition>
</div>
new Vue({
  el: '#example-4',
  data: {
    show: false
  },
  methods: {
    beforeEnter: function (el) {
      el.style.opacity = 0
      el.style.transformOrigin = 'left'
    },
    enter: function (el, done) {
      Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 })
      Velocity(el, { fontSize: '1em' }, { complete: done })
    },
    leave: function (el, done) {
      Velocity(el, { translateX: '15px', rotateZ: '50deg' }, { duration: 600 })
      Velocity(el, { rotateZ: '100deg' }, { loop: 2 })
      Velocity(el, {
        rotateZ: '45deg',
        translateY: '30px',
        translateX: '30px',
        opacity: 0
      }, { complete: done })
    }
  }
})

2、初始渲染的过渡
能够经过 appear 特性设置节点在初始渲染的过渡

<transition appear>
  <!-- ... -->
</transition>

这里默认和进入/离开过渡同样,一样也能够自定义 CSS 类名。

<transition
  appear
  appear-class="custom-appear-class"
  appear-to-class="custom-appear-to-class" (2.1.8+)
  appear-active-class="custom-appear-active-class"
>
  <!-- ... -->
</transition>

自定义 JavaScript 钩子:

<transition
  appear
  v-on:before-appear="customBeforeAppearHook"
  v-on:appear="customAppearHook"
  v-on:after-appear="customAfterAppearHook"
  v-on:appear-cancelled="customAppearCancelledHook"
>
  <!-- ... -->
</transition>

appear主要用于元素的首次渲染,若是同时声明了enter和appear的相关钩子函数,元素首次渲染的时候会使用appear系钩子函数,再次渲染的时候才使用enter系钩子函数。

2、多个元素的过渡
咱们以后讨论多个组件的过渡,对于原生标签可使用 v-if/v-else 。最多见的多标签过渡是一个列表和描述这个列表为空消息的元素:

<transition>
  <table v-if="items.length > 0">
    <!-- ... -->
  </table>
  <p v-else>Sorry, no items found.</p>
</transition>

能够这样使用,可是有一点须要注意:当有相同标签名的元素切换时,须要经过 key 特性设置惟一的值来标记以让 Vue 区分它们,不然 Vue 为了效率只会替换相同标签内部的内容。即便在技术上没有必要,给在 <transition> 组件中的多个元素设置 key 是一个更好的实践。示例:

<transition>
  <button v-if="isEditing" key="save">
    Save
  </button>
  <button v-else key="edit">
    Edit
  </button>
</transition>

在一些场景中,也能够经过给同一个元素的 key 特性设置不一样的状态来代替 v-if 和 v-else,上面的例子能够重写为:

<transition>
  <button v-bind:key="isEditing">
    {{ isEditing ? 'Save' : 'Edit' }}
  </button>
</transition>

使用多个 v-if 的多个元素的过渡能够重写为绑定了动态属性的单个元素过渡。例如:

<transition>
  <button v-if="docState === 'saved'" key="saved">
    Edit
  </button>
  <button v-if="docState === 'edited'" key="edited">
    Save
  </button>
  <button v-if="docState === 'editing'" key="editing">
    Cancel
  </button>
</transition>

能够重写为:

<transition>
  <button v-bind:key="docState">
    {{ buttonMessage }}
  </button>
</transition>
// ...
computed: {
  buttonMessage: function () {
    switch (this.docState) {
      case 'saved': return 'Edit'
      case 'edited': return 'Save'
      case 'editing': return 'Cancel'
    }
  }
}

过渡模式mode
同时生效的进入和离开的过渡不能知足全部要求,因此 Vue 提供了过渡模式,控制过渡插入/移除的前后顺序,主要用于元素切换时。可供选择的值有“out-in” “in-out”,若是不设置,则同时调用。

  • in-out:新元素先进行过渡,完成以后当前元素过渡离开。
  • out-in:当前元素先进行过渡,完成以后新元素过渡进入。
<div id="app">
    <button @click="show = !show">
        Toggle
    </button>
    <transition name="fade" mode="out-in">
        <div :key="show">{{show}}</div>
    </transition>
</div>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            show: true
        }
    });
</script>

当show在true和false切换时,mode="out-in"决定先移除<div>true</div>,等过渡结束后,再插入<div>true</div>元素,mode="in-out"则相反。

列表过渡
怎么同时渲染整个列表,好比使用 v-for ?在这种场景中,使用 <transition-group> 组件。这个组件的特色:

  • 不一样于<transition>,<transition-group>不是一个虚拟DOM,会真实渲染在DOM树中。默认<span>,能够经过 tag 特性更换为其余元素。
  • 过渡模式不可用,不支持mode参数,由于咱们再也不相互切换特有的元素。
  • 内部元素老是须要提供惟一的 key 属性值。

列表的进入/离开过渡

<div id="app">
    <button @click="pushFn">push</button>
    <transition-group name="list" appear tag="ul">
        <li v-for="item in items" :key="item.id" class="list-li">{{item.text}}</li>
    </transition-group>
</div>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            items: [
                {id: 1, text: '11'},
                {id: 2, text: '22'},
                {id: 3, text: '33'}
            ]
        },
        methods: {
            pushFn() {
                this.items.push({id: 4, text: '44'});
            }
        }
    });
</script>
<style>
    .list-li{
        width: 100px;
        height: 20px;
        transform: translate(0, 0);
    }
    .list-enter,.list-leave-active{
        opacity: 0;
        transform: translate(-30px, 0);
    }
    .list-enter-active, .list-leave-active{
        transition: all 0.5s ease;
    }
</style>

列表的排序过渡
列表的交错过渡
可复用的过渡
动态过渡

第6章 组件

1、组件注册
Vue.js提供了两种注册方式,分别是全局注册和局部注册。
全局注册须要确保在根实例初始化以前注册,这样才能使组件在任意实例中被使用,示例:

Vue.component('my-component-name', {
    // ... 选项 ...
})
// 这条语句须要写在new Vue({ el: '#app' })以前。
Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })

new Vue({ el: '#app' })
<div id="app">
    <component-a></component-a>
    <component-b></component-b>
    <component-c></component-c>
</div>
// 这三个组件在各自内部也均可以相互使用。

若是你使用一个像 webpack 这样的构建系统,全局注册全部的组件意味着即使你已经再也不使用一个组件了,它仍然会被包含在你最终的构建结果中。这形成了用户下载的 JavaScript 的无谓的增长。

局部注册则限定了组件只能在被注册的组件中使用,而没法在其余组件中使用,示例:

var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
new Vue({
    el: '#app',
    components: {
        'component-a': ComponentA,
        'component-b': ComponentB
    }
})

对于 components 对象中的每一个属性来讲,其属性名就是自定义元素的名字,其属性值就是这个组件的选项对象。
注意局部注册的组件在其子组件中不可用。例如,若是你但愿 ComponentA 在 ComponentB 中可用,则你须要这样写:

var ComponentA = { /* ... */ }
var ComponentB = {
    components: {
        'component-a': ComponentA
    },
    // ...
}

或者若是你经过 Babel 和 webpack 使用 ES2015 模块,:

import ComponentA from './ComponentA.vue'
export default {
    components: {
        ComponentA
    },
    // ...
}

注意在 ES2015+ 中,在对象中放一个相似 ComponentA 的变量名实际上是 ComponentA: ComponentA 的缩写,即这个变量名同时是:

  • 用在模板中的自定义元素的名称
  • 包含了这个组件选项的变量名

构造器写法:Vue.extend({})

<div id="app">
    <my-component></my-component>
    <parent></parent>
</div>
<script>
    // 全局注册
    var MyComponent = Vue.extend({
        template: '<p>This is a component</p>'
    });
    Vue.component('my-component', MyComponent);
    
    // 局部注册
    var Parent = Vue.extend({
        template: `<div><p>This is a parent component</p>
                    <my-child></my-child>
                    </div>`,
        components: {
            'my-child': {
                template: '<p>This is a child component</p>'
            }
        }
    });
    var vm = new Vue({
        el: '#app',
        components: {
            parent: Parent
        }
    });
</script>

Vue 组件的基本示例:

<div id="app">
    <button-counter></button-counter>
</div>
<script>
    // 组件是可复用的 Vue 实例,因此它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。
    Vue.component('button-counter', {
        // 一个组件的 data 选项必须是一个函数,所以每一个实例能够维护一份被返回对象的独立的拷贝:
        data: function () {
            return {
                count: 0
            }
        },
        template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
    });
    var vm = new Vue({
        el: '#app'
    });
</script>
相关文章
相关标签/搜索