寫在最前:文章轉(zhuǎn)自掘金
一唯竹、prop
& this.emit('Method name', value)
1. 優(yōu)點(diǎn)
父子組件通信方面靈活
2. 缺點(diǎn)
- props 對(duì)父組件數(shù)據(jù)的篡改
- 跨層級(jí)通信,兄弟組件通訊困難
3. 應(yīng)用場(chǎng)景
props
的應(yīng)用場(chǎng)景很簡(jiǎn)單苦丁,就是正常的父子組件通信
二浸颓、this.$xxx
實(shí)際操作中會(huì)有很大的弊端,而且vue本身也不提倡這種通信方式。而且這種通信方式也有很多風(fēng)險(xiǎn)性产上。
三棵磷、provide inject
1. 基本用法
在父組件上通過(guò)provide
將方法,屬性晋涣,或者是自身實(shí)例暴露出去仪媒,子孫組件,插槽組件,甚至是子孫組件的插槽組件谢鹊,通過(guò)inject
把父輩provide
引進(jìn)來(lái)算吩。
父組件:
<template>
<div class="father" >
<div>子組件對(duì)我說(shuō):{{ sonMes }}</div>
<div>孫組件對(duì)我說(shuō):{{ grandSonMes }}</div>
<son />
</div>
</template>
<script>
import son from './son'
export default {
name:'father',
components:{
son /* 子組件 */
},
provide(){
return {
/* 將自己暴露給子孫組件 ,這里聲明的名稱要于子組件引進(jìn)的名稱保持一致 */
father:this
}
},
data(){
return {
grandSonMes:'', /* 來(lái)自子組件的信息 */
sonMes:'' /* 發(fā)送給子組件的信息 */
}
},
methods:{
/* 接受孫組件信息 */
grandSonSay(value){
this.grandSonMes = value
},
/* 接受子組件信息 */
sonSay(value){
this.sonMes = value
},
},
}
</script>
這里我們通過(guò)provide
把本身暴露出去。??????這里聲明的名稱要與子組件引進(jìn)的名稱保持一致
子組件
<template>
<div class="son" >
<input v-model="mes" /> <button @click="send" >對(duì)父組件說(shuō)</button>
<grandSon />
</div>
</template>
<script>
import grandSon from './grandSon'
export default {
/* 子組件 */
name:'son',
components:{
grandSon /* 孫組件 */
},
data(){
return {
mes:''
}
},
/* 引入父組件 */
inject:['father'],
methods:{
send(){
this.father.sonSay(this.mes)
}
},
}
</script>
子組件通過(guò)inject
把父組件實(shí)例引進(jìn)來(lái)佃扼,然后可以直接通過(guò)this.father
可以直接獲取到父組件赌莺,并調(diào)用下面的sonSay
方法。
孫組件
<template>
<div class="grandSon" >
<input v-model="mes" /> <button @click="send" >對(duì)爺爺組件說(shuō)</button>
</div>
</template>
<script>
export default {
/* 孫組件 */
name:'grandSon',
/* 引入爺爺組件 */
inject:['father'],
data(){
return {
mes:''
}
},
methods:{
send(){
this.father.grandSonSay( this.mes )
}
}
}
</script>
2. 插槽方式
provide
, inject
同樣可以應(yīng)用在插槽上松嘶,我們給父子組件稍微變動(dòng)一下艘狭。
父組件
<template>
<div class="father" >
<div>子組件對(duì)我說(shuō):{{ sonMes }}</div>
<div>孫組件對(duì)我說(shuō):{{ grandSonMes }}</div>
<son >
<grandSon/>
</son>
</div>
</template>
<script>
import son from './slotSon'
import grandSon from './grandSon'
export default {
name:'father',
components:{
son, /* 子組件 */
grandSon /* 孫組件 */
},
provide(){
return {
/* 將自己暴露給子孫組件 */
father:this
}
},
data(){
return {
grandSonMes:'', /* 來(lái)自子組件的信息 */
sonMes:'' /* 發(fā)送給子組件的信息 */
}
},
methods:{
/* 接受孫組件信息 */
grandSonSay(value){
this.grandSonMes = value
},
/* 接受子組件信息 */
sonSay(value){
this.sonMes = value
},
},
}
</script>
子組件
<template>
<div class="son" >
<input v-model="mes" /> <button @click="send" >對(duì)父組件說(shuō)</button>
<slot />
</div>
</template>
達(dá)到了同樣的通信效果。實(shí)際這種插槽模式,所在都在父組件注冊(cè)的組件翠订,最后孫組件也會(huì)綁定到子組件的children下面巢音。和上述的情況差不多。
3. provied
其他用法
provide
不僅能把整個(gè)父組件全部暴露出去尽超,也能根據(jù)需要只暴露一部分(一些父組件的屬性或者是父組件的方法)官撼,上述的例子中,在子孫組件中似谁,只用到了父組件的方法傲绣,所以我們可以只提供兩個(gè)通信方法。但是這里注意的是巩踏,如果我們向外提供了方法,如果方法里面有操作this
行為秃诵,需要綁定this
父組件
provide(){
return {
/* 將通信方法暴露給子孫組件(注意綁定this) */
grandSonSay:this.grandSonSay.bind(this),
sonSay:this.sonSay.bind(this)
}
},
methods:{
/* 接受孫組件信息 */
grandSonSay(value){
this.grandSonMes = value
},
/* 接受子組件信息 */
sonSay(value){
this.sonMes = value
},
},
子組件
/* 引入父組件方法 */
inject:['sonSay'],
methods:{
send(){
this.sonSay(this.mes)
}
},
4. 優(yōu)缺點(diǎn)
- 組件通信不受到子組件層級(jí)的影響
- 適用于插槽,嵌套插槽
- 不適合兄弟通訊
- 父級(jí)組件無(wú)法主動(dòng)通信
5. 應(yīng)用場(chǎng)景
provide-inject
這種通信方式塞琼,更適合深層次的復(fù)雜的父子代通信菠净,子孫組件可以共享父組件的狀態(tài),還有一點(diǎn)就是適合el-form
el-form-item
這種插槽類型的情景彪杉。
四署穗、vuex
五秃嗜、事件總線一 EventBus
EventBus
事件總線, EventBus
所有事件統(tǒng)一調(diào)度,有一個(gè)統(tǒng)一管理事件中心,一個(gè)組件綁定事件低飒,另一個(gè)組件觸發(fā)事件眼五,所有的組件通信不再收到父子組件的限制案腺,那個(gè)頁(yè)面需要數(shù)據(jù)日戈,就綁定事件另凌,然后由數(shù)據(jù)提供者觸發(fā)對(duì)應(yīng)的事件來(lái)提供數(shù)據(jù)。
EventBus
核心思想是事件的綁定和觸發(fā)残拐,這一點(diǎn)和vue
中 this.$emit
和 this.$on
一樣途茫,這個(gè)也是整個(gè)EventBus
核心思想。接下來(lái)我們來(lái)重點(diǎn)解析這個(gè)流程溪食。
1. 基本用法
EventBus
class EventBus {
es = {}
/* 綁定事件 */
on(eventName, cb) {
if (!this.es[eventName]) {
this.es[eventName] = []
}
this.es[eventName].push({
cb
})
}
/* 觸發(fā)事件 */
emit(eventName, ...params) {
const listeners = this.es[eventName] || []
let l = listeners.length
for (let i = 0; i < l; i++) {
const { cb } = listeners[i]
cb.apply(this, params)
}
}
}
export default new EventBus()
這個(gè)就是一個(gè)簡(jiǎn)單的事件總線囊卜,有on
,emit
兩個(gè)方法
父組件
<template>
<div class="father" >
<input v-model="mes" /> <button @click="send" >對(duì)子組件說(shuō)</button>
<div>子組件對(duì)我說(shuō):{{ sonMes }}</div>
<son />
<brotherSon />
</div>
</template>
<script>
import son from './son'
import brotherSon from './brother'
import EventBus from './eventBus'
export default {
name:'father',
components:{
son ,/* 子組件 */
brotherSon, /* 子組件 */
},
data(){
return {
mes:'',
sonMes:''/* 發(fā)送給子組件的信息 */
}
},
mounted(){
/* 綁定事件 */
EventBus.on('sonSay',this.sonSay)
},
methods:{
/* 傳遞給子組件 */
send(){
EventBus.emit('fatherSay',this.mes)
},
/* 接受子組件信息 */
sonSay(value){
this.sonMes = value
},
},
}
</script>
我們?cè)诔跏蓟臅r(shí)候通過(guò)EventBus
的on
方法綁定sonSay
方法供給給子組件使用。向子組件傳遞信息的時(shí)候错沃,通過(guò)emit
觸發(fā)子組件的綁定方法栅组,實(shí)現(xiàn)了父子通信。 接下來(lái)我們看一下子組件枢析。
子組件
<template>
<div class="son" >
<div> 父組件對(duì)我說(shuō):{{ fatherMes }} </div>
<input v-model="mes" /> <button @click="send" >對(duì)父組件說(shuō)</button>
<div>
<input v-model="brotherMes" /> <button @click="sendBrother" >對(duì)兄弟組件說(shuō)</button>
</div>
</div>
</template>
<script>
import EventBus from './eventBus'
export default {
name:'son',
data(){
return {
mes:'',
brotherMes:'',
fatherMes:''
}
},
mounted(){
/* 綁定事件 */
EventBus.on('fatherSay',this.fatherSay)
},
methods:{
/* 向父組件傳遞信息 */
send(){
EventBus.emit('sonSay',this.mes)
},
/* 向兄弟組件傳遞信息 */
sendBrother(){
EventBus.emit('brotherSay',this.brotherMes)
},
/* 父組件對(duì)我說(shuō) */
fatherSay(value){
this.fatherMes = value
}
},
}
</script>
和父組件的邏輯差不多玉掸,把需要接受數(shù)據(jù)的方法,通過(guò)EventBus
綁定醒叁,通過(guò)觸發(fā)eventBus
方法司浪,來(lái)向外部傳遞信息。我們還模擬了兄弟之間通信的場(chǎng)景把沼。我們建立一個(gè)兄弟組件啊易。
<template>
<div class="son" > 兄弟組件對(duì)我說(shuō): {{ brotherMes }} </div>
</template>
<script>
import EventBus from './eventBus'
export default {
/* */
name:'brother',
data(){
return {
brotherMes:''
}
},
mounted(){
/* 綁定事件給兄弟組件 */
EventBus.on('brotherSay',this.brotherSay)
},
methods:{
brotherSay(value){
this.brotherMes = value
}
}
}
</script>
我們可以看到,兄弟組件處理邏輯和父子之間沒(méi)什么區(qū)別饮睬。
2. 優(yōu)缺點(diǎn)
- 簡(jiǎn)單靈活租谈,父子兄弟通信不受限制。
- 通信方式不受框架影響
- 維護(hù)困難捆愁,容易引起連鎖問(wèn)題
- 需要謹(jǐn)小慎微的命令規(guī)范
- 不利于組件化開發(fā)
3. 應(yīng)用場(chǎng)景
實(shí)現(xiàn)總線這種方式更適合割去,微信小程序,和基于vue構(gòu)建的小程序昼丑,至于為什么呢呻逆,因?yàn)槲覀兌贾佬〕绦虿捎秒p線程模型(渲染層+邏輯層)(如下圖所示),渲染層作用就是小程序wxml
渲染到我們的視線中矾克,而邏輯層就是我們寫的代碼邏輯页慷,在性能上,我們要知道在渲染層浪費(fèi)的性能要遠(yuǎn)大于邏輯層的代碼執(zhí)行性能開銷胁附,如果我們?cè)谛〕绦蚶锊捎猛ㄟ^(guò)props
等傳遞方式,屬性是綁定在小程序標(biāo)簽里面的滓彰,所以勢(shì)必要重新渲染視圖層控妻。如果頁(yè)面結(jié)構(gòu)復(fù)雜,可能會(huì)造成卡頓等情況揭绑,所以我們通過(guò)eventBus
可以繞過(guò)渲染層弓候,直接有邏輯層講數(shù)據(jù)進(jìn)行推送郎哭,節(jié)約了性能的開銷。