組件是可復(fù)用的 Vue 實例,所以與 new Vue 接收相同的選項,例如 data省容、computed、watch燎字、methods 以及生命周期鉤子等腥椒。僅有的例外是像 el 這樣根實例特有的選項阿宅。
一個組件的 data 選項必須是一個函數(shù),因此每個實例可以維護(hù)一份被返回對象的獨立的拷貝笼蛛。(類似閉包洒放。)否則同一組件的各個實例的data會共用。
data: function () {
return {
count: 0
}
}
組件名
組件名可以接受kebab-case
和PascalCase
,但在dom中使用時只能接受kebab-case
(且至少包含一個連字符),例如 <my-component-name>
滨砍。
組件注冊
全局注冊
全局注冊的組件可以用在其被注冊之后的任何 (通過 new Vue) 新創(chuàng)建的 Vue 根實例拉馋,也包括其組件樹中的所有子組件的模板中(包括自己引用自己)。
Vue.component('my-component-name', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
})
局部注冊
- 通過
components
對象進(jìn)行局部注冊惨好。其key就是自定義元素的名字,其屬性值就是這個組件的選項對象随闺。 - 可注冊的位置包括
new Vue()
中日川,另一個component
中,以及采用import時的export default
內(nèi)矩乐。
components: {
"my-component-name": {
props: ['title'],
template:'<h3>{{ title }}</h3>'
}
}
根元素和屬性繼承
- 每個組件必須有且只有一個根元素龄句。
- 為組件賦予的
class
,style
等非prop屬性散罕,會自動合并到該組件實例的根元素上
對于class
分歇,style
以外的非prop屬性可通過在組件的選項對象中配置inheritAttrs: false
禁用自動合并到根元素,而實例的$attrs
可以指定由哪個元素繼承組件上的屬性
<base-input
v-model="username"
required
placeholder="Enter your username"
></base-input>
...
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
`
})
數(shù)據(jù)傳遞
通過 Prop 向子組件傳遞數(shù)據(jù)
-
prop
名在 JavaScript 中可以是 camelCase 的(但依然推薦使用kebab-case)欧漱,但在 HTML 中總是 kebab-case 的职抡。 - 在組件實例中能夠訪問
props
的值,就像訪問data
中的值一樣误甚。prop
會讀取組件中的自定義屬性缚甩。 -
prop
可以以字符串?dāng)?shù)組形式表示,也可以以對象形式窑邦,指定各個prop
值的類型擅威,并提供必選,默認(rèn)值冈钦,校驗等配置郊丛。
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
//或
props: {
title: String,
likes: [Number,String],//多個可能的類型
isPublished: {
type: Boolean,
required: true,//必填
default: 100//設(shè)置默認(rèn)值
},
counts:{
validator: function (value) {//自定義驗證函數(shù),可在開發(fā)環(huán)境拋出log警告
// 這個值必須匹配下列字符串中的一個
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
},
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
- 傳入布爾值
<!-- 包含該 prop 沒有值的情況在內(nèi)瞧筛,都意味著 `true`厉熟。-->
<blog-post is-published></blog-post>
<!-- 使用 `v-bind` 告訴 Vue 這是一個布爾值表達(dá)式而非字符串。 -->
<blog-post v-bind:is-published="false"></blog-post>
- 傳入一個對象的所有屬性
可以使用不帶參數(shù)的 v-bind
post: {
id: 1,
title: 'My Journey with Vue'
}
...
<blog-post v-bind="post"></blog-post>
//等價于
<blog-post
v-bind:id="post.id"
v-bind:title="post.title"
></blog-post>
- 單項數(shù)據(jù)流
父級 prop 的更新會向下流動到子組件中驾窟,但是反過來則不行庆猫。
每次父級組件發(fā)生更新時,子組件中所有的 prop 都將會刷新為最新的值绅络。因此不應(yīng)該在一個子組件內(nèi)部改變 prop月培。
當(dāng)需要在子組件中改變prop時嘁字,可采取:- 定義一個本地的 data 屬性并將這個 prop 用作其初始值
- 使用一個計算屬性來關(guān)聯(lián)這個 prop 的值
- 注意杉畜,在子組件中改變prop引用的對象或數(shù)組本身將會影響到父組件的狀態(tài)纪蜒。
事件驅(qū)動
- 事件名應(yīng)始終使用
kebab-case
。(不像prop中vue做了優(yōu)化此叠,會將js中的camelCase在html中轉(zhuǎn)為kebab-case) - 父級組件
v-on
監(jiān)聽子組件實例纯续,子組件通過調(diào)用$emit
來觸發(fā)一個事件,也可以在子組件中通過$on
監(jiān)聽事件
<blog-post v-on:enlarge-text="postFontSize += $event"></blog-post>
components: {
"my-component-name": {
template: '
<button v-on:click="$emit('enlarge-text', 0.1)">
Enlarge text
</button>',
mounted() {
this.$on('enlarge-text', target => {
console.log("enlarge-text", target)
})
}
}
通過EventBus實現(xiàn)組件間通訊
公共事件總線EventBus的實質(zhì)就是創(chuàng)建一個vue實例灭袁,通過一個空的vue實例作為橋梁實現(xiàn)vue組件間的通信猬错。它是實現(xiàn)非父子組件通信的一種解決方案。
通過單獨建一個vue實例茸歧,并在需要通訊的A倦炒、B組件中同時導(dǎo)入
import Vue from 'Vue'
export default new Vue
import EventBus from '這里是你引入bus.js的路徑'
在A組件中調(diào)用EventBus.$emit
,在B組件中監(jiān)聽EventBus.$on
软瞎,即實現(xiàn)通訊
注意逢唤,在B組件的beforeDestroy或者destroyed中應(yīng)調(diào)用EventBus.$off
清除監(jiān)聽,否則重復(fù)加載B時會導(dǎo)致創(chuàng)建多個監(jiān)聽
將原生事件綁定到組件
如根元素直接為發(fā)生事件的元素涤浇,需使用.native
修飾符鳖藕。否則此處事件(focus)會被認(rèn)為是對子組件emit的監(jiān)聽,而非瀏覽器原生事件只锭。
<base-input v-on:focus.native="onFocus"></base-input>
否則可利用$listeners
屬性
雙向綁定
通過props和$emit完成父子組件雙向綁定
v-model
v-model是雙向綁定的一個特例著恩,利用名為 value
的 prop 和名為 input
的事件(或在子組件model配置中修改),即可簡寫為v-model
<custom-input
v-bind:value="searchText"
v-on:input="searchText = $event"
></custom-input>
//或
<custom-input v-model="searchText"></custom-input>
...
Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
- 像單選框蜻展、復(fù)選框等類型的輸入控件可能會將
value
特性用于不同的目的页滚。model
選項可以用來避免這樣的沖突:
<base-checkbox v-model="lovingVue"></base-checkbox>
...
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
})
.sync修飾符
.sync修飾符是雙向綁定的一個特例,利用名為 XXX
的 prop 和名為 update:XXX
的事件铺呵,即可簡寫為.sync
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
...
this.$emit('update:title', newTitle)
<text-document v-bind:title.sync="doc.title"></text-document>
插槽
組件元素間的html內(nèi)容,可以通過<slot></slot>
插入渲染后的組件實例裹驰。否則會被全部廢棄。
<alert-box>
Something bad happened.
</alert-box>
...
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})
后備內(nèi)容
寫在<slot>
標(biāo)簽內(nèi)片挂,若父級組件中沒有傳入插槽內(nèi)容幻林,則顯示該后備內(nèi)容。(雖然寫在子組件內(nèi)音念,但作用域同父級組件)
具名插槽 和 作用域插槽 (2.6.0)
- 具名插槽
通過<slot>
元素的name
屬性沪饺,定義額外的插槽。一個不帶name
的<slot>
出口會帶有隱含的名字“default”
闷愤。在向具名插槽提供內(nèi)容的時候整葡,我們可以在一個<template>
元素上使用v-slot
指令,并以v-slot
的參數(shù)的形式提供其名稱讥脐。
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
</div>
...
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
</base-layout>
- 作用域插槽
父級模板里的所有內(nèi)容都是在父級作用域中編譯的遭居;子模板里的所有內(nèi)容都是在子作用域中編譯的啼器。
因此通常插槽可訪問父級作用域而非子級作用域。如果需要訪問子組件實例中的屬性俱萍,可以在 <slot>
元素上添加特性端壳,并為v-slot
賦值進(jìn)行關(guān)聯(lián)
<span>
<slot :user="user">
{{ user.lastName }}
</slot>
</span>
...
//此處的slotProps等同于插槽的props
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
- 跟
v-on
和v-bind
一樣,v-slot
也可縮寫為#
枪蘑。例如v-slot:header
可以被重寫為#header
獨占插槽的縮寫
當(dāng)僅含有默認(rèn)插槽時,v-slot
可以不寫在<template>
中而直接寫在組件上损谦,甚至可以省略default
<current-user v-slot:default="slotProps"></current-user>
<current-user v-slot="slotProps"></current-user>
動態(tài)插槽
同動態(tài)指令參數(shù),使用 [XXX]
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
</base-layout>
動態(tài)組件 和 異步組件
動態(tài)組件
通過 <component>
元素和 is
特性來實現(xiàn):
<!-- 組件會在 `currentTabComponent` 改變時改變 -->
<component v-bind:is="currentTabComponent"></component>
在解析dom模板時岳颇,有些 HTML 元素照捡,諸如 <ul>、<ol>话侧、<table> 和 <select>麻敌,對于哪些元素可以出現(xiàn)在其內(nèi)部是有嚴(yán)格限制的。而有些元素掂摔,諸如 <li>、<tr> 和 <option>赢赊,只能出現(xiàn)在其它某些特定的元素內(nèi)部乙漓。此時可以通過
is
防止渲染出錯。
<table>
<tr is="blog-post-row"></tr>
</table>
在動態(tài)組件上使用 keep-alive
組件動態(tài)切換時释移,可以繼承上次離開時的狀態(tài)
<!-- 失活的組件將會被緩存叭披!-->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
異步組件
可以使用工廠函數(shù)(Promise)按需加載模塊 查看文檔
單文件組件
文件擴(kuò)展名為 .vue 的 單文件組件,解決了多個component重名玩讳,字符串模板的語法高亮涩蜘,組件作用域的CSS,模塊化構(gòu)建等問題熏纯,且方便使用各種預(yù)處理器同诫。
組件間通信方式總結(jié)
- provide / inject
允許一個祖先組件向其所有子孫后代注入一個依賴。
可以視為祖先組件和后代組件間的“l(fā)ong range props”樟澜,除了:
- 父組件不需要知道哪些子組件使用它 provide 的 property
- 子組件不需要知道 inject 的 property 來自哪里
provide 和 inject 綁定沒有做響應(yīng)式處理误窖。但如果傳入了一個對象,其屬性還是可響應(yīng)的秩贰。
// 祖先組件
app.component('todo-list', {
data() {
return {
a:{name:100}
}
},
provide: {
a:this.a,//無法如此操作,應(yīng)改為如下的provide函數(shù)形式
b: 200
},
provide(){
return {
a:this.a霹俺,
todoLength: Vue.computed(() => this.todos.length)
}
}
}
// 子組件
app.component('todo-list-statistics', {
inject: ['a'],
created() {
console.log(`Injected property: ${this.a.name}`)
}
})
- Vue.observable
讓一個對象可響應(yīng)。Vue 內(nèi)部會用它來處理 data 函數(shù)返回的對象毒费。
返回的對象可以直接用于渲染函數(shù)和計算屬性內(nèi)丙唧,并且會在發(fā)生改變時觸發(fā)相應(yīng)的更新。也可以作為最小化的跨組件狀態(tài)存儲器觅玻,用于簡單的場景:
const state = Vue.observable({ count: 0 })
const Demo = {
render(h) {
return h('button', {
on: { click: () => { state.count++ }}
}, `count is: ${state.count}`)
}
}
- $attrs
包含了父作用域中未被props涵蓋的特性想际,通常用于在創(chuàng)建高級別組件時再通過v-bind="$attrs"
傳入更內(nèi)部的組件培漏。 - $listeners
包含了父作用域中的 (非 .native 修飾器的) v-on 事件監(jiān)聽器。它可以通過 v-on="$listeners" 傳入內(nèi)部組件——在創(chuàng)建更高層次的組件時非常有用沼琉。 - props
- $emit
- eventbus
- vuex
- $parent / $children / ref
父子組件鉤子順序
- 加載渲染過程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted - 子組件更新過程
父beforeUpdate->子beforeUpdate->子updated->父updated - 父組件更新過程
父beforeUpdate->父updated - 銷毀過程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed