学习vue.js的详细介绍能够在这里找到:http://cn.vuejs.org/guide/html
响应的数据绑定
Vue.js 的核心是一个响应的数据绑定系统,它让数据与 DOM 保持同步很是简单。在使用 jQuery 手工操做 DOM 时,咱们的代码经常是命令式的、重复的与易错的。Vue.js 拥抱数据驱动的视图概念。通俗地讲,它意味着咱们在普通 HTML 模板中使用特殊的语法将 DOM “绑定”到底层数据。一旦建立了绑定,DOM 将与数据保持同步。每当修改了数据,DOM 便相应地更新。这样咱们应用中的逻辑就几乎都是直接修改数据了,没必要与 DOM 更新搅在一块儿。这让咱们的代码更容易撰写、理解与维护。
组件系统
组件系统是 Vue.js 另外一个重要概念,由于它提供了一种抽象,让咱们能够用独立可复用的小组件来构建大型应用。vue
构造器
每一个 Vue.js 应用的起步都是经过构造函数 Vue 建立一个 Vue 的根实例算法
var vm = new Vue({ // 选项 })
一个 Vue 实例其实正是一个 MVVM 模式中所描述的 ViewModel - 所以在文档中常常会使用 vm 这个变量名。
在实例化 Vue 时,须要传入一个选项对象,它能够包含数据、模板、挂载元素、方法、生命周期钩子等选项。
实例生命周期
Vue 实例在建立时有一系列初始化步骤——例如,它须要创建数据观察,编译模板,建立必要的数据绑定。在此过程当中,它也将调用一些生命周期钩子,给自定义逻辑提供运行机会。例如 created 钩子在实例建立后调用:api
var vm = new Vue({ data: { a: 1 }, created: function () { // `this` 指向 vm 实例 console.log('a is: ' + this.a) } }) // -> "a is: 1"
也有一些其它的钩子,在实例生命周期的不一样阶段调用,如 compiled、 ready 、destroyed。钩子的 this 指向调用它的 Vue 实例。一些用户可能会问 Vue.js 是否有“控制器”的概念?答案是,没有。组件的自定义逻辑能够分割在这些钩子中。
生命周期图示数组
Vue.js 的模板是基于 DOM 实现的。这意味着全部的 Vue.js 模板都是可解析的有效的 HTML,且经过一些特殊的特性作了加强。Vue 模板于是从根本上不一样于基于字符串的模板,请记住这点。
插值
(1)文本
数据绑定最基础的形式是文本插值,使用 “Mustache” 语法(双大括号):浏览器
<span>Message: {{ msg }}</span>
Mustache 标签会被相应数据对象的 msg 属性的值替换。每当这个属性变化时它也会更新。
你也能够只处理单次插值,从此的数据变化就不会再引发插值更新了:缓存
<span>This will never change: {{* msg }}</span>
(2)原始的 HTML
双 Mustache 标签将数据解析为纯文本而不是 HTML。为了输出真的 HTML 字符串,须要用三 Mustache 标签:dom
<div>{{{ raw_html }}}</div>
内容以 HTML 字符串插入——数据绑定将被忽略。若是须要复用模板片段,应当使用 partials。ide
注:在网站上动态渲染任意 HTML 是很是危险的,由于容易致使 XSS 攻击。记住,只对可信内容使用 HTML 插值,永不用于用户提交的内容。
(3)HTML 特性
Mustache 标签也能够用在 HTML 特性 (Attributes) 内:函数
<div id="item-{{ id }}"></div>
注意在 Vue.js 指令和特殊特性内不能用插值。没必要担忧,若是 Mustache 标签用错了地方 Vue.js 会给出警告。
绑定表达式
放在 Mustache 标签内的文本称为绑定表达式。在 Vue.js 中,一段绑定表达式由一个简单的 JavaScript 表达式和可选的一个或多个过滤器构成。
(1)JavaScript 表达式
到目前为止,咱们的模板只绑定到简单的属性键。不过实际上 Vue.js 在数据绑定内支持全功能的 JavaScript 表达式:
{{ number + 1 }} {{ ok ? 'YES' : 'NO' }} {{ message.split('').reverse().join('') }}
这些表达式将在所属的 Vue 实例的做用域内计算。一个限制是每一个绑定只能包含单个表达式,所以下面的语句是无效的:
<!-- 这是一个语句,不是一个表达式: --> {{ var a = 1 }} <!-- 流程控制也不能够,可改用三元表达式 --> {{ if (ok) { return message } }}
(2)过滤器
Vue.js 容许在表达式后添加可选的“过滤器 (Filter) ”,以“管道符”指示:
{{ message | capitalize }}
这里咱们将表达式 message 的值“管输(pipe)”到内置的 capitalize 过滤器,这个过滤器其实只是一个 JavaScript 函数,返回大写化的值。Vue.js 提供数个内置过滤器,在后面咱们会谈到如何开发本身的过滤器。
注意管道语法不是 JavaScript 语法,所以不能在表达式内使用过滤器,只能添加到表达式的后面。
过滤器能够串联:
{{ message | filterA | filterB }}
过滤器也能够接受参数:
{{ message | filterA 'arg1' arg2 }}
过滤器函数始终以表达式的值做为第一个参数。带引号的参数视为字符串,而不带引号的参数按表达式计算。这里,字符串 'arg1' 将传给过滤器做为第二个参数,表达式 arg2 的值在计算出来以后做为第三个参数。
指令
指令 (Directives) 是特殊的带有前缀 v- 的特性。指令的值限定为绑定表达式,所以上面提到的 JavaScript 表达式及过滤器规则在这里也适用。指令的职责就是当其表达式的值改变时把某些特殊的行为应用到 DOM 上。咱们来回头看下“概述”里的例子:
<p v-if="greeting">Hello!</p>
这里 v-if 指令将根据表达式 greeting 值的真假删除/插入 <p> 元素。
(1)参数
有些指令能够在其名称后面带一个“参数” (Argument),中间放一个冒号隔开。例如,v-bind 指令用于响应地更新 HTML 特性:
<a v-bind:href="url"></a>
这里 href 是参数,它告诉 v-bind 指令将元素的 href 特性跟表达式 url 的值绑定。可能你已注意到能够用特性插值 href="{{url}}"
得到一样的结果:这样没错,而且实际上在内部特性插值会转为 v-bind 绑定。
另外一个例子是 v-on 指令,它用于监听 DOM 事件:
<a v-on:click="doSomething">
这里参数是被监听的事件的名字。咱们也会详细说明事件绑定。
(2)修饰符
修饰符 (Modifiers) 是以半角句号 . 开始的特殊后缀,用于表示指令应当以特殊方式绑定。例如 .literal 修饰符告诉指令将它的值解析为一个字面字符串而不是一个表达式:
<a v-bind:href.literal="/a/b/c"></a>
固然,这彷佛没有意义,由于咱们只须要使用 href="/a/b/c" 而没必要使用一个指令。这个例子只是为了演示语法。后面咱们将看到修饰符更多的实践用法。
(4)缩写
v- 前缀是一种标识模板中特定的 Vue 特性的视觉暗示。当你须要在一些现有的 HTML 代码中添加动态行为时,这些前缀能够起到很好的区分效果。但你在使用一些经常使用指令的时候,你会感受一直这么写实在是啰嗦。并且在构建单页应用(SPA )时,Vue.js 会管理全部的模板,此时 v- 前缀也没那么重要了。所以Vue.js 为两个最经常使用的指令 v-bind 和 v-on 提供特别的缩写:
v-bind 缩写
<!-- 完整语法 --> <a v-bind:href="url"></a> <!-- 缩写 --> <a :href="url"></a> <!-- 完整语法 --> <button v-bind:disabled="someDynamicCondition">Button</button> <!-- 缩写 --> <button :disabled="someDynamicCondition">Button</button> v-on 缩写 <!-- 完整语法 --> <a v-on:click="doSomething"></a> <!-- 缩写 --> <a @click="doSomething"></a>
它们看起来跟“合法”的 HTML 有点不一样,可是它们在全部 Vue.js 支持的浏览器中都能被正确地解析,而且不会出如今最终渲染的标记中。缩写语法彻底是可选的,不过随着一步步学习的深刻,你会庆幸拥有它们。
在模板中绑定表达式是很是便利的,可是它们实际上只用于简单的操做。模板是为了描述视图的结构。在模板中放入太多的逻辑会让模板太重且难以维护。这就是为何 Vue.js 将绑定表达式限制为一个表达式。若是须要多于一个表达式的逻辑,应当使用计算属性。
基础例子
<div id="example"> a={{ a }}, b={{ b }} </div> var vm = new Vue({ el: '#example', data: { a: 1 }, computed: { // 一个计算属性的 getter b: function () { // `this` 指向 vm 实例 return this.a + 1 } } })
结果:a=1,b=2
这里咱们声明了一个计算属性 b。咱们提供的函数将用做属性 vm.b的 getter。
console.log(vm.b) // -> 2 vm.a = 2 console.log(vm.b) // -> 3
你能够打开浏览器的控制台,修改 vm。vm.b 的值始终取决于 vm.a 的值。
你能够像绑定普通属性同样在模板中绑定计算属性。Vue 知道 vm.b 依赖于 vm.a,所以当 vm.a 发生改变时,依赖于 vm.b 的绑定也会更新。并且最妙的是咱们是声明式地建立这种依赖关系:计算属性的 getter 是干净无反作用的,所以也是易于测试和理解的。
计算属性 vs. $watch
Vue.js 提供了一个方法 $watch,它用于观察 Vue 实例上的数据变更。当一些数据须要根据其它数据变化时, $watch 很诱人 —— 特别是若是你来自 AngularJS。不过,一般更好的办法是使用计算属性而不是一个命令式的 $watch 回调。考虑下面例子:
<div id="demo">{{fullName}}</div> var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' } }) vm.$watch('firstName', function (val) { this.fullName = val + ' ' + this.lastName }) vm.$watch('lastName', function (val) { this.fullName = this.firstName + ' ' + val })
API:computed
类型: Object
详细:实例计算属性。getter 和 setter 的 this 自动地绑定到实例。
上面代码是命令式的重复的。跟计算属性对比:
var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar' }, computed: { fullName: function () { return this.firstName + ' ' + this.lastName } } })
这样更好,不是吗?
计算 setter
计算属性默认只是 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] } } } // ...
如今在调用 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstName 和 vm.lastName 也会有相应更新。
数据绑定一个常见需求是操做元素的 class 列表和它的内联样式。由于它们都是 attribute,咱们能够用 v-bind 处理它们:只须要计算出表达式最终的字符串。不过,字符串拼接麻烦又易错。所以,在 v-bind 用于 class 和 style 时,Vue.js 专门加强了它。表达式的结果类型除了字符串以外,还能够是对象或数组。
绑定 HTML Class
尽管能够用 Mustache 标签绑定 class,好比 class="{{ className }}",可是咱们不推荐这种写法和 v-bind:class 混用。二者只能选其一!
对象语法
咱们能够传给 v-bind:class 一个对象,以动态地切换 class。注意 v-bind:class 指令能够与普通的 class 特性共存:
<div class="static" v-bind:class="{ 'class-a': isA, 'class-b': isB }"></div> data: { isA: true, isB: false }
渲染为:
<div class="static class-a"></div>
当 isA 和 isB 变化时,class 列表将相应地更新。例如,若是 isB 变为 true,class 列表将变为 "static class-a class-b"。
你也能够直接绑定数据里的一个对象:
<div v-bind:class="classObject"></div> data: { classObject: { 'class-a': true, 'class-b': false } }
咱们也能够在这里绑定一个返回对象的计算属性。这是一个经常使用且强大的模式。
数组语法
咱们能够把一个数组传给 v-bind:class,以应用一个 class 列表:
<div v-bind:class="[classA, classB]"> data: { classA: 'class-a', classB: 'class-b' }
渲染为:
<div class="class-a class-b"></div>
若是你也想根据条件切换列表中的 class,能够用三元表达式:
<div v-bind:class="[classA, isB ? classB : '']">
此例始终添加 classA,可是只有在 isB 是 true 时添加 classB 。
不过,当有多个条件 class 时这样写有些繁琐。在 1.0.19+ 中,能够在数组语法中使用对象语法:
<div v-bind:class="[classA, { classB: isB, classC: isC }]">
绑定内联样式
对象语法
v-bind:style 的对象语法十分直观——看着很是像 CSS,其实它是一个 JavaScript 对象。CSS 属性名能够用驼峰式(camelCase)或短横分隔命名(kebab-case):
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div> data: { activeColor: 'red', fontSize: 30 }
直接绑定到一个样式对象一般更好,让模板更清晰:
<div v-bind:style="styleObject"></div> data: { styleObject: { color: 'red', fontSize: '13px' } }
一样的,对象语法经常结合返回对象的计算属性使用。
数组语法
v-bind:style 的数组语法能够将多个样式对象应用到一个元素上:
<div v-bind:style="[styleObjectA, styleObjectB]">
自动添加前缀
当 v-bind:style 使用须要厂商前缀的 CSS 属性时,如 transform,Vue.js 会自动侦测并添加相应的前缀。
v-if
在字符串模板中,如 Handlebars,咱们得像这样写一个条件块:
<!-- Handlebars 模板 --> {{#if ok}} <h1>Yes</h1> {{/if}}
在 Vue.js,咱们使用 v-if 指令实现一样的功能:
<h1 v-if="ok">Yes</h1>
也能够用 v-else 添加一个 “else” 块:
<h1 v-if="ok">Yes</h1> <h1 v-else>No</h1>
template v-if
由于 v-if 是一个指令,须要将它添加到一个元素上。可是若是咱们想切换多个元素呢?此时咱们能够把一个 <template> 元素当作包装元素,并在上面使用 v-if,最终的渲染结果不会包含它。
<template v-if="ok"> <h1>Title</h1> <p>Paragraph 1</p> <p>Paragraph 2</p> </template>
v-show
另外一个根据条件展现元素的选项是 v-show 指令。用法大致上同样:
<h1 v-show="ok">Hello!</h1>
不一样的是有 v-show 的元素会始终渲染并保持在 DOM 中。v-show 是简单的切换元素的 CSS 属性 display。
注意 v-show 不支持 <template> 语法。
v-else
能够用 v-else 指令给 v-if 或 v-show 添加一个 “else 块”:
<div v-if="Math.random() > 0.5"> Sorry </div> <div v-else> Not sorry </div>
v-else 元素必须当即跟在 v-if 或 v-show 元素的后面——不然它不能被识别。
组件警告
将 v-show 用在组件上时,由于指令的优先级 v-else 会出现问题。所以不要这样作:
<custom-component v-show="condition"></custom-component> <p v-else>这可能也是一个组件</p>
用另外一个 v-show 替换 v-else:
<custom-component v-show="condition"></custom-component> <p v-show="!condition">这可能也是一个组件</p>
这样就能够达到 v-if 的效果。
v-if vs. v-show
在切换 v-if 块时,Vue.js 有一个局部编译/卸载过程,由于 v-if 之中的模板也可能包括数据绑定或子组件。v-if 是真实的条件渲染,由于它会确保条件块在切换当中合适地销毁与重建条件块内的事件监听器和子组件。
v-if 也是惰性的:若是在初始渲染时条件为假,则什么也不作——在条件第一次变为真时才开始局部编译(编译会被缓存起来)。
相比之下,v-show 简单得多——元素始终被编译并保留,只是简单地基于 CSS 切换。
通常来讲,v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗。所以,若是须要频繁切换 v-show 较好,若是在运行时条件不大可能改变 v-if 较好。
v-for
可使用 v-for 指令基于一个数组渲染一个列表。这个指令使用特殊的语法,形式为 item in items,items 是数据数组,item 是当前数组元素的别名:
示例:
<ul id="example-1"> <li v-for="item in items"> {{ item.message }} </li> </ul> var example1 = new Vue({ el: '#example-1', data: { items: [ { message: 'Foo' }, { message: 'Bar' } ] } })
结果:
·Foo ·Bar
在 v-for 块内咱们能彻底访问父组件做用域内的属性,另有一个特殊变量 $index,正如你猜到的,它是当前数组元素的索引:
<ul id="example-2"> <li v-for="item in items"> {{ parentMessage }} - {{ $index }} - {{ item.message }} </li> </ul> var example2 = new Vue({ el: '#example-2', data: { parentMessage: 'Parent', items: [ { message: 'Foo' }, { message: 'Bar' } ] } })
结果:
·Parent-0-Foo ·Parent-1-Bar
另外,你能够为索引指定一个别名(若是 v-for 用于一个对象,则能够为对象的键指定一个别名):
<div v-for="(index, item) in items"> {{ index }} {{ item.message }} </div>
从 1.0.17 开始可使用 of 分隔符,更接近 JavaScript 遍历器语法:
<div v-for="item of items"></div>
template v-for
相似于 template v-if,也能够将 v-for 用在 <template> 标签上,以渲染一个包含多个元素的块。例如:
<ul> <template v-for="item in items"> <li>{{ item.msg }}</li> <li class="divider"></li> </template> </ul>
数组变更检测
(1)变异方法
Vue.js 包装了被观察数组的变异方法,故它们能触发视图更新。被包装的方法有:
push() pop() shift() unshift() splice() sort() reverse()
你能够打开浏览器的控制台,用这些方法修改上例的 items 数组。例如:example1.items.push({ message: 'Baz' })
。
(2)替换数组
变异方法,如名字所示,修改了原始数组。相比之下,也有非变异方法,如 filter(), concat() 和 slice(),不会修改原始数组而是返回一个新数组。在使用非变异方法时,能够直接用新数组替换旧数组:
example1.items = example1.items.filter(function (item) { return item.message.match(/Foo/) })
可能你以为这将致使 Vue.js 弃用已有 DOM 并从新渲染整个列表——幸运的是并不是如此。 Vue.js 实现了一些启发算法,以最大化复用 DOM 元素,于是用另外一个数组替换数组是一个很是高效的操做。
(3)track-by
有时须要用全新对象(例如经过 API 调用建立的对象)替换数组。由于 v-for 默认经过数据对象的特征来决定对已有做用域和 DOM 元素的复用程度,这可能致使从新渲染整个列表。可是,若是每一个对象都有一个惟一 ID 的属性,即可以使用 track-by 特性给 Vue.js 一个提示,Vue.js 于是能尽量地复用已有实例。
例如,假定数据为:
{ items: [ { _uid: '88f869d', ... }, { _uid: '7496c10', ... } ] }
而后能够这样给出提示:
<div v-for="item in items" track-by="_uid"> <!-- content --> </div>
而后在替换数组 items 时,若是 Vue.js 遇到一个包含 _uid: '88f869d' 的新对象,它知道它能够复用这个已有对象的做用域与 DOM 元素。
track-by $index
若是没有惟一的键供追踪,可使用 track-by="$index",它强制让 v-for 进入原位更新模式:片段不会被移动,而是简单地以对应索引的新值刷新。这种模式也能处理数据数组中重复的值。
这让数据替换很是高效,可是也会付出必定的代价。由于这时 DOM 节点再也不映射数组元素顺序的改变,不能同步临时状态(好比 <input> 元素的值)以及组件的私有状态。所以,若是 v-for 块包含 <input> 元素或子组件,要当心使用 track-by="$index"
问题
由于 JavaScript 的限制,Vue.js 不能检测到下面数组变化:
直接用索引设置元素,如 vm.items[0] = {}; 修改数据的长度,如 vm.items.length = 0。
为了解决问题 (1),Vue.js 扩展了观察数组,为它添加了一个 $set() 方法:
// 与 example1.items[0] = ...
相同,可是能触发视图更新
example1.items.$set(0, { childMsg: 'Changed!'})
至于问题 (2),只需用一个空数组替换 items。
除了 $set(), Vue.js 也为观察数组添加了 $remove() 方法,用于从目标数组中查找并删除元素,在内部它调用 splice() 。所以,没必要这样:
var index = this.items.indexOf(item) if (index !== -1) { this.items.splice(index, 1) }
只用这样:
this.items.$remove(item)
(4)使用 Object.freeze()
在遍历一个数组时,若是数组元素是对象而且对象用 Object.freeze() 冻结,你须要明确指定 track-by。在这种状况下若是 Vue.js 不能自动追踪对象,将给出一条警告。
对象 v-for
也可使用 v-for 遍历对象。除了 $index 以外,做用域内还能够访问另一个特殊变量 $key。
<ul id="repeat-object" class="demo"> <li v-for="value in object"> {{ $key }} : {{ value }} </li> </ul> new Vue({ el: '#repeat-object', data: { object: { FirstName: 'John', LastName: 'Doe', Age: 30 } } })
结果:
·FirstName:'John' ·LastName:'Doe' ·Age:30
也能够给对象的键提供一个别名:
<div v-for="(key, val) in object"> {{ key }} {{ val }} </div>
在遍历对象时,是按 Object.keys() 的结果遍历,可是不能保证它的结果在不一样的 JavaScript 引擎下是一致的。
(5)值域 v-for
v-for 也能够接收一个整数,此时它将重复模板数次。
<div> <span v-for="n in 10">{{ n }} </span> </div>
结果:
0123456789
显示过滤/排序的结果
有时咱们想显示过滤/排序过的数组,同时不实际修改或重置原始数据。有两个办法:
建立一个计算属性,返回过滤/排序过的数组; 使用内置的过滤器 filterBy 和 orderBy。
计算属性有更好的控制力,也更灵活,由于它是全功能 JavaScript。可是一般过滤器更方便,详细见 API。