Vue 实例
属性和方法
-
每个 Vue 实例都会代理其
data
对象里所有的属性:var data = { a: 1 } var vm = new Vue({ data: data }) vm.a === data.a // -> true // 设置属性也会影响到原始数据 vm.a = 2 data.a // -> 2 // ... 反之亦然 data.a = 3 vm.a // -> 3
只有被代理的属性是响应的。在实例创建之后添加新的属性到实例上,它不会触发视图更新。
Vue 实例暴露了一些有用的实例属性和方法。这些属性和方法都有前缀
$
,以便于代理的数据属性区分。
实例生命周期
-
Vue 实例在其生命周期的不同阶段会调用不同的钩子,如
created
、compiled
、ready
、destroy
。钩子的this
指向调用它的 Vue 实例。var vm = new Vue({ data: { a: 1 }, created: function () { // `this` 指向 vm 实例 console.log('a is: ' + this.a) } }) // -> "a is: 1"
-
生命周期图示
数据绑定语法
插值
文本
-
处理单次插值,今后的数据变化不引起插值更新。
<span>This will never change: {{* msg }}</span>
原始的 HTML
-
使用三 Mustache 标签输出真的 HTML 字符串。
<div>{{{ raw_html }}}</div>
内容以 HTML 字符串插入时,**数据绑定将被忽略 **。如果要复用模板片段,应当使用 partials
只对可信内容使用 HTML 插值,永不用于用户提交的内容。
HTML 特性
-
Mustache 标签也可以用在 HTML 特性 (attribute) 内。
<div id="item-{{ id }}"></div>
Vue.js 指令和特殊特性内不能用插值。
绑定表达式
JavaScript 表达式
- Vue.js 在数据绑定内支持全功能的 JavaScript 表达式。表达式将在 Vue 实例的作用域内计算。
- 每个绑定只能包含单个表达式。
过滤器
-
Vue.js 允许在表达式后添加可选的“过滤器”,以
|
(管道符)指示。{{ message | capitalize }}
管道语法不是 JavaScript 语法,不能在表达式内使用过滤器,只能添加到表达式后边。
-
过滤器可以串联,也可以接受参数。过滤器函数始终以表达式的值作为第一个参数。
{{ message | filterA | filterB }} {{ message | filterA 'arg2' arg3 }}
指令
修饰符
-
修饰符是以半角句号
.
开始的特殊后缀,用于表示指令应当以特殊方式绑定。<a v-bind:href.literal="/a/b/c"></a>
Vue.js 为两个最常用的指令 v-bind
和 v-on
提供特别版的缩写:
<!-- v-bind -->
<a v-bind:href="url"></a>
<a :href="url"></a>
<!-- v-on -->
<a v-on:click="doSomething"></a>
<a @click="doSomething"></a>
计算属性
- 在模板中要使用多余一个表达式的逻辑时,应当使用计算属性。
基础例子
var vm = new Vue({
el: '#example',
data: {
a: 1
},
computed: {
// 一个计算属性的 getter
b: function () {
// `this` 指向 vm 实例
return this.a + 1
}
}
})
<div id="example">
a={{ a }}, b={{ b }}
</div>
计算 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]
}
}
}
// ...
- 规定 setter 之后,调用
vm.fullName = 'John Doe'
时,setter 会被调用,vm.firstName
和vm.lastName
也会有相应更新。
Class 与 Style 绑定
- 在
v-bind
用于class
和style
时,Vue.js 专门增强了它。表达式的结果类型除了字符串之外,还可以是对象或数组。
绑定 HTML 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>
也可以直接绑定数据里的一个对象。
数组语法
-
把一个数组传递给
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 : '']">
在 1.0.19+ 中,可以在数组语法中使用对象语法。
<div v-bind:class="[classA, { classB: isB, classC: isC }]">
绑定内联样式
对象语法
-
v-bind:style
是一个 JavaScript 对象。其中的 CSS 属性名可以用驼峰式,也可以用短横分割命名。不过,更推荐直接绑定一个样式对象<div v-bind:style="styleObject"></div>
data: { styleObject: { color: 'red', fontSize: '13px' } }
数组语法
-
v-bind:style
的数组语法还可以将多个样式对象应用到一个元素上。<div v-bind:style="[styleObjectA, styleObjectB]">
自动添加前缀
- Vue.js 会自动为需要添加厂商前缀的 CSS 属性添加厂商前缀。
条件渲染
v-if
-
使用
v-if
控制元素切换。<h1 v-if="ok">Yes</h1> <h1 v-else>No</h1>
template 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
与v-if
用法大体一样,不同的是有v-show
的元素会始终渲染并保持在 DOM 中�,其变化只是简单地切换元素的 CSS 属性display
。 -
v-show
不支持<template>
语法。
组件警告
-
将
v-show
用在组件上时,因为指令的优先级v-else
会出现问题。因此应当使用另一个v-show
替换v-else
。<custom-component v-show="condition"></custom-component> <p v-show="!condition">这可能也是一个组件</p>
v-if vs. v-show
-
v-if
是真实的条件渲染,因为它会确保条件块在切换当中合适地销毁与重建条件块内的时间监听器和子组件。 -
v-if
具有惰性。如果在初始渲染时条件为假,则什么也不做;在条件第一次变为真时才开始局部编译。相比之下v-show
元素始终被编译并保留。 - 一般来说,
v-if
有更高的切换消耗而v-show
有更高的初始渲染消耗。因此,如果需要频繁切换v-show
较好,如果在运行时条件不大可能改变v-if
较好。
列表渲染
v-for
-
在
v-for
块内我们能完全访问父组件作用域内的属性,另有一个特殊变量$index
作为当前数组元素的索引(从 0 开始)。<ul id="example-2"> <li v-for="item in items"> {{ parentMessage }} - {{ $index }} - {{ item.message }} </li> </ul>
-
可以为索引指定一个别名(如果
v-for
用于一个对象,则可以为对象的键指定一个别名)<div v-for="(index, item) in items"> {{ index }} {{ item.message }} </div>
从 1.0.17 开始可以使用
of
替代in
以更接近 JavaScript 遍历器的语法。
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>
数组变动检测
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
即可。-
Vue.js 同时为观察数组扩展了
$remove()
方法,用于从目标数组中查找并删除元素,而无需使用splice()
。this.items.$remove(item)
在遍历一个数组时,如果数组元素是对象并且对象用
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 } } })
也可以给对象的键提供一个别名:
<div v-for="(key, val) in object"> {{ key }} {{ val }} </div>
遍历对象使用
Object.keys()
的结果遍历,但并不能保证它的结果在不同的 JavaScript 引擎下一致。
值域 v-for
-
v-for
可以接收一个整数,此时它将重复模板数次。<div> <span v-for="n in 10">{{ n }} </span> </div>
显示过滤 / 排序的结果
创建一个计算属性,返回过滤 / 排序过的数组。
-
使用内置过滤器:
filterBy
和orderBy
(详见 API)。<div v-for="item in items | filterBy 'hello'">
<ul> <li v-for="user in users | orderBy 'name'"> {{ user.name }} </li> </ul>
方法与事件处理器
方法处理器
<div id="example">
<button v-on:click="greet">Greet</button>
</div>
-
event
是原生 DOM 事件,使用event.target
访问 DOM 。
内联语句处理器
<div id="example-2">
<button v-on:click="say('hi')">Say Hi</button>
<button v-on:click="say('what')">Say What</button>
</div>
new Vue({
el: '#example-2',
methods: {
say: function (msg) {
alert(msg)
}
}
})
-
如果需要在内联语句中访问原生 DOM 事件,可以使用特殊变量
$event
。<button v-on:click="say('hello!', $event)">Submit</button>
methods: { say: function (msg, event) { // 现在我们可以访问原生事件对象 event.preventDefault() } }
事件修饰符
- 将方法从处理 DOM 事件中解放出来,只专注于数据逻辑。为此 Vue.js 为
v-on
提供两个事件修饰符:.prevent
和.stop
。
```
<!-- 阻止单击事件冒泡 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat">
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
```
1.0.16 添加了两个额外的修饰符:
```
<!-- 添加事件侦听器时使用 capture 模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div v-on:click.self="doThat">...</div>
```
按键修饰符
-
Vue.js 允许为
v-on
添加按键修饰符,同时为常用的 KeyCode 提供了别名。<!-- 只有在 keyCode 是 13 时调用 vm.submit() --> <input v-on:keyup.13="submit">
<!-- 同上 --> <input v-on:keyup.enter="submit"> <!-- 缩写语法 --> <input @keyup.enter="submit">
-
1.0.17+ 可以自定义按键别名。
// 可以使用 @keyup.f1 Vue.directive('on').keyCodes.f1 = 112
为什么在 HTML 中监听事件
- 当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。
表单控件绑定
基础用法
Checkbox
-
多个勾选框,绑定到同一个数组。
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames"> <label for="jack">Jack</label> <input type="checkbox" id="john" value="John" v-model="checkedNames"> <label for="john">John</label> <input type="checkbox" id="mike" value="Mike" v-model="checkedNames"> <label for="mike">Mike</label> <br> <span>Checked names: {{ checkedNames | json }}</span>
new Vue({ el: '...', data: { checkedNames: [] } })
-
多选(绑定到一个数组)。
<select v-model="selected" multiple> <option selected>A</option> <option>B</option> <option>C</option> </select> <br> <span>Selected: {{ selected | json }}</span>
-
动态选项,用
v-for
渲染。<select v-model="selected"> <option v-for="option in options" v-bind:value="option.value"> {{ option.text }} </option> </select> <span>Selected: {{ selected }}</span>
new Vue({ el: '...', data: { selected: 'A', options: [ { text: 'One', value: 'A' }, { text: 'Two', value: 'B' }, { text: 'Three', value: 'C' } ] } })
绑定 value
- 对于单选按钮,勾选框及选择框选项,
v-model
绑定的 value 通常是静态字符串(对于勾选框是逻辑值);若想绑定 value 到 Vue 实例的一个动态属性上,可以用v-bind
实现,并且这个属性的值可以不是字符串。
Checkbox
<input
type="checkbox"
v-model="toggle"
v-bind:true-value="a"
v-bind:false-value="b">
// 当选中时
vm.toggle === vm.a
// 当没有选中时
vm.toggle === vm.b
Radio
<input type="radio" v-model="pick" v-bind:value="a">
// 当选中时
vm.pick === vm.a
Select Options
<select v-model="selected">
<!-- 对象字面量 -->
<option v-bind:value="{ number: 123 }">123</option>
</select>
// 当选中时
typeof vm.selected // -> 'object'
vm.selected.number // -> 123
参数特性
lazy
- 在默认情况下,
v-model
在input
事件中同步输入框值与数据,可以添加一个特性lazy
,从而改到在change
事件中同步。
number
- 如果想自动将用户的输入转为 Number 类型(如果原值的转换结果为 NaN 则返回原值),可以添加一个特性
number
。
<input v-model="age" number>
debounce
-
debounce
设置一个最小的延时,在每次敲击之后延时同步输入框的值与数据。如果每次更新都要进行高耗操作(例如在输入提示中 Ajax 请求),它较为有用。<input v-model="msg" debounce="500">
-
debounce
参数不会延迟 input 事件:它延迟“写入”底层数据。因此在使用debounce
时应当用vm.$watch()
相应数据的变化。若想延迟 DOM 事件,应当使用 debounce 过滤器。<input @keyup="onKeyup | debounce 500">
过渡
- 应用过渡效果,要在目标元素上使用
transition
特性。<div v-if="show" transition="my-transition"></div>
-
transition
特性可以与下面资源一起用:v-if
v-show
-
v-for
(只在插入和删除时触发,使用 vue-animated-list 插件) - 动态组件
- 在组件的根节点上,并且被 Vue 实例 DOM 方法(如
vm.$appendTo(el)
)触发。
- 当插入或删除带有过渡的元素时,Vue 将:
- 尝试以 ID
"my-transition"
查找 JavaScript 过渡钩子对象 —— 通过Vue.transition(id, hooks)
或transitions
选项注册。如果找到了,将在过渡的不同阶段调用相应的钩子。 - 自动嗅探目标元素是否有 CSS 过渡或动画,并在合适时添加 / 删除 CSS 类名。
- 如果没有找到 JavaScript 钩子并且也没有检测到 CSS 过渡 / 动画,DOM操作(插入 / 删除)在下一帧中立即执行。
- 尝试以 ID
CSS 过渡
示例
<div v-if="show" transition="expand">hello</div>
/* 必需 */
.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;
}
-
可以在同一元素上通过动态绑定实现不同的过渡。
<div v-if="show" :transition="transitionName">hello</div>
new Vue({ el: '...', data: { show: false, transitionName: 'fade' } })
-
可以提供 JavaScript 钩子。
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 类名
- 类名的添加和切换取决于
transition
特性的值,比如transition="fade"
,会有三个 CSS 类名:-
.fade-transition
始终保留在元素上。 -
.fade-enter
定义进入过渡的开始状态,只应用一帧然后立即删除。 -
.fade-leave
定义离开过渡的结束状态。在离开过渡开始时生效,在它结束后删除。
-
- 如果
transition
特性没有值,类名默认是.v-transition
,v-enter
,v-leave
。
自定义过渡类名
- 自定义过渡类名会覆盖默认的类名。
显式声明 CSS 过渡类型
-
可能希望一个元素同时带有两种类型的动画,此时要显式的声明期望 Vue 处理的动画类型(
animation
或者transition
)。Vue.transition('bounce', { // 该过渡效果将只侦听 `animationend` 事件 type: 'animation' })
CSS 动画
- 在动画中
v-center
类名在节点插入 DOM 后不会立即删除,而是在animationed
事件触发时删除。
JavaScript 过渡
- 只使用 JavaScript 钩子,不用定义任何 CSS 规则。当只使用 JavaScript 过渡时,
enter
和leave
钩子需要调用done
回调,否则他们将被同步调用,过渡将立即结束。 - 为 JavaScript 过渡显式声明
css: false
以使 Vue.js 跳过 CSS 检测。
渐进过渡
-
transition
与v-for
一起使用时可以创建渐进式过渡。给过渡元素添加一个特性stagger
、enter-stagger
或leave-stagger
。或者提供一个钩子stagger
、enter-stagger
或leave-stagger
。<div v-for="item in list" transition="stagger" stagger="100"></div>
Vue.transition('stagger', { stagger: function (index) { // 每个过渡项目增加 50ms 延时 // 但是最大延时限制为 300ms return Math.min(300, index * 50) } })
组件
使用组件
注册
用
Vue.extend()
创建一个组件构造器。-
用
Vue.component(tag, constructor)
注册。var MyComponent = Vue.extend({ // 选项... }) // 全局注册组件,tag 为 my-component Vue.component('my-component', MyComponent)
组件在注册之后,便可以在父实例的模块中以自定义元素
<my-component>
的形式使用。注意自定义元素的作用只是作为一个挂载点。可以用实例选项
replace
决定是否替换。
局部注册
-
可以用实例选项
components
注册局部组件。var Child = Vue.extend({ /* ... */ }) var Parent = Vue.extend({ template: '...', components: { // <my-component> 只能用在父组件模板内 'my-component': Child } })
注册语法糖
-
直接传入选项对象而不是构造器给
Vue.component()
和component
选项,Vue.js 会在背后自动调用Vue.extend()
。// 在一个步骤中扩展与注册 Vue.component('my-component', { template: '<div>A custom component!</div>' }) // 局部注册也可以这么做 var Parent = Vue.extend({ components: { 'my-component': { template: '<div>A custom component!</div>' } } })
组件选项问题
-
传入 Vue 构造器的多数选项可以用在
Vue.extend()
中,但如果简单的把一个对象作为data
或el
传递给Vue.extend()
var data = { a: 1 } var MyComponent = Vue.extend({ data: data })
MyComponent
所有的实例都会共享同一个data
对象(或者el
对象)。 -
应当使用一个函数作为
data
选项,让这个函数返回一个新的对象。var MyComponent = Vue.extend({ data: function () { return { a: 1 } } })
模板解析
-
一些 HTML 元素对什么元素可以放在它里面有限制,比如:
-
a
不能包含其它的交互元素(如按钮,链接) -
ul
和ol
只能直接包含li
-
select
只能包含option
和optgroup
-
table
只能直接包含thead
,tbody
,tfoot
,tr
,caption
,col
,colgroup
-
tr
只能直接包含th
和td
。
-
不能依赖自定义组件在浏览器验证之前的展开结果。
自定义标签(包括自定义元素和特殊标签,如
<component>
、<template>
� 、<partial>
)不能用在ul
,select
,table
等对内部元素有限制的标签内。-
对于自定义元素,应当使用
is
属性。<table> <tr is="my-component"></tr> </table>
-
<template>
不能用在<table>
内,应当使用<tbody>
,<table>
可以有多个<tbody>
。<table> <tbody v-for="item in items"> <tr>Even row</tr> <tr>Odd row</tr> </tbody> </table>
Props
使用 props 传递数据
-
组件实例的作用域是孤立的,不能也不应该在子组件的模板内直接引用父组件的数据,可以使用
props
把数据传递给子组件。Vue.component('child', { // 声明 props props: ['msg'], // prop 可以用在模板内 // 可以用 `this.msg` 设置 template: '<span>{{ msg }}</span>' })
<child msg="hello!"></child>
camelCase vs. kebab-case
-
名字形式为 camelCase 的 prop 用作特性时,需要转变为 kebab-case。
Vue.component('child', { // camelCase in JavaScript props: ['myMessage'], template: '<span>{{ myMessage }}</span>' })
<!-- kebab-case in HTML --> <child my-message="hello!"></child>
动态 Props
-
可以用
v-bind
绑定动态 Props 到父组件的数据。每当父组件数据变化时,也会传导给子组件。<div> <input v-model="parentMsg"> <br> <child v-bind:my-message="parentMsg"></child> </div>
字面量语法 vs. 动态语法
-
字面 prop
<!-- 传递了一个字符串 "1" --> <comp some-prop="1"></comp>
-
动态 prop (使用动态语法)
<!-- 传递实际的数字 --> <comp :some-prop="1"></comp>
Prop 绑定类型
prop 默认是单向绑定的。当父组件的属性变化时,将传导给子组件。
-
可以使用
.sync
或.once
绑定修饰符 显式地强制双向或单次绑定。<!-- 默认为单向绑定 --> <child :msg="parentMsg"></child> <!-- 双向绑定 --> <child :msg.sync="parentMsg"></child> <!-- 单次绑定 --> <child :msg.once="parentMsg"></child>
双向绑定会把子组件的
msg
属性同步回父组件的parentMsg
属性。单次绑定在建立之后不会同步之后的变化。如果 prop 是一个对象或数组,是按引用传递。在子组件内修改它会影响父组件的状态,不管使用哪种绑定类型。
Prop 验证
-
为 props 加入验证要求,以确保其他人正确使用组件。 此时 props 的值是一个对象,包含验证要求。
Vue.component('example', { props: { // 基础类型检测 (`null` 意思是任何类型都可以) propA: Number, // 多种类型 (1.0.21+) propM: [String, Number], // 必需且是字符串 propB: { type: String, required: true }, // 数字,有默认值 propC: { type: Number, default: 100 }, // 对象/数组的默认值应当由一个函数返回 propD: { type: Object, default: function () { return { msg: 'hello' } } }, // 指定这个 prop 为双向绑定 // 如果绑定类型不对将抛出一条警告 propE: { twoWay: true }, // 自定义验证函数 propF: { validator: function (value) { return value > 10 } }, // 转换函数(1.0.12 新增) // 在设置值之前转换值 propG: { coerce: function (val) { return val + '' // 将值转换为字符串 } }, propH: { coerce: function (val) { return JSON.parse(val) // 将 JSON 字符串转换为对象 } } } })
当 prop 验证失败了,Vue 将拒绝在子组件上设置此值,如果使用的是开发版本会抛出一条警告。
具体可以参阅 Vue.js 的相关文档。
父子组件通信
父链
- 组件可以用
this.$parent
访问它的父组件。根实例的后代可以用this.$root
访问它。父组件有一个数组this.$children
,包含它所有的子元素。 - 子组件应当避免直接依赖父组件的数据,尽量显式地使用
props
传递数据。 - 不要再子组件中修改父组件的状态。
自定义事件
- 每一个 Vue 实例都是一个事件触发器。
- 使用
$on()
监听事件; - 使用
$emit()
在它上面触发事件; - 使用
$dispatch()
派发事件,事件沿着父链冒泡; - 使用
$broadcast()
广播事件,事件向下传导给所有的后代。
- 使用
- 不同于 DOM 事件,Vue 事件在冒泡过程中第一次触发回调之后自动停止冒泡,除非回调明确返回
true
。
使用 v-on 绑定自定义事件
-
在模板中子组件用到的地方声明事件处理器。
<child v-on:child-msg="handleIt"></child>
子组件索引
-
可以使用
v-ref
为子组件指定一个索引 ID 。<div id="parent"> <user-profile v-ref:profile></user-profile> </div>
var parent = new Vue({ el: '#parent' }) // 访问子组件 var child = parent.$refs.profile
v-ref
和v-for
一起用时,ref 是一个数组或对象,包含相应的子组件。
使用 Slot 分发内容
- 为了让组件可以组合(内容分发),Vue.js 实现了一个内容分发 API ,使用特殊的
<slot>
元素作为原始内容的插槽。
编译作用域
组件作用域:父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译。
-
以下模板
<child-component> {{ msg }} </child-component>
msg
应当是绑定到父组件的数据(假定someChildProperty
是子组件的属性)。 -
一个常见的错误:
<!-- 无效 --> <child-component v-show="someChildProperty"></child-component>
父组件模板不应该知道子组件的状态,应当在子组件模板中这样做:
Vue.component('child-component', { // 有效,因为是在正确的作用域内 template: '<div v-show="someChildProperty">Child</div>', data: function () { return { someChildProperty: true } } })
类似地,分发内容也是在父组件作用域内编译。
单个 Slot
父组件的内容将被抛弃,除非子组件模板包含
<slot>
。如果子组件模板只有一个没有特性的 slot ,父组件的整个内容将插到 slot 所在的地方并替换它。<slot>
标签的内容视为回退内容,在子组件的作用域内编译。当宿主元素为空并且没有内容供插入时显示回退内容。-
一个例子
my-component
组件模板:<div> <h1>This is my component!</h1> <slot> 如果没有分发内容则显示我。 </slot> </div>
父组件模板:
<my-component> <p>This is some original content</p> <p>This is some more original content</p> </my-component>
渲染结果
<div> <h1>This is my component!</h1> <p>This is some original content</p> <p>This is some more original content</p> </div>
具名 Slot
<slot>
元素可以用一个特殊属性name
来配置如何分发内容。可以有一个匿名 slot 作为默认 slot。如果没有默认 slot ,找不到匹配的内容片段将被抛弃。
-
一个例子
multi-insertion
组件:<div> <slot name="one"></slot> <slot></slot> <slot name="two"></slot> </div>
父组件模板:
<multi-insertion>
<p slot="one">One</p>
<p slot="two">Two</p>
<p>Default A</p>
</multi-insertion>
```
渲染结果:
```
<div>
<p slot="one">One</p>
<p>Default A</p>
<p slot="two">Two</p>
</div>
```
动态组件
-
多个组件可以使用同一个挂载点,然后动态地进行切换。使用保留的
<component>
元素,动态地绑定到它的is
特性。new Vue({ el: 'body', data: { currentView: 'home' }, components: { home: { /* ... */ }, posts: { /* ... */ }, archive: { /* ... */ } } })
<component :is="currentView"> <!-- 组件在 vm.currentview 变化时改变 --> </component>
keep-alive
-
如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染。为此可以添加一个
keep-alive
指令参数:<component :is="currentView" keep-alive> <!-- 非活动组件将被缓存 --> </component>
activate
钩子
-
控制组件切换这一时段,可以为切入组件添加
activate
钩子。Vue.component('activate-example', { activate: function (done) { var self = this loadDataAsync(function (data) { self.someData = data done() }) } })
activate
钩子只作用于动态组件切换或静态组件初始化渲染的过程中,不作用于使用实例方法手工插入的过程中。
transition-mode
-
transition-mode
特性用于指定两个动态组件之间如何过渡。<!-- 先淡出再淡入 --> <component :is="view" transition="fade" transition-mode="out-in"> </component>
.fade-transition { transition: opacity .3s ease; } .fade-enter, .fade-leave { opacity: 0; }
杂项
-
自定义组件也可以使用
v-for
,但是不能将数据传递给组件,因为其作用域是孤立的。应当使用 props 传递。<my-component v-for="item in items" :item="item" :index="$index"> </my-component>
异步组件
Vue.js 允许将组件定义为一个工厂函数,动态地解析组件的定义。Vue.js 只在组件需要渲染时触发工厂函数,并且把结果缓存起来,用于后面的再次渲染。
-
一个例子
Vue.component('async-example', function (resolve, reject) { setTimeout(function () { resolve({ template: '<div>I am async!</div>' }) }, 1000) })
工厂函数接收一个
resolve
回调,在收到从服务器下载的组件定义时调用。也可以调用reject(reason)
指示加载失败。这里setTimeout
只是为了演示。怎么获取组件完全由你决定。推荐配合使用 Webpack 的代码分割功能Vue.component('async-webpack-example', function (resolve) { // 这个特殊的 require 语法告诉 webpack // 自动将编译后的代码分割成不同的块, // 这些块将通过 ajax 请求自动下载。 require(['./my-async-component'], resolve) })
递归组件
-
有
name
选项的组件可以再模板内部递归调用自己。var StackOverflow = Vue.extend({ name: 'stack-overflow', template: '<div>' + // 递归地调用它自己 '<stack-overflow></stack-overflow>' + '</div>' })
上面组件会导致一个错误 “max stack size exceeded”,所以要确保递归调用有终止条件。
片段实例
N/A
内联模板
-
如果子组件有
inline-template
特性,组件将把它的内容当做它的模板,而不是分发内容。<my-component inline-template> <p>These are compiled as the component's own template</p> <p>Not parent's transclusion content.</p> </my-component>
inline-template
让模板的作用域难以理解,并且不能缓存模板编译结果。最佳实践是使用template
选项在组件内定义模板。
深入响应式原理
[可以查看我的 Vue 2 的 Guide Note]
自定义指令
基础
- Vue.js 允许用户通过
Vue.directive(id, definition)
方法注册一个全局自定义指令,接收两个参数:指令 ID 和 定义对象。也可以用组件的directive
选项注册一个局部自定义指令。
钩子函数
- 定义对象可以提供几个钩子函数:
bind: 只调用一次,在指令第一次绑定到元素上时调用。
update: 在
bind
之后立即以初始值为参数第一次调用,之后每当绑定值变化时调用,参数为新值与旧值。-
unbind: 只调用一次,在指令从元素上解绑时调用。
Vue.directive('my-directive', { bind: function () { // 准备工作 // 例如,添加事件处理器或只需要运行一次的高耗任务 }, update: function (newValue, oldValue) { // 值更新时的工作 // 也会以初始值为参数调用一次 }, unbind: function () { // 清理工作 // 例如,删除 bind() 添加的事件监听器 } })
<div v-my-directive="someValue"></div>
当只需要
update
函数时,可以传入一个函数替代定义对象Vue.directive('my-directive', function (value) { // 这个函数用作 update() })
指令实例属性
-
所有的钩子函数将被复制到实际的指令对象中,钩子内
this
指向这个指令对象。这个对象暴露了一些有用的属性。- el: 指令绑定的元素。
- vm: 拥有该指令的上下文 ViewModel。
- expression: 指令的表达式,不包括参数和过滤器。
- arg: 指令的参数。
- name: 指令的名字,不包含前缀。
- modifiers: 一个对象,包含指令的修饰符。
- descriptor: 一个对象,包含指令的解析结果。
-
一个例子
<div id="demo" v-demo:hello.a.b="msg"></div>
Vue.directive('demo', { bind: function () { console.log('demo bound!') }, update: function (value) { this.el.innerHTML = 'name - ' + this.name + '<br>' + 'expression - ' + this.expression + '<br>' + 'argument - ' + this.arg + '<br>' + 'modifiers - ' + JSON.stringify(this.modifiers) + '<br>' + 'value - ' + value } }) var demo = new Vue({ el: '#demo', data: { msg: 'hello!' } })
结果
name - demo expression - msg argument = hello modifiers - {"b":true,"a":true} value = hello!
字面修饰符
当指令使用了字面修饰符,它的值将按照普通字符串处理并传递给
update
方法。-
普通字符串不能响应数据变化,故
update
方法将只调用一次。<div v-demo.literal="foo bar baz">
Vue.directive('demo', function (value) { console.log(value) // "foo bar baz" })
元素指令
-
元素指令可以看做一个轻量组件。
Vue.elementDirective('my-directive', { // API 同普通指令 bind: function () { // 操作 this.el... } })
<my-directive></my-directive>
元素指令是终结性的,这意味着,一旦 Vue 遇到一个元素指令,它将跳过该元素及其子元素 —— 只有该元素指令本身可以操作该元素及其子元素。
高级选项
[正在整理]
自定义过滤器
基础
-
可以使用全局方法
Vue.filter()
注册一个自定义过滤器,接收两个参数: 过滤器 ID 和 过滤器函数。过滤器函数以值为参数,返回转换后的值。Vue.filter('reverse', function (value) { return value.split('').reverse().join('') })
-
过滤器函数可以接收任意数量的参数
Vue.filter('wrap', function (value, begin, end) { return begin + value + end })
<!-- 'hello' => 'before hello after' --> <span v-text="message | wrap 'before' 'after'"></span>
双向过滤器
-
定义一个过滤器,把来自视图(
<input>
元素)的值写回模型之前转化它。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)) } })
动态参数
- 如果过滤器参数没有用引号包起来,则它会在当前 vm 作用域内动态计算。另外,过滤器函数的
this
始终指向调用它的 vm。
混合
基础
-
混合以一种灵活的方式为组件提供分布复用功能。混合对象可以包含任意的组件选项。当组件使用了混合对象时,混合对象的所有选项将被“混入”组件自己的选项中。
// 定义一个混合对象 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()
使用同样的合并策略。
全局混合
- 慎用全局混合,因为它影响到每个创建的 Vue 实例,包括第三方组件。在大多数情况下,它应当只用于自定义选项。
自定义选项合并策略
-
如果想用自定义逻辑合并自定义选项,则向
Vue.config.optionMergeStrategies
添加一个函数:Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) { // 返回 mergedVal }
对于多数值为对象的选项,可以简单地使用
methods
所用的合并策略var strategies = Vue.config.optionMergeStrategies strategies.myOption = strategies.methods
插件
开发插件
[正在整理]
使用插件
-
通过
Vue.use()
全局方法使用插件。// 调用 `MyPlugin.install(Vue)` Vue.use(MyPlugin)
也可以传入一个选项对象
Vue.use(MyPlugin, { someOption: true })
虽然对于一些插件,如果
Vue
是全局变量则自动调用Vue.use()
,但是在模块环境中应当始终显式调用Vue.use()
。
构建大型应用
- 在 Vue 中可以使用诸如 Jade 之类的预处理器。
路由
- vue-router
- 对于简单的路由逻辑,可以通过监听
hashchange
事件,并使用动态组件实现。
与服务器通信
状态管理
- 使用 store 模式,把所有的 action 放在 store 内,action 修改 store 的状态。
- 不要在 action 中替换原始的状态对象!
- Vuex : Vue's Flux.