能够简单地在页面引入Vue.js做为独立版本,Vue即被注册为全局变量,能够在页面使用了。javascript
若是但愿搭建大型应用,则可使用npm安装Vue.js:css
# 最新稳定版本
$ npm install vue
# 最新稳定 CSP 兼容版本
$ npm install vue@csp
复制代码
甚至可使用vue官方推荐的项目模板来建立新的大型项目,这就须要使用vue命令行工具vue-cli:html
# 全局安装 vue-cli
$ npm install -g vue-cli
# 建立一个基于 "webpack" 模板的新项目
$ vue init webpack my-project
# 安装依赖,走你
$ cd my-project
$ npm install
$ npm run dev
复制代码
Vue的目标是实现响应的数据绑定和组合的视图组件,因此其核心只有两块:vue
数据经过在HTML模板中的指令和“Mustache”语法,绑定到对应的HTML元素上,其底层是JavaScript对象的存取器属性和原生javascript事件。java
组件系统则经过扩展的Vue实例,来渲染位于HTML中的相似于自定义元素的Vue组件,从而实现独立可复用的组件。webpack
每一个Vue应用的起步都是从一个Vue的根实例开始:web
var vm = new Vue({
//选项,包含数据、模板、挂载元素等
})
复制代码
而每一个组件,也是一个扩展的vue实例:ajax
var myCompnent = Vue.extend({
//选项,包含组件的数据、模板、挂载元素等
})
复制代码
Vue实例会代理设置在data
选项中的数据,全部data
选项中的数据均可以经过vm自己进行访问:算法
var vm = new Vue({
data: {
name: 'chen'
}
})
data.name // -> chen
vm.name = 'wei'
data.name // -> wei
复制代码
除了数据属性,Vue实例还暴露了一些有用的实例属性与方法,这些属性与方法都有前缀$,区别于数据属性(参考API文档):vue-router
vm.$watch
vm.$data
vm.$el
复制代码
在一个Vue实例的生命周期的不一样时间,能够调用相应的回调函数:
有两种方式能够进行数据绑定:插值和指令。
使用双大括号语法来绑定数据到HTML中,使用三大括号语法来输出HTML字符(不经常使用)。插值也能够用在HTML属性中:
<span> {{msg }}</span>
<div>{{{ raw_html }}}</div>
<div id="item-{{ id }}"></div>
复制代码
使用指令 (Directives,是特殊的带有前缀v-
的特性)来将数据绑定到HTML,一般用于应用特殊行为到DOM上:
<p v-if="greeting">Hello!</p> <!--当greeting为真时p存在-->
复制代码
指令有三个须要记住的特征:
v-on:click="doSomething"
,这里click
是v-on
指令的参数v-on:click.stop="doSomething"
,这里.stop
是修饰符,表示阻止冒泡v-bind
指令可缩写为简单的:
号,v-on
指令能够缩写为@
号<a :href="url"></a>
<a @click="doSomething"></a>
复制代码
插值的内容和指令的值,都被限定为只能使用绑定表达式,它由一个JavaScript表达式和可选的一个或多个过滤器构成。须要注意JavaScript表达式和语句的区别,而过滤器则是一个JavaScript函数,Vue提供了不少内置的过滤器,也能够本身编写自定义过滤器:
{{ message | filterA | filterB }}
{{ message | filterA 'arg1' arg2 }} // 过滤器也能够接受参数
复制代码
绑定表达式被限定只能使用一个JavaScript表达式,目的是为了限制在数据绑定中放入过多的逻辑。若是须要更复杂的数据处理逻辑,则应该使用计算属性:
var vm = new Vue({
el: '#example',
data: {
a: 1
},
conmpued: {
a () {
return this.a+1
}
}
})
复制代码
Vue默认将提供的回调函数做为计算属性的getter来使用,因此默认计算属性是只读的。可是必要时也可指定计算属性的setter:
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
复制代码
Vue还提供了一个$watch方法用于监视数据变更:
vm.$watch('a', val => {
console.log(val)
})
// 或者写成:
var vm = new Vue({
… …
watch: {
'a' (val) { // 以须要监测的属性为属性名
console.log(val)
}
}
})
复制代码
可是由于可读性,推荐使用计算属性而不是watch方法。
因为其经常使用性,Vue加强了class
与style
两个HTML属性的绑定,不只仅能够接受字符串值,还能够接受数组和对象两种类型的值。
v-bind:class="value"
,接受字符串,也能够接受对象和数组,而且还能够和普通的class
属性共存:
<!-- 接受字符串 -->
v-bind:class="classA"
<!-- 接受对象 -->
v-bind:class="{ 'class-a': isA, 'class-b': isB }"
<!-- 直接绑定数据里的对象,或者计算属性返回的对象 -->
v-bind:class="classObject"
<!-- 与普通class属性共存 -->
class="some-class" v-bind:class="{ 'class-a': isA, 'class-b': isB }"
<!-- 接受数组 -->
v-bind:class="[classA, classB]"
<!--直接绑定数据里的数组,或者计算属性返回的数组 -->
v-bind:class="classArray"
<!--数组语法与对象语法混用 1.0.19+ -->
v-bind:class="[classA, { 'class-b': isB, 'class-c': isC}]"
复制代码
内联样式绑定也能够接受对象或者数组,当接受对象时语法很是直观(看着很是像CSS语法),当接受数组时能够将多个样式对象应用到元素上。对象和数组均可以直接来自数据data
或者computed
或者props
。
同时内联样式的绑定Vue会自动侦测浏览器并添加相应浏览器前缀。
<!-- 接受对象 -->
v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"
<!-- 直接绑定数据里的对象,或者计算属性返回的对象 -->
v-bind:style="styleObject"
v-bind:style="[styleObjectA, styleObjectB]"
复制代码
Vue能够根据指定的条件来决定是否渲染元素,有两个指令v-if
和v-show
,用法基本一致。
<h1 v-if="isOk">Hello</h1>
复制代码
使用一个template
元素包裹多个元素并条件渲染它。
<template v-if="isOk">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template >
复制代码
还可使用v-else
来为条件渲染添加一个else
块:
<div v-if="isOk">hello</div>
<!-- v-else指令必须紧跟v-if或v-show不然不生效 -->
<div v-else>fuck off</div>
<!-- 这两个元素仅会显示一个 -->
复制代码
组件也可以使用条件渲染,可是将v-show用在组件中时,因为指令的优先级问题,不能在后面使用v-else指令,而应该使用另外一个v-show来替代:
<my-compnent v-show="condition"></my-component>
<p v-else>这可能也是一个组件</p> <!--错误用法-->
<my-compnent v-show="condition"></my-component>
<p v-show="!condition">这可能也是一个组件</p> <!--正确用法-->
复制代码
编译区别:v-if
是惰性的,若是初始条件为假则这个元素不会被编译,只有当条件第一次为真时,Vue才编译并缓存编译结果以供后续使用;v-show
的元素则直接编译。
显示区别:v-if
是真实的在HTML中重建或者销毁这个元素,v-show
则只是经过display
来控制,简单得多。
结论:v-if
有更高的切换消耗而v-show
有更高的初始渲染消耗,频繁切换用v-show
,条件稳定用v-if
使用v-for指令,Vue能够根据一个数组,渲染一组列表元素。
<li v-for="item in items">{{ item.message + ":" + $index }}</li>
<!-- items是数据中的一个数组,item是数组元素的别名 -->
<!-- 有一个特殊变量:$index,是数组元素的索引 -->
复制代码
Vue会更新被观察的数组items的大部分修改自身的方法:push()
、pop()
、shift()
、unshift()
、splice()
、sort()
、reverse()
调用这些方法时数组的变更会被实时反映在数据绑定中,而那些不会修改原数组而是返回新数组的方法:filter()
、concat()
、slice()
则须要使用其结果替换原数组,数据变更才能反映在数据绑定中。
数组替换并不会彻底从新渲染整个列表,Vue使用了一些启发算法提升了性能。默认这个算法是根据数组元素自己的值来追踪和复用DOM,这会致使数组中重复的值只会渲染一次,这时可使用track-by="$index"
指令强制Vue进入原位更新模式(只有$index
位置的值与上次位置的值一致时才复用DOM元素,不然马上从新生成并渲染新的DOM元素),但在须要同步临时状态和组件的私有状态时需谨慎使用。
若是数组中每一个元素都有一个相同但值惟一的属性,好比下面这样
{
items: [
{ _uid: '88f869d', ... },
{ _uid: '7496c10', ... }
]
}
复制代码
则能够指定track-by="_uid"
来让Vue尽量地复用DOM以提升性能。
另外,若是数组元素是已经被Object.freeze()
方法冻结的对象,则须要使用track-by
指令明确指定追踪属性,不然将不能自动追踪(被冻结的对象没法设置存取器属性)
因为JavaScript自己的限制,Vue不能检测到两种数据变更:
vm.items[0] = {}
vm.items.length = 0
应该使用下面两种技术进行替换:
var item = { changeMsg: 'changed!' }
vm.items.$set(0, item)
vm.items.$remove(item)
复制代码
vm.items = []
遍历对象时可访问一个特殊变量$key
,表示对象的键名
<ul>
<li v-for="value in object"> <!-- value是对象键值的别名,object是数据对象的别名 -->
{{ $key }} : {{ value }}
</li>
</ul>
复制代码
也能够给对象的键名提供一个别名
<ul>
<li v-for="(key, value) in object"> <!-- value是对象键值的别名,object是数据对象的别名 -->
{{ key }} : {{ value }}
</li>
</ul>
复制代码
对象遍历的结果是按Object.keys()的结果来的,不保证在全部引擎中都一致。
v-for也能够接受一个整数表示值域,即简单重复渲染元素:
<div>
<span v-for="n in 10">{{ n }}</span> /* 渲染10个span */
</div>
复制代码
将v-for指令用在 元素上,能够重复渲染多个元素:
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider"></li>
</template>
</ul>
复制代码
有时咱们想显示过滤/排序过的数组,同时不实际修改或重置原始数据。有两个办法:
filterBy
和orderBy
计算属性有更好的控制力,也更灵活,由于它是全功能 JavaScript;可是一般过滤器更方便。
使用v-on
指令监听DOM事件,v-on
后接冒号传入事件类型名称,指令的值为事件处理器,事件处理器只能指定为methods
中的一个方法或者一个JavaScript语句(一般是方法调用语句,方便传值):
<button v-on:click="sayHi">click me</button>
<button v-on:click="say('Hello!')">click me</button>
复制代码
事件处理器能够接受一个特殊参数即为事件对象,使用$event表示:
<button v-on:click="say('hello', $event)">click me</button>
复制代码
v-on
指令支持两类修饰符:事件修饰符和按键修饰符
事件修饰符有四个(1.0.16+):
按键修饰符则表示只在指定按键上触发事件(一般在监听键盘事件时使用,keyup
、keydown
、keypress
)
<input v-on:keyup.13="submit">
复制代码
除了使用keycode,按键修饰符还可使用vue提供的按键别名:
enter
、tab
、delete
、esc
、space
、up
、down
、left``right
1.0.8+可使用单字母别名,全部26个字母均可以做为按键别名使用,1.0.17能够自定义按键别名:
Vue.directive('on').keyCodes.f1 = 112
复制代码
使用v-model指令在表单控件中建立双向数据绑定。
model.text
的值实时双向同步<input type="text" v-model="model.text" />
复制代码
<input type="ratio" v-model="model.ratio" />
复制代码
<input type="checkbox" value="jack" v-model="model.anArray" />
<input type="checkbox" value="john" v-model="model.anArray" />
<input type="checkbox" value="awey" v-model="model.anArray" />
复制代码
<select v-model="model.anArray" multiple>
<option selected>A</option>
<option>B</option>
<option>C</option>
</select>
复制代码
全部表单控件都只能使用v-model
而不能使用v-bind:value
来进行数据双向绑定,由于v-bind
指令在绑定普通属性(除了子组件的prop
,它可使用async
修饰符)时,是数据到HTML的单向数据绑定,HTML中的变化是不会同步到数据中的。
可是若是但愿v-model
双向绑定的HTML和数据属性,指向的是vue实例中的另外一个数据属性(好比有两个属性a和b,其中a绑定到v-mode上,b是另外一个属性,若是但愿radio和checkbox选中时,a的值为b,不选中时a的值为默认值),则可使用v-bind:value
来实现
<input type="radio" v-model="isPicked" v-bind:value="anotherDataProperty">
复制代码
<input type="checkbox" v-model="isSelected" v-bind:true-value="a" v-bind:false-value="b">
复制代码
<select v-model="selected">
…
<option v-bind:value="a">a</option>
…
</select>
复制代码
在这种状况下使用v-bind:value指令,其值不限于字符串,能够是任意JavaScript值:
<input type="radio" v-model="isPicked" v-bind:value="{ name: awey, age: 26 }">
复制代码
v-model可使用三个特性,lazy
、debounce
和number
<input type="text" v-model="inputValue" lazy />
复制代码
<input type="text" v-model="inputValue" debounce="500"/>
复制代码
<input type="text" v-model="inputValue" number />
复制代码
表单控件有一些诸如
checked
和disabled
这类HTML属性,不管赋值与否只要存在该属性便可生效。可是在使用v-bind
绑定这类属性时,vue对其作了处理,值为真假值便可让属性生效或不生效,无需再有上述顾虑。
在插入和移除或者显示和隐藏DOM元素时,经过Vue的过渡动画系统,能够给元素添加三种类型的动画:CSS过渡(trasition或者animateframe),JavaScript过渡和渐进过渡。v-if、v-show、v-for(可使用 vue-animated-list插件)三个指令可使用过渡动画。
只须要在元素上添加transition属性便可应用过渡动画
<div v-if="show" transition="my-transition"></div>
复制代码
在元素上添加transition属性
<div v-if="show" transition="expand"></div>
复制代码
而后添加CSS过渡动画
/* 必需 */
.expand-transition {
transition: all .3s ease;
height: 30px;
padding: 10px;
background-color: #eee;
overflow: hidden;
}
/* .expand-enter 定义进入的开始状态 */
/* .expand-leave 定义离开的结束状态 */
.expand-enter, .expand-leave {
height: 0;
padding: 0 10px;
opacity: 0;
}
复制代码
若是transition属性没有值,则默认的CSS类名是.v-transition
, .v-enter
和 .v-leave
。默认的进入和退出动画都是*-leave
的形式,若是但愿使用其它的CSS类名,则能够自定义,这样便能很好配合第三方CSS库好比animate.css
Vue.transition('bounce', {
enterClass: 'bounceInLeft',
leaveClass: 'bounceOutRight'
});
复制代码
<div v-show="ok" class="animated" transition="bounce">Watch me bounce</div>
复制代码
用法与CSS transition同样,惟一的区别是CSS transition依靠删除类名来触发transition,元素一插入文档便即刻删除类名;而CSS animation则是在CSS动画的animationend
事件中来删除类名,由于它须要等到动画运行结束才能删除,不然动画不起效
<span v-show="show" transition="bounce">Look at me!</span>
复制代码
.bounce-transition {
display: inline-block; /* 不然 scale 动画不起做用 */
}
.bounce-enter {
animation: bounce-in .5s;
}
.bounce-leave {
animation: bounce-out .5s;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
@keyframes bounce-out {
0% {
transform: scale(1);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(0);
}
}
复制代码
若是只使用了css transtion或者css animation其中一个来实现动画,则vue会根据css样式自动判断是哪一种类型的动画,从而监听对应的事件(transitionend或者animationend)。
可是有时会出现冲突,好比你使用了css animation来实现动画,可是又定义了鼠标悬停时的css transition动画,这时这两类事件都会被该元素触发,则你须要显示地声明vue应该监听哪类事件:
Vue.transition('bounce', {
// 该过渡效果将只侦听 `animationend` 事件
type: 'animation'
})
复制代码
在动画的各个阶段均可以提供回调函数,vue会在相应的动画阶段调用它们:
Vue.transition('expand', {
beforeEnter: function (el) {
el.textContent = 'beforeEnter'
},
enter: function (el) {
el.textContent = 'enter'
},
afterEnter: function (el) {
el.textContent = 'afterEnter'
},
enterCancelled: function (el) {
// handle cancellation
},
beforeLeave: function (el) {
el.textContent = 'beforeLeave'
},
leave: function (el) {
el.textContent = 'leave'
},
afterLeave: function (el) {
el.textContent = 'afterLeave'
},
leaveCancelled: function (el) {
// handle cancellation
}
})
复制代码
既然在动画的各个阶段都有回调函数,天然,咱们彻底能够不定义任何CSS,直接使用JavaScript来定义元素动画。注意,enter和leave须要调用done回调函数,不然动画将当即结束。
Vue.transition('fade', {
css: false, // 显示地声明为非css回调,vue将跳过css检测,同时也能防止css规则干扰过渡
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)
},
leaveCancelled: function (el) {
$(el).stop()
}
})
复制代码
你能够在同一元素上经过动态绑定实现不一样的过渡:
<div v-if="show" :transition="transitionName">hello</div>
复制代码
new Vue({
el: '...',
data: {
show: false,
transitionName: 'fade'
}
})
复制代码
transition
与v-for
一块儿用时能够建立渐近过渡。所谓渐进过渡,即每一个元素进入或退出都比上一元素延迟一点,不是同时进入或退出。只须要给过渡元素添加一个特性stagger
,enter-stagger
或leave-stagger
,便可添加渐进过渡:
<div v-for="item in list" transition="stagger" stagger="100"></div>
复制代码
或者提供一个钩子 stagger, enter-stagger 或 leave-stagger,以更好的控制:
Vue.transition('stagger', {
stagger: function (index) {
// 每一个过渡项目增长 50ms 延时
// 可是最大延时限制为 300ms
return Math.min(300, index * 50)
}
})
复制代码
使用组件来封装可重用的代码,使用Vue.extend()
来构造组件,使用Vue.component()
或者选项对象中的components
属性来注册组件,使用自定义标签<component-name></component-name>
来使用组件
组件的使用分为3个步骤:
var MyComponent = Vue.extend({
template: '<div>A custom component!</div>'
})
复制代码
Vue.component('my-component', MyComponent) // 全局注册
var Parent = Vue.extend({
template: '...',
components: {
// <my-component> 只能用在父组件模板内
'my-component': Child
}
})
复制代码
组件能够注册在全局,也能够注册在另外一个组件内。在哪注册则仅能在该做用域使用
Vue.extend()能够将组件的注册和定义一次写完,只须要将定义组件时传入给extend()的对象直接传递给components内便可
var Parent = Vue.extend({
template: '...',
components: {
// <my-component> 只能用在父组件模板内
'my-component': {
template: '<div>A custom component!</div>'
}
}
})
复制代码
<div id="example">
<my-component></my-component>
</div>
复制代码
new Vue()
的选项均可以在构造组件时使用,但有两个特例:data
和el
,它们在vue()构造器中是对象或者对象的引用,若是组件也这么用,将形成全部组件共享一个对象引用的结果,组件之间存在干扰,因此这两个选项须要使用函数返回新对象var MyComponent = Vue.extend({
data: function () {
return { a: 1 }
}
})
复制代码
a 不能包含其它的交互元素(如按钮,连接)
ul 和 ol 只能直接包含 li
select 只能包含 option 和 optgroup
table 只能直接包含 thead, tbody, tfoot, tr, caption, col, colgroup
tr 只能直接包含 th 和 td
复制代码
因此对模板的使用限制就很明显了,自定义元素和特殊元素不能做为像table
这样对内部元素有限制的元素的直接子元素,也不能做为像option
这样只能做为select
子元素的元素的父元素(违反这个规则将渲染不正确)
当须要将某个限制类的元素用做模板时,使用is指令来规避以上限制:
<table>
<tr is='my-component'></tr>
</table>
复制代码
组件实例的做用域是孤立的,但能够经过组件的props属性向组件传递数据:
Vue.component('child', {
template: "<p>msg</p>",
props: ['msg'] // props中数据的读写与data属性中的数据一致
});
复制代码
<child msg='hello!'></child> <!--在父组件中向子组件传递数据-->
复制代码
当使用驼峰写法声明props属性时,在使用该属性传递数据时须要写为短横线格式:
...
props: ['myProp'],
...
复制代码
<child my-prop='hello!'></child>
复制代码
使用v-bind能够在父组件向子组件中传递动态数据或者实际的值而不是字符串:
<child :my-prop="someDataFromFather"></child> <!--传递动态数据-->
<child :my-prop="{ a:1, b:2 }"></child> <!--传递实际的值-->
复制代码
绑定动态数据时,props默认是单向绑定,父组件的数据变化会传递并反映在子组件中,也可使用.async修饰符强制双向绑定,子组件中改变props的值也会反映在父组件中,单并不推荐,由于会让数据流很差理解
<child :my-prop.async="someDataFromFather"></child> <!--传递实际的值-->
复制代码
能够为props指定验证规则,props设置再也不是数组而是对象,每一个prop都是一个规定了验证要求的对象,能够设置的验证要求包括: * type:基础类型检测,能够设置为String、Number、Boolean、Function、Object、Array,当只须要这一个验证时可直接设置prop: String,若是能够是多种类型,则可设置prop: [Number, String] * required:是否必须,boolean * default:默认值 * twoWay:是否双向 * validator:自定义验证函数 * coerce:自定义转换函数,在设置以前转换值
当prop验证失败了,Vue将拒绝在子组件上设置此值,若是使用的是开发版本会抛出一条警告 另外须要注意,props
的defalut
可使用一个function
动态返回一个值,因此若是指望一个prop是function类型,则应当在匿名函数中返回这个function
除了props属性可以让父子组件之间通讯外,还有下面的一些方式可以在组件树种进行通讯
* 子组件能够经过this.$parent访问父组件
* 父组件经过this.$children访问包含了全部子组件的数组
* 根实例后代经过this.$root访问根实例(也即new Vue())
* 尽量避免使用父链进行通讯,应当尽量使用props显示声明
复制代码
Vue实现了一套自定义事件接口用于组件树通讯,独立于DOM事件 * 触发事件: * dispatch() 派发事件,事件沿着父链冒泡,子组件事件能够在父组件中监听 *
on() 监听事件 * events选项监听事件 * v-on指令监听事件
注意,Vue事件会在冒泡后第一次触发执行事件处理函数后中止冒泡,除非在事件处理函数中显式返回true
Vue官方推荐当一个子组件派发事件后,在父组件中使用子组件的时候v-on
监听,是最直观的方式,由于这样可以直观知道事件来自哪里:
<child :msg="hello!" v-on:customEvent="handler"></child>
复制代码
有时仍须要在JavaScript中直接访问子组件,可使用v-ref指令指定子组件索引
<child v-ref:profile></child>
复制代码
而后在注册它的父级中直接访问:
var child = parent.$refs.profile
复制代码
当和v-for一块儿使用时是一个数组或者对象,包含相应的子组件
slot
分发内容——父组件向子组件下发内容当你的子组件有容器的功能时,子组件的部份内容(一般是html结构)就不肯定了,好比你定义了一个列表组件,可是你但愿你的列表中每一项的内容可以在使用组件的时候给定,而不是写死在组件中,这时就须要用到slot
来向组件内分发内容,惟一须要记住的是:
父组件模板的内容在父组件做用域内编译,子组件模板中的内容在子组件做用域内编译 slot中的分发内容是在父组件中编译的
<!-- 单个slot -->
<template>
<div>
<slot></slot>
</div>
</template>
复制代码
<template>
<!-- 多个slot(具名slot) -->
<div>
<slot name="title"></slot>
<p>ajsf ah askhjdf a askjfh a fhasdkfu9fa ajsh ia uiasd gak jgasdfh la ioa akjsflai a</p>
<slot name="footer"></slot>
</div>
</template>
复制代码
<!-- 单个slot -->
<my-component>
<p>这里的全部内容都将插入组件的slot中<p>
<div>这里的全部内容都将插入组件的slot中</div>
</my-component>
复制代码
<!-- 多个slot(具名slot) -->
<my-component>
<p slot="title">这里的内容将插入组件的name="title"的slot中<p>
<div slot="footer">这里的全部内容将插入组件的name="footer"的slot中</div>
<p>这里的内容将被插入组件中未署名的slot中,若是没有未署名的slot则将被忽略</p>
</my-component>
复制代码
能够将多个组件挂在到同一个挂载点上,而后动态切换它们。使用保留的元素,将组件动态地绑定到它的is属性上:
new Vue({
el: 'body',
data: {
currentView: 'home'
},
components: {
home: { /* ... */ },
posts: { /* ... */ },
archive: { /* ... */ }
}
})
复制代码
<component :is="currentView">
<!-- 组件在 vm.currentview 变化时改变 -->
</component>
复制代码
可使用keep-alive
指令将切换出去的组件保留在内存中,避免从新渲染而且保留它的状态
<component :is="currentView" keep-alive></component>
复制代码
组件中可使用activate
钩子来在切入前作一些事情,一般是异步操做好比加载数据等等:
Vue.component('activate-example', {
activate: function (done) {
var self = this
loadDataAsync(function (data) {
self.someData = data
done()
})
}
})
复制代码
组件还可使用transition
、transition-mode
两个属性来指定动态组件的过场动画,transition的使用和普通元素一致,trnasition-mode
为可选项,不指定时默认是进入与离开的组件同时开始过渡,指定为in-out
和out-in
则分别是先进后出或先出后进。惟一须要注意的是,动态组件不能是片断实例(没有惟一的父级元素),不然transition
将不起做用
vue-router的<route-view></route-view>
即是利用了动态组件的特性,全部用于动态组件的属性均可以用于route-view
v-for
组件可使用v-for
,v-for
中的item
数据应当也只能使用组件的props
属性传递给组件
若是你的组件是须要复用的,则应当尽量与其它组件解耦,定义好清晰的公开接口:props
、事件、slot
,使用组件时使用v-bind
和v-on
的简写@
和:
,并清晰的进行缩进
可让组件只在须要时从服务器下载,vue容许将组建定义为一个工厂函数,只在第一次须要渲染时触发工厂函数从服务器下载并缓存结果供屡次渲染使用
Vue.component('async-component-example', (resolve, reject) => {
setTimeout(() => { // 伪装是在从服务器下载 O(∩_∩)O哈哈哈~
resolve({ // 将下载好的组件传递出去
template: '<div>I am async!</div>'
})
// 固然也能够调用reject()提示加载失败
}, 1000)
})
复制代码
这个功能能够与Webpack的代码分隔功能配合使用
Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 require 语法告诉 webpack
// 自动将编译后的代码分割成不一样的块,
// 这些块将经过 ajax 请求自动下载。
require(['./my-async-component'], resolve)
})
复制代码
HTML中元素的属性名是不区分大小写的,可是当在定义组件挂载名称、props属性和事件名等等须要在HTML中使用的资源时,也可使用驼峰命名(camelCase),只是使用的时候须要转成中划线命名(kebab-case)
组件能够在本身的template中递归调用本身,可是须要注意,只有拥有name属性的组件才能递归调用本身(不然在本身的模板中没有挂载名可以使用),而且要确保递归调用有终止条件
当一个组件知足如下条件时,即为片断实例
片断实例虽然能够正确渲染,可是存在以下问题:
若是组件有inline-template
属性,则挂载组件时组件标签之间中内容将被做为组件的模板进行渲染,也就是说,你能够在调用组件时动态地定义组件的模板,这给了组件极大的灵活性。但缺点是模板编译结果不能被缓存,而且模板的做用域也很差理解了
在初始化vue实例时,vue会遍历实例的data选项并使用Object.defineProperty()来将data的各个属性转换为getter/setter(存取器属性,这也是vue不支持IE8及更早版本的缘由)
使用vm.$log()方法能够将实例的data更为友好地打印到控制台
绑定到DOM中的每一个数据或指令都有一个对应的watcher,它将数据和DOM中的绑定联系起来(把属性记录为依赖):每次数据变化时,它的setter都会通知watcher,watcher再通知DOM进行更新
实例化以后响应式属性的添加:
vue只能在初始化vue实例的时候将数据转换为响应式的,若是初始化以后再向data
中添加属性,则该属性不会是响应的而是普通属性,因此vue提供了在实例建立后继续向data
添加响应属性的方法:vm.$set()
和Vue.set()
vue实例可使用$set()来继续添加响应属性
vm.$set(propName, value)
复制代码
对于普通的数据对象,可使用全局方法Vue.set()来添加响应属性
Vue.set(object, key, value)
复制代码
另外,若是向data中已有的对象上添加新的属性,好比使用Object.assign()或_.extend()添加属性,新添加的属性也不会是动态的,这时能够建立一个包含该属性的新对象,替换原有的data中对象
尽量不要使用$set
在实例化后再次添加新的属性,而是应该在vue实例选项中事先所有声明好,这会让代码更易于理解。
另外添加一个顶级响应属性会强制全部watcher从新计算,由于添加以前它不存在,也没有watcher追踪它,添加它之后以前的依赖须要从新计算,虽然这对比Angular1的脏检查性能仍是能够接受的,但仍旧能够在初始化以前所有声明好来避免这部分性能开销
vue内部使用一个队列来异步地更新DOM,若是一个watcher被屡次触发,只会推入一次到队列中,只进行必要的DOM更新。异步队列中优先使用MutationObserver
,若是不支持则使用setTimeout(fn, 0)
。
本次队列里的全部DOM更新操做,不会当即执行,而是在下次事件循环清空队列前更新。明白这一点,咱们即可以合理地使用vue给出的Vue.nextTick(callback)
方法,在更新完成DOM后作一些事情
该方法在vue实例上也有,而且更方便,由于回调的this自动指向了当前的vue实例对象
计算属性不是简单的getter,计算属性持续追踪它的依赖,只要依赖变化了,计算属性就变化。
而且计算属性会缓存计算结果,除非依赖发生变化,不然读取计算属性时只从缓存读取(性能最优)
明白这一点,就能明白为何计算属性的getter不是每次都会被调用:当你在计算属性的getter中new Date()时,并非每次的值都是当前时间,它可能不会变化,仍是过去的某个时间。若是不但愿缓存,能够在设置计算属性时关闭缓存:
computed: {
example: {
cache: false,
get: function () {
return Date.now() + this.msg
}
}
}
复制代码
然而即便关闭缓存,也仅仅是在js中读取这个属性时是实时的,DOM中的数据绑定仍旧是依赖驱动的,只有计算属性的依赖发生了变化,DOM才会更新。
除了内置指令,还能够自定义指令。使用Vue.directive(id, definition)
方法注册全局自定义指令 使用组件的directives
选项注册局部自定义指令
Vue.directive('my-directive', {
bind: function () {
// 准备工做
// 例如,添加事件处理器或只须要运行一次的高耗任务
},
update: function (newValue, oldValue) {
// 值更新时的工做
// 也会以初始值为参数调用一次
},
unbind: function () {
// 清理工做
// 例如,删除 bind() 添加的事件监听器
}
})
复制代码
当注册之后,即可以在模板中像使用内置指令同样使用自定义指令:
<div v-my-directive="someValue"></div>
复制代码
当只须要update函数时,能够只传入一个函数替代definition对象
在钩子函数中,this指向当前的指令实例,指令实例上暴露了一些有用的属性:
这些属性应当被当作只读的,不该该修改他们,即便你给指令对象添加自定义属性,也要注意不要覆盖这些内部属性
元素指令能够看作是一个轻量的组件,它是以自定义元素的形式使用指令,而不是以特性的形式。使用Vue.elementDirective(id, discription)
来注册自定义指令
Vue.elementDirective('my-directive', {
// API 同普通指令
bind: function () { // 注意使用简写函数时this的指向
// 操做this.el
}
})
复制代码
使用时便如同自定义元素:
<my -directive></my-directive>
复制代码
因此元素指令不接受参数或表达式,只能读取元素属性来进行操做
元素指令是终结性的,一旦Vue遇到一个元素指令它将跳过该院故事及其子元素,而将它们留给指令自己去操做
自定义指令能够接收一个params数组,指定一个特性列表,Vue编译器将自动提取这些特性:
<div v-example a="hi"></div>
复制代码
Vue.directive('example', {
params: ['a'],
bind: function () {
console.log(this.params.a)
}
})
复制代码
params中的属性也支持动态属性:
<div v-example :a="msg"></div>
复制代码
Vue.directive('example', {
params: ['a'],
paramWatchers: {
a: function (val, oldVal) {
console.log('a changed!')
}
}
})
复制代码
若是自定义属性的值为一个对象,当但愿对象内部属性发生变化时触发update
,则在定义指令时指定deep:true
<div v-my-directive="obj"></div>
复制代码
Vue.directive('my-directive', {
deep: true,
update: function (obj) {
// 在obj的内部属性变化时调用
}
})
复制代码
默认状况下指令中是不能向vm写入数据的,可是可使用twoWay选项开启,这样指令中就可使用set()
方法向vm回写数据,前提是指令绑定的是vm中的数据属性
Vue.directive('example', {
towWay: true,
bind: function () {
this.handler = function () {
// 将数据写回 vm
// 若是指令这样绑定 v-example="a.b.c"
// 它将用给定值设置 `vm.a.b.c`
this.set(this.value)
}.bind(this)
this.el.addEventListener('input', this.handler)
},
unbind: function () {
this.el.removeEventListener('input', this.handler)
}
})
复制代码
acceptStatement:true
可让自定义指令接受内联js语句(好比v-on
),但须要注意内联语句的反作用
Vue.directive('my-directive', {
acceptStatement: true,
update: function (fn) {
// 传入值是一个函数
// 在调用它时将在所属实例做用域内计算 "a++" 语句
}
})
复制代码
(1.0.19+)terminal: true
指定由指令来接管元素的编译(好比v-if
和v-for
)。可能还须要Vue.FragmentFactory
来编译partial。须要较好了解Vue的编译流程(2.X中编译流程略有变化),建议通读v-if
和v-for
源码来写terminal指令
普通指令默认1000,terminal指令默认2000。在API文档中查看指令优先级。流程控制指令使用用友最高优先级。
使用Vue.filter()
来注册自定义过滤器,第一个参数为过滤器名称,第二个参数为过滤器函数:
Vue.filter('wrap', function (value, begin, end) { // 过滤器函数接受任意数量参数
return begin + value + end
})
复制代码
<!-- 'hello' => 'before hello after' -->
<span v-text="message | wrap 'before' 'after'"></span>
复制代码
这个过滤器会未来自vm的数据处理后交给view显示。
过滤器不只能够处理从vm到view的数据,也能够处理从view到vm的数据。传递给Vue.filter()
的第二个参数变为一个含有read
和write
方法的对象:
Vue.filter('currencyDisplay', {
// model -> view
// 在更新 `<input>` 元素以前格式化值
read: function(val) {
return '$'+val.toFixed(2)
},
// view -> model
// 在写回数据以前格式化值
write: function(val, oldVal) {
var number = +val.replace(/[^\\\\\\\\d.]/g, '')
return isNaN(number) ? 0 : parseFloat(number.toFixed(2))
}
})
复制代码
注意:
filterBy
和orderBy
根据指定的方法过滤/排序传入的数组定义一个选项对象,而后在实例化vue时使用mixins: [optionObj1, optionObj12]
来将选项混合到当前的vue实例选项中:
// 定义一个混合对象
var myMixin = {
created: function () {
this.hello()
},
methods: {
hello: function () {
console.log('hello from mixin!')
}
}
}
// 定义一个组件,使用这个混合对象
var Component = Vue.extend({
mixins: [myMixin]
})
var component = new Component() // -> "hello from mixin!"
复制代码
默认的合并策略是:
Vue.extend()
一样使用上述策略进行合并所谓自定义选项,是除开相似data
,methods
这样vue官方提供的选项,你但愿使用的由你本身命名的选项。 一般状况下你在某个vue实例中使用了自定义选项,你可能对选项进行处理。可是若是你但愿全部的vue实例在使用这个自定义选项时都作一样处理,这时你可使用全局混合。
可使用Vue.mixin()
来注册全局混合。所谓全局混合就是一旦注册,会应用到以后建立的全部vue实例上,因此须要当心使用。但这个特性特别适合用于为自定义选项注入处理逻辑,让它表现得像官方选项同样:
// 为 `myOption` 自定义选项注入一个处理器
Vue.mixin({
created: function () {
var myOption = this.$options.myOption
if (myOption) {
console.log(myOption)
}
}
})
new Vue({
myOption: 'hello!'
})
// -> "hello!"
复制代码
混合时对自定义选项的处理只是简单地覆盖已有值,若是想用自定义逻辑合并自定义选项,则向Vue.config.optionMergeStrategies
添加一个函数:
Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) {
// 返回 mergedVal
}
复制代码
对于多数值为对象的选项,能够简单地使用 methods 所用的合并策略:
var strategies = Vue.config.optionMergeStrategies
strategies.myOption = strategies.methods
复制代码
插件、构建和对比其它框架三个主题略过,可去官方文档查看详细信息