前言
相信實際項目中用過vue的同學,一定對vue中父子組件之間的通信并不陌生,vue中采用良好的數據通訊方式,避免組件通信帶來的困擾。今天筆者和大家一起分享vue父子組件之間的通信方式,優缺點,及其實際工作中的應用場景
首先我們帶著這些問題去思考
1 vue中到底有多少種父子組件通信方式?
2 vue中那種父子組件最佳通信方式是什么?
3 vue中每個通信方式應用場景是什么?
一 prop
1 基本用法
prop
通信方式大家最常見的,也是最常用的父子組件通信類型,我們可以直接在標簽里面給子組件綁定屬性和方法,對于屬性我們可以直接通過子組件聲明的prop
拿到,對于父元素的方法,我們可以通過?this.$emit觸發
。
我們先簡單寫一個props
父子組件通信的場景
父組件
<template>
<div class="father" >
<input v-model="mes" /> <button @click="send" >對子組件說button>
<div>子組件對我說:{{ sonMes }}div>
<son :fatherMes="sendSonMes" @sonSay="sonSay" />
div>
template>
<script>import son from './son'export default {name:'father',components:{
son /* 子組件 */
},data(){return {mes:'',sendSonMes:'', /* 來自子組件的信息 */sonMes:'' /* 發送給子組件的信息 */
}
},methods:{/* 傳遞給子組件 */send(){this.sendSonMes = this.mes
},/* 接受子組件信息 */ sonSay(value){this.sonMes = value
},
},
}script>
我們這里只需要將給子組件的數據fatherMes
和提供給子組件的方法?sonSay
?通過標簽方式傳遞給子組件。
子組件
<template>
<div class="son" >
<div> 父組件對我說:{{ fatherMes }} div>
<input v-model="mes" /> <button @click="send" >對父組件說button>
div>
template>
<script>export default {name:'son',props:{fatherMes:{type:String,default:''
}
},data(){return {mes:''
}
},methods:{send(){this.$emit('sonSay',this.mes)
}
},
}script>>
子組件通過props
來定義接受父組件傳遞過來的信息,我們就可以直接通過this
獲取到,對于父組件傳遞的事件,我們可以通過this.$emit
來觸發事件。
區別react的props
這里和react
的props
有點區別,react
組件更新來源于props
更新和自身state
改變,當props
改變,會默認更新組件。而在Vue
中,如果我們對父組件傳過來的新的props
沒有做依賴收集(template
模版收集,computed
計算屬性收集),組件是不會觸動更新的。
效果
數據格式化
如果我們想對props
數據進行數據格式化,那么用computed
接受props
并格式化想要的數據類型。
<div class="son" >
<div> 父組件對我說:{{ computedFatherMes }} div>
<input v-model="mes" /> <button @click="send" >對父組件說button>
div>
export default {
name:'son',
props:{
fatherMes:{
type:String,
default:''
}
},
computed:{
computedFatherMes(){
return this.fatherMes + '??'
}
},
}
數據監聽
當我們根據props
改變不想更新視圖,或者不想立即更新視圖,那么可以用watch
來監聽來自父組件的props
的變化。
watch:{
fatherMes(newVaule){
console.log( newVaule)
}
},
2優點
props
傳遞數據優點是顯而易見的,靈活簡單,可以對props
數據進行計算屬性,數據監聽等處理。父子組件通信靈活方便。這里可能單單僅限父子一層。
3缺點
1 props篡改
我們在子組件中使用父組件props
的時候,如果涉及一些變量賦值,修改等操作,props
被莫名其妙的修改了,連同父組件的數據也被篡改了,有的同學可能會很疑惑,父元素的props
不是不能修改么,這里怎么改變了呢,vue
中props
到底能不能改變?,至于這個問題,不能直接給出答案,如果props
是基礎數據類型,當我們改變的時候,就會曝出錯誤。我們一起看一個例子。
父組件
<template>
<div>
<div>父組件div>
<son :fData="sonProps" />
div>
template>
<script>import son from './sTampering'export default {name:'father',components:{
son
},data(){return {sonProps:''
}
},
}script>
子組件
<template>
<button >改變父組件propsbutton>
template>
<script>export default {name:'son',props:{fData:{required:true
}
},methods:{changFatherProps(){this.fData = 'hello ,father'
}
}
}script>
當我們直接點擊button 會拋出以下警。
但是當我們傳過來的是一個引用數據類型,并且修改數據下某一個屬性的時候。
父組件
data(){
return {
sonProps:{
a:1,
b:2
}
}
},
子組件
changFatherProps(){
this.fData.a = 'hello,world'
}
當點擊按鈕發現并沒有報錯。
于是我們打印父組件的sonprops
數據:
我們發現父組件的data
數據已經被子組件篡改。于是我們總結出一個結論,子組件雖然不能直接對父組件prop
進行重新賦值,但是當父組件是引用類型的時候,子組件可以修改父組件的props
下面的屬性。這就是一個很尷尬的事情,如果我們設計的初衷就是父組件數據也同時被修改,這個結果是可以接受的,但是當我們不希望父組件那份數據源有任何變化的時候,這就是一個嚴重的邏輯bug
。所以這就是props
通訊的風險項之一。
2 跨層級通信,兄弟組件通訊困難
對于父組件-子組件-子組件的子組件這種跨層級的通信,顯然需要我們一層一層的prop
綁定屬性和方法,如果遇到更復雜的情況,實現起來比較困難。
對于兄弟組件之間的通訊,props
需要通過父組件作為橋梁,實現子組件-> 父組件 -> 子組件通信模式,如果想要通過父組件做媒介,那么必定會造成父組件重新渲染,為了實現兄弟組件通信付出的代價比較大。
4 應用場景
props
的應用場景很簡單,就是正常不是嵌套很深的父子組件通信,和關系不是很復雜的兄弟組件組件通信。
二 this.$xxx
通過this下面的數據直接獲取vue
實例這種方法比較暴力,因為我們所謂的組件,最終都會是一個對象,存放組件的各種信息,組件和組件通過this.$children
和this.$parent
指針關聯起來。因為在項目中只有一個root
根組件,理論上,我們可以找到通過this.$children
?this.$parent
來訪問頁面上的任何一個組件 ,但是實際上如何精確匹配到目標組件,確是一個無比棘手的問題。
1 基本用法
父組件
<template>
<div class="father" >
<input v-model="mes" /> <button @click="send" >對子組件說button>
<son2 v-if="false" />
<div>子組件對我說:{{ sonMes }}div>
<son />
div>
template>
<script>import son from './son'import son2 from './son2'export default {name:'father',components:{
son ,/* 子組件 */
son2
},data(){return {mes:'',sendSonMes:'', /* 來自子組件的信息 */sonMes:'' /* 發送給子組件的信息 */
}
},methods:{/* 傳遞給子組件 */send(){/* 因為son組件是第一個有效組件所以直接去下標為0的組件 */const currentChildren = this.$children[0]
currentChildren.accept(this.mes)
},/* 接收子組件的信息 */accept(value){this.sonMes = value
}
},
}script>
子組件
<template>
<div class="son" >
<div> 父組件對我說:{{ fatherMes }} div>
<input v-model="mes" /> <button @click="send" >對父組件說button>
div>
template>
<script>export default {name:'son',data(){return {mes:'',fatherMes:''
}
},methods:{/* 接受父組件內容 */accept(value){this.fatherMes = value
},/* 向父組件發送信息 */send(){this.$parent.accept(this.mes)
},
},
}script>
效果
我們可以清楚看到,和props
通信相比,this.$parent
,this.$children
顯得更加簡潔,無需再給子組件綁定事件和屬性,只需要在父組件和子組件聲明發送和接受數據的方法。就可以實現組件間的通信,看起來很是便捷,但是實際操作中會有很大的弊端,而且vue
本身也不提倡這種通信方式。而且這種通信方式也有很多風險性,我們稍后會給予解釋。
2 優點
簡單,方便?this.$children
,this.$parent
?this.$refs
?這種通信方式,更加的簡單直接獲取vue實例,對vue實例下的數據和方法直接獲取或者引用。
3 缺點
1 $this.children不可控性大,有一定風險
如上的例子,我們稍微對父組件加以改動,就會直接報錯。
之前的
<son2 v-if="false" />
改成
<son2 v-if="true" />
就會報如上錯誤,錯誤的原因很簡單,我們用?$children
?的下標獲取,但是兄弟組件son2
?v-if = true
?之后,我們通過通過this.$children[0]
?獲取的就是son2
組件,son2沒有綁定方法,所以得出結論,對于v-if
動態控制組件顯示隱藏的不建議用this.$children
用法,取而代之的我們可以用ref
獲取對應子組件的實例。
如上改成
<son ref="son" />
然后獲取:
const currentChildren = this.$refs.son
根本解決了問題。
2 不利于組件化
直接獲取組件實例這種方式,在一定程度上妨礙了組件化開發,組件化開發的過程中,那些方法提供給外部,那些方法是內部使用,在沒有提前商量的情況下,父子組件狀態不透明的情況下,一切都是未知的,所以不同開發人員在獲取組件下的方法時候,存在風險,提供的組件方法,屬性是否有一些內部耦合。組件開發思路初衷,也不是由組件外部來對內部作出一定改變,而往往是內部的改變,通知外部綁定的方法事件。反過來如果是子組件內部,主動向父組件傳遞一些信息,也不能確定父組件是否存在。
3 兄弟組件深層次嵌套組件通訊困難
和props方式一下,如果是兄弟直接組件的通信,需要通過父組件作為中間通訊的橋梁,而深層次的組件通訊,雖然不需要像props通訊那樣逐層綁定,但是有一點,需要逐漸向上層或下層獲取目標實例,如何能精準獲取這是一個非常頭疼的問題,而且每當深入一層,風險性和不確定性會逐級擴大。
4 應用場景
直接通過實例獲取的通信方式適合已知的,固定化的頁面結構,這種通訊方式,要求父子組件高度透明化,知己知彼,很明確父子組件有那些方法屬性,都是用來干什么。所以說這種方式更適合頁面組件,而不適合一些第三方組件庫,或者是公共組件。
三 provide inject
如果說vue
中?provide
?和?inject
,我會首先聯想到react
的context
上下文,兩個作用在一定程度上可以說非常相似,在父組件上通過provide
將方法,屬性,或者是自身實例暴露出去,子孫組件,插槽組件,甚至是子孫組件的插槽組件,通過inject
把父輩provide
引進來。提供給自己使用,很經典的應用?provide
和?inject
的案例就是?element-ui
中?el-form
和?el-form-item
我們先不妨帶著問題想象一個場景
<el-form label-width="80px" :model="formData">
<el-form-item label="姓名">
<el-input v-model="formData.name">el-input>
el-form-item>
<el-form-item label="年齡">
<el-input v-model="formData.age">el-input>
el-form-item>
el-form>
我們可以看到?el-form
?和?el-form-item
不需要建立任何通信操作,那么el-form
和el-form-item
?是如何聯系起來,并且共享狀態的呢?我們帶著疑問繼續往下看。
1 基本用法
普通方式
我們用父組件 -> 子組件 -> 孫組件 的案例
父組件
<template>
<div class="father" >
<div>子組件對我說:{{ sonMes }}div>
<div>孫組件對我說:{{ grandSonMes }}div>
<son />
div>
template>
<script>import son from './son'export default {name:'father',components:{
son /* 子組件 */
},provide(){return {/* 將自己暴露給子孫組件 ,這里聲明的名稱要于子組件引進的名稱保持一致 */father:this
}
},data(){return {grandSonMes:'', /* 來自子組件的信息 */sonMes:'' /* 發送給子組件的信息 */
}
},methods:{/* 接受孫組件信息 */grandSonSay(value){this.grandSonMes = value
},/* 接受子組件信息 */ sonSay(value){this.sonMes = value
},
},
}script>
這里我們通過provide
把本身暴露出去。??????這里聲明的名稱要與子組件引進的名稱保持一致
子組件
<template>
<div class="son" >
<input v-model="mes" /> <button @click="send" >對父組件說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>
子組件通過inject
把父組件實例引進來,然后可以直接通過this.father
可以直接獲取到父組件,并調用下面的sonSay
方法。
孫組件
<template>
<div class="grandSon" >
<input v-model="mes" /> <button @click="send" >對爺爺組件說button>
div>
template>
<script>export default {/* 孫組件 */name:'grandSon',/* 引入爺爺組件 */inject:['father'],data(){return {mes:''
}
},methods:{send(){this.father.grandSonSay( this.mes )
}
}
}script>
孫組件沒有如何操作,引入的方法和子組件一致。
效果
2 插槽方式
provide , inject
?同樣可以應用在插槽上,我們給父子組件稍微變動一下。
父組件
<template>
<div class="father" >
<div>子組件對我說:{{ sonMes }}div>
<div>孫組件對我說:{{ 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:'', /* 來自子組件的信息 */sonMes:'' /* 發送給子組件的信息 */
}
},methods:{/* 接受孫組件信息 */grandSonSay(value){this.grandSonMes = value
},/* 接受子組件信息 */ sonSay(value){this.sonMes = value
},
},
}script>
子組件
<template>
<div class="son" >
<input v-model="mes" /> <button @click="send" >對父組件說button>
<slot />
div>
template>
達到了同樣的通信效果。實際這種插槽模式,所在都在父組件注冊的組件,最后孫組件也會綁定到子組件的children下面。和上述的情況差不多。
provied其他用法
provide
不僅能把整個父組件全部暴露出去,也能根據需要只暴露一部分(一些父組件的屬性或者是父組件的方法),上述的例子中,在子孫組件中,只用到了父組件的方法,所以我們可以只提供兩個通信方法。但是這里注意的是,如果我們向外提供了方法,如果方法里面有操作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)
}
},
2 優點
1 組件通信不受到子組件層級的影響
provide inject
用法 和?react.context
非常相似,?provide
相當于Context.Provider
?,inject
?相當于?Context.Consumer
,讓父組件通信不受到組件深層次子孫組件的影響。
2 適用于插槽,嵌套插槽
provide inject
?讓插槽嵌套的父子組件通信變得簡單,這就是剛開始我們說的,為什么?el-form
?和?el-form-item
能夠協調管理表單的狀態一樣。在element
源碼中?el-form
?就是將this
本身provide
出去的。
3 缺點
1 不適合兄弟通訊
provide-inject
?協調作用就是獲取父級組件們提供的狀態,方法,屬性等,流向一直都是由父到子,provide
提供內容不可能被兄弟組件獲取到的,所以兄弟組件的通信不肯能靠這種方式來完成。
2 父級組件無法主動通信
provide-inject
更像父親掙錢給兒子花一樣,兒子可以從父親這里拿到提供的條件,但是父親卻無法向兒子索取任何東西。正如這個比方,父組件對子組件的狀態一無所知。也不能主動向子組件發起通信。
4 應用場景
provide-inject
這種通信方式,更適合深層次的復雜的父子代通信,子孫組件可以共享父組件的狀態,還有一點就是適合el-form
?el-form-item
這種插槽類型的情景。
四 vuex
vuex
算是vue
中處理復雜的組件通信的最佳方案,畢竟是vue
和vuex
一個娘胎里出來的。而且vuex
底層也是用vue
實現的。相信不少同學對vuex
并不陌生。接下來我們開始介紹vuex。
1 基礎用法
vuex文件
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
export default new Vuex.Store({
state:{
fatherMes:'',
sonMes:'',
fatherMesAsync:''
},
mutations:{
sayFaher(state,value){
state.fatherMes = value
},
saySon(state,value){
state.sonMes = value
},
sayAsyncFather(state,value){
state.fatherMesAsync = value
}
},
actions:{
asyncSayFather({ commit },payload){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve(payload)
},2000)
}).then(res=>{
commit('sayAsyncFather',res)
})
}
}
})
在store
文件中,我們聲明三個mutations
?分別是向父組件通信saySon
,父組件向子組件通信,同步方法sayFaher
和異步方法sayAsyncFather
?,actions中模擬了一個三秒后執行的異步任務asyncSayFather
。
main.js
import store from './components/vuex/store'
new Vue({
render: h => h(App),
store
}).$mount('#app')
main.js
注入store
父組件
<template>
<div class="father" >
<input v-model="mes" /> <button @click="send" >同步:對子組件說button><br/>
<input v-model="asyncMes" /> <button @click="asyncSend" >異步:對子組件說button><br/>
<div>子組件對我說:{{ sonMes }}div>
<son />
div>
template>
<script>import son from './son'export default {/* 父組件 */name:'father',components:{
son ,/* 子組件 */
},data(){return {mes:'',asyncMes:''
}
},computed:{sonMes(){return this.$store.state.sonMes
}
},mounted(){console.log(this.$store)
},methods:{/* 觸發mutations,傳遞數據給子組件 */send(){this.$store.commit('sayFaher',this.mes)
},/* 觸發actions,傳遞數據給子組件 */asyncSend(){this.$store.dispatch('asyncSayFather',this.asyncMes)
}
},
}script>
父組件分別觸發同步異步方法,把信息發送給子組件。用computed
來接受vuex
中的state
。
子組件
<template>
<div class="son" >
<div> 父組件對我說:{{ fatherMes }} div>
<div> 父組件對我說(異步):{{ fatherMesAsync }} div>
<input v-model="mes" /> <button @click="send" >對父組件說button>
div>
template>
<script>export default {name:'son',data(){return {mes:'',
}
},computed:{/* 接受父組件同步消息 */fatherMes(){return this.$store.state.fatherMes
},/* 接受父組件異步消息 */fatherMesAsync(){return this.$store.state.fatherMesAsync
}
},methods:{/* 向父組件發送信息 */send(){this.$store.commit('saySon',this.mes)
},
},
}script>
子組件的方法和父組件保持一致。
效果
2 優點
1 根本解決復雜組件的通信問題
vuex
在一定程度上根本解決了vue
復雜的組件通信情況,我們不再關心兩個毫無干系的兩個組件的通信問題。
2 支持異步組件通信
vuex
中actions
允許我們做一些異步操作,然后通過commit
可以把數據傳入對應的mutation
,至于actions
為什么可以執行異步,是因為里面底層通過Promise.resolve
能夠獲取異步任務完成的狀態。
3 缺點
1 流程相比稍微復雜
vuex
通信方式相比其他方式,比較復雜,而且如果不同的模塊,需要建立獨立的modules
。
4 應用場景
實際開發場景中,不會存在demo
項目這樣簡單的通信,vuex
的出現,就是解決這些比較復雜的組件通信場景。對于中大型項目,vuex
是很不錯的狀態管理,數據通信方案。
五 事件總線一 EventBus
EventBus
事件總線,?EventBus
?所有事件統一調度,有一個統一管理事件中心,一個組件綁定事件,另一個組件觸發事件,所有的組件通信不再收到父子組件的限制,那個頁面需要數據,就綁定事件,然后由數據提供者觸發對應的事件來提供數據,這種通訊場景不僅僅應用在vue
,而且也應用在react
。
EventBus
?核心思想是事件的綁定和觸發,這一點和vue中?this.$emit
?和?this.$on
一樣,這個也是整個EventBus
核心思想。接下來我們來重點解析這個流程。
1 基本用法
EventBus
export default class EventBus {
es = {}
/* 綁定事件 */
on(eventName, cb) {
if (!this.es[eventName]) {
this.es[eventName] = []
}
this.es[eventName].push({
cb
})
}
/* 觸發事件 */
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()
這個就是一個簡單的事件總線,有on
,emit
兩個方法。
父組件
<template>
<div class="father" >
<input v-model="mes" /> <button @click="send" >對子組件說button>
<div>子組件對我說:{{ 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:''/* 發送給子組件的信息 */
}
},mounted(){/* 綁定事件 */
EventBus.on('sonSay',this.sonSay)
},methods:{/* 傳遞給子組件 */send(){
EventBus.emit('fatherSay',this.mes)
},/* 接受子組件信息 */ sonSay(value){this.sonMes = value
},
},
}script>
我們在初始化的時候通過EventBus
的on
方法綁定sonSay
方法供給給子組件使用。向子組件傳遞信息的時候,通過emit
觸發子組件的綁定方法,實現了父子通信。接下來我們看一下子組件。
子組件
<template>
<div class="son" >
<div> 父組件對我說:{{ fatherMes }} div>
<input v-model="mes" /> <button @click="send" >對父組件說button>
<div>
<input v-model="brotherMes" /> <button @click="sendBrother" >對兄弟組件說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)
},/* 父組件對我說 */fatherSay(value){this.fatherMes = value
}
},
}script>
和父組件的邏輯差不多,把需要接受數據的方法,通過EventBus
綁定,通過觸發eventBus方法,來向外部傳遞信息。我們還模擬了兄弟之間通信的場景。我們建立一個兄弟組件。
<template>
<div class="son" > 兄弟組件對我說: {{ 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>
我們可以看到,兄弟組件處理邏輯和父子之間沒什么區別。
效果
2 優點
1 簡單靈活,父子兄弟通信不受限制。
eventBus的通信方式,相比之前的幾種比較簡單,而且不受到組件層級的影響,可以實現任意兩個組件的通信。需要數據就通過on
綁定,傳遞數據就emit
觸發。
2 通信方式不受框架影響
eventBus的通信方式,不只是vue可以用,react,小程序都可以使用這種通信方式,而且筆者感覺這種通信方式更適合小程序通信,至于為什么稍后會一一道來。
4 缺點
1 維護困難,容易引起連鎖問題
如果我們采用事件總線這種通信模式,因為所有事件是高度集中,統一管理的,中間如果有一個環節出現錯誤,就會造成牽一發動全身的災難.而且后期維護也是十分困難的。
2 需要謹小慎微的命令規范
現實的應用場景,要比demo場景復雜的多,實際場景會有無數對父子組件,無數對兄弟組件,我們不肯能每個事件都叫相同名字,所以eventBus
綁定事件的命名要有嚴格的規范,不能起重復名字,也不能用錯名字。
3 不利于組件化開發
eventBus
通信方式是無法進行有效的組件化開發的,假設一個場景,一個頁面上有多個公共組件,我們只要向其中的一個傳遞數據,但是每個公共組件都綁定了數據接受的方法。我們怎么樣做到把數據傳遞給需要的組件呢?
4 應用場景
實現總線這種方式更適合,微信小程序,和基于vue構建的小程序,至于為什么呢,因為我們都知道小程序采用雙線程模型(渲染層+邏輯層)(如下圖所示),渲染層作用就是小程序wxml
渲染到我們的視線中,而邏輯層就是我們寫的代碼邏輯,在性能上,我們要知道在渲染層浪費的性能要遠大于邏輯層的代碼執行性能開銷,如果我們在小程序里采用通過props
等傳遞方式,屬性是綁定在小程序標簽里面的,所以勢必要重新渲染視圖層。如果頁面結構復雜,可能會造成卡頓等情況,所以我們通過eventBus
可以繞過渲染層,直接有邏輯層講數據進行推送,節約了性能的開銷。
六 事件總線二 new Vue
new Vue
?這種通信方式和eventBus
大致差不多,有一點不同的是,以vue
實例作為eventBus
中心,除了我們可以用$on
,$emit
之外,我們還可以用vue下的data
,watch
等方法,而且我們建立多個多個vue
,作為不同模塊的數據通信橋梁,相比上邊那個EventBus
方法,new Vue
這種方法更高效,更適合vue
項目場景。我們接著往下看。
1 基本使用
VueBus
import Vue from 'vue'
export default new Vue()
父組件
<template>
<div class="father" >
<input v-model="mes" /> <button @click="send" >對子組件說button>
<div>子組件對我說:{{ sonMes }}div>
<son />
div>
template>
<script>import son from './son'import VueBus from './vueBus'export default {/* 父組件 */ name:'father',components:{
son ,/* 子組件 */
},data(){return {mes:'',sonMes:'' /* 發送給子組件的信息 */
}
},created(){/* 綁定屬性 */
VueBus._data.mes = 'hello,world'
},mounted(){/* 綁定事件 */
VueBus.$on('sonSay',this.sonSay)
},methods:{/* 傳遞給子組件 */send(){
VueBus.$emit('fatherSay',this.mes)
},/* 接受子組件信息 */ sonSay(value){this.sonMes = value
},
},
}script>
我們通過?$on
綁定了接受數據的方法,初始化的時候向 vue_data下面綁定了數據。
子組件
<template>
<div class="son" >
<div> 父組件對我說:{{ fatherMes }} div>
<input v-model="mes" /> <button @click="send" >對父組件說button><br/>
<button @click="getFatherMes" >獲取數據button>
div>
template>
<script>import VueBus from './vueBus' export default {name:'son',data(){return {mes:'',brotherMes:'',fatherMes:''
}
},mounted(){/* 綁定事件 */
VueBus.$on('fatherSay',this.fatherSay)
},methods:{/* 向父組件傳遞信息 */send(){
VueBus.$emit('sonSay',this.mes)
},/* 父組件對我說 */fatherSay(value){this.fatherMes = value
},/* 獲取父組件存入vue中的數據 */getFatherMes(){console.log( VueBus._data.mes )
}
},
}script>
和eventBus
事件總線一樣,我們還可以直接通過_data
數據直接獲取到父組件傳遞的內容。
效果
2 優點
1 簡單靈活,任意組件之間通信。
和上邊eventBus
通信方式一樣,這種通信方式很靈活,可以輕松在任意組件間實現通信。
2 除了通信還可以使用watch
?,?computed
等方法
如果我們通過vue作為通信媒介,那么只用其中的$emit
和$on
真的是有點大材小用了,既然實例了一個vue
,我們可以輕松的使用vue的?$watch
?computed
等功能。
3 缺點
基本上EventBus
的缺點,都在vue
這種通信方式中都有存在。
4 應用場景
在項目中不考慮用vuex
的中小型項目中,可以考慮采用vue
事件總線這種通信方式,在使用的這種方式的時候,我們一定要注意命名空間,不要重復綁定事件名稱。分清楚業務模塊,避免后續維護困難。
寫在后面
我們在寫vue
項目中,具體要用什么通信方式,還要看具體的業務場景,項目大小等因素綜合評估。文章中給大家介紹了vue通信方式的優缺點,可以給大家實際工作提供一個參考。
交流討論
歡迎關注公眾號「前端試煉」,公眾號平時會分享一些實用或者有意思的東西,發現代碼之美。專注深度和最佳實踐,希望打造一個高質量的公眾號。
公眾號后臺回復「小煉」加我微信,帶你飛。
如果覺得這篇文章還不錯,來個【分享、點贊、在看】三連吧,讓更多的人也看到~