Vue組件間通信詳解

????眾所周知屿良,組件是 vue.js最強(qiáng)大的功能之一溶浴,而組件實(shí)例的作用域是相互獨(dú)立的,這就意味著不同組件之間的數(shù)據(jù)無(wú)法相互引用管引。那么組件之間如何通信呢?主要有闯两,props褥伴、$emit/$onvuex漾狼、$parent / $children重慢、$attrs/$listenersprovide/inject,本文將分別介紹組件間的通信方式逊躁。

image.png

上圖表示了組件之間所有可能的關(guān)系
A 和 B似踱、B 和 C、B 和 D 都是父子關(guān)系稽煤,C 和 D 是兄弟關(guān)系核芽,A 和 C 是隔代關(guān)系(可能隔多代)

第一種 props/$emit,適用于父子組件之間的通信

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Vue組件間的通信</title>
  </head>
  <body>
    <div id="app">
      <h3>組件間的通信(父子組件之間的通信props和$emit)</h3>
      <parent></parent>
    </div>
  </body>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    Vue.component('parent', {
      template: `
        <div>
          <h2>父組件</h2>
          <p>子組件傳來(lái)的數(shù)據(jù)==>{{ fromChildMsg }}</p>
          <child :msgParent="msgParent" @sendMsgToParent="childMsgHandle"></child>  
        </div>
      `,
      data(){
        return {
          msgParent:'父組件的數(shù)據(jù)',
          fromChildMsg:''
        }
      },
      methods: {
        childMsgHandle(msg){
          this.fromChildMsg = msg
        }
      }
    })
    Vue.component('child', {
      template: `
        <div>
          <h3>子組件</h3>
          <p>父組件傳來(lái)的數(shù)據(jù)==>{{ msgParent }}</p>
        </div>
      `,
      props:{
        msgParent:{
          type:String
        }
      },
      data() {
        return {
          msgChild: '子組件的數(shù)據(jù)'
        }
      },
      mounted () {
        this.$emit('sendMsgToParent',this.msgChild)
      }
    })
    const app = new Vue({
      el: '#app',
      data: {}
    })
  </script>
</html>

注意:在組件間通行過(guò)程中酵熙,Vue組件遵循單向數(shù)據(jù)流原則:就是數(shù)據(jù)只能通過(guò) props 由父組件流向子組件轧简,而子組件并不能通過(guò)修改 props 傳過(guò)來(lái)的數(shù)據(jù)修改父組件的相應(yīng)狀態(tài)。原因是:所有的 prop 都使得其父子 prop 之間形成了一個(gè)單向下行綁定:父級(jí) prop 的更新會(huì)向下流動(dòng)到子組件中匾二,但是反過(guò)來(lái)則不行哮独。這樣會(huì)防止從子組件意外改變父級(jí)組件的狀態(tài),從而導(dǎo)致你的應(yīng)用的數(shù)據(jù)流向難以理解察藐。額外的皮璧,每次父級(jí)組件發(fā)生更新時(shí),子組件中所有的 prop 都將會(huì)刷新為最新的值分飞。這意味著你不應(yīng)該在一個(gè)子組件內(nèi)部改變 prop悴务。如果你這樣做了,Vue 會(huì)在瀏覽器的控制臺(tái)中發(fā)出警告浸须。

運(yùn)行結(jié)果

image.png

第二種惨寿,$attrs$listeners,適用于后代組件的通信

  • $attrs:包含了父作用域中不被prop 所識(shí)別 (且獲取) 的特性綁定 (classstyle 除外)删窒。當(dāng)一個(gè)組件沒(méi)有聲明任何 prop 時(shí)裂垦,這里會(huì)包含所有父作用域的綁定屬性 (classstyle 除外),并且可以通過(guò) v-bind="$attrs" 傳入內(nèi)部組件肌索。
  • $listeners:包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監(jiān)聽(tīng)器蕉拢。它可以通過(guò) v-on="$listeners" 傳入內(nèi)部組件。
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Vue組件間的通信</title>
  </head>
  <body>
    <div id="app">
      <h3>組件間的通信(父組件和后代之間的通信$attrs和$listeners)</h3>
      <parent />
    </div>
  </body>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    Vue.component('parent', {
      template: `
        <div>
          <h2>父組件</h2>
          <p>子組件傳來(lái)的數(shù)據(jù)==>{{ fromChildMsg }}</p>
          <p>后代件傳來(lái)的數(shù)據(jù)==>{{ fromProgenyMsg }}</p>
          <child :msgParentToProgeny="msgParentToProgeny" :msgParent="msgParent" @sendMsgToParent="childMsgHandle" @progenyToParent="progenyMsgHandle"/>
        </div>
      `,
      data(){
        return {
          msgParent:'父組件的數(shù)據(jù)',
          msgParentToProgeny:'父組件數(shù)據(jù)給后代組件',
          fromChildMsg:'',
          fromProgenyMsg:''
        }
      },
      methods: {
        childMsgHandle(msg){
          this.fromChildMsg = msg
        },
        progenyMsgHandle(msg){
          this.fromProgenyMsg = msg
        }
      }
    })
    Vue.component('child', {
      template: `
        <div>
          <h3>子組件</h3>
          <p>父組件傳來(lái)的數(shù)據(jù)==>{{ msgParent }}</p>
          //  后代組件中能直接觸發(fā) progenyToParent 的原因在于:子組件調(diào)用 后代組件時(shí),使用 v-on 綁定了 $listeners 屬性 
          //  通過(guò)v-bind 綁定 $attrs 屬性晕换,后代組件可以直接獲取到 父組件組件中傳遞下來(lái)的 props(除了 子組件中 props聲明的) 
          <progeny v-bind="$attrs" v-on="$listeners" />
        </div>
      `,
      props:{
        msgParent:{
          type:String
        }
      },
      data() {
        return {
          msgChild: '子組件的數(shù)據(jù)'
        }
      },
      mounted () {
        this.$emit('sendMsgToParent',this.msgChild)
      }
    })
    Vue.component('progeny',{
      template:`
        <div>
          <h3>后代組件</h3>
        </div>
      `,
      data(){
        return {
          progenyMsg:'后代組件的信息'
        }
      },
      mounted () {
        this.$emit('progenyToParent',this.progenyMsg)
      }
    })
    const app = new Vue({
      el: '#app',
      data: {}
    })
  </script>
</html>

運(yùn)行結(jié)果

image.png

第三種:事件總線方式午乓,在項(xiàng)目規(guī)模不大的情況下,完全可以使用中央事件總線 的方式,中央事件總線 EventBus 非常簡(jiǎn)單闸准,就是任意組件和組件之間打交道益愈,沒(méi)有多余的業(yè)務(wù)邏輯,只需要在狀態(tài)變化組件觸發(fā)一個(gè)事件夷家,然后在處理邏輯組件監(jiān)聽(tīng)該事件就可以蒸其。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Vue組件間的通信</title>
  </head>
  <body>
    <div id="app"></div>
  </body>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    Vue.component('child1', {
      template: `
        <div>
          <h3>子組件1</h3>  
          <button @click="$EventBus.$emit('child1Event',child1Msg)">bus</button>
        </div>
      `,
      data() {
        return {
          child1Msg:'組件1的信息'
        }
      }
    })
    Vue.component('child2', {
      template: `
        <div>
          <h3>子組件2</h3>  
        </div>
      `,
      mounted () {
        this.$EventBus.$on('child1Event',msg=>{
          console.log(msg)
        })
      }
    })
    // 定義事件總線
    const EventBus = new Vue()
    Vue.prototype.$EventBus = EventBus
    const app = new Vue({
      el: '#app',
      template: `
        <div>
          <h3>組件間的通信(父子組件之間的通信-事件總線)</h3>
          <child1 />
          <child2 />
        </div>
      `
    })
  </script>
</html>

image.png

第四種,provideinject库快,適用于根組件和后代組件通信

在父組件中通過(guò) provider 來(lái)提供屬性摸袁,然后在子組件中通過(guò) inject 來(lái)注入變量。不論子組件有多深义屏,只要調(diào)用了 inject 那么就可以注入在 provider 中提供的數(shù)據(jù)靠汁,而不是局限于只能從當(dāng)前父組件的 prop 屬性來(lái)獲取數(shù)據(jù),只要在父組件的生命周期內(nèi)闽铐,子組件都可以調(diào)用蝶怔。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Vue組件間的通信</title>
  </head>
  <body>
    <div id="app">
      <c1 />
    </div>
  </body>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    Vue.component('c1', {
      template: `
        <div>
          <h3>組件1</h3> 
          <p>組件1-{{ rootMsg }}</p>
          <c11 /> 
        </div>
      `,
      provide: {
        c1Msg: '組件1信息'
      },
      inject: ['rootMsg']
    })
    Vue.component('c11', {
      template: `
        <div>
          <h3>組件1-1</h3>  
          <p>組件1-1-{{ c1Msg }}</p>
          <c111 />
        </div>
      `,
      inject: ['c1Msg']
    })
    Vue.component('c111', {
      template: `
        <div>
          <h3>組件1-1-1</h3>
          <p>組件1-1-1{{ c1Msg }}</p> 
        </div>
      `,
      inject: ['c1Msg']
    })
    const app = new Vue({
      el: '#app',
      provide: {
        rootMsg: '根信息'
      }
    })
  </script>
</html>
image.png

第五種 v-model,適用于父子組件的通信

v-model用于父子組件之間的通信,體現(xiàn)的是v-model的實(shí)現(xiàn)原理:綁定value阳啥,監(jiān)聽(tīng)input事件添谊。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Vue組件間的通信</title>
  </head>
  <body>
    <div id="app">
      <parent />
    </div>
  </body>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    Vue.component('parent',{
      template:`
        <div>
          <h3>父組件</h3>  
          <p>父組件==>{{ message }}</p>
          <child v-model="message" />
        </div>
      `,
      data(){
        return {
          message:''
        }
      }
    })
    Vue.component('child',{
      template:`
        <div>
          <input type="text" v-model="msg" @input="changeValueHandle" /> 
        </div>
      `,
      props:{
        value:{
          type:String //v-model 會(huì)自動(dòng)傳遞一個(gè)字段為 value 的 props 屬性
        }
      },
      data(){
        return {
          msg:''
        }
      },
      methods:{
        changeValueHandle(){
          this.$emit('input',this.msg)
        }
      }
    })
    const app = new Vue({
      el: '#app'
    })
  </script>
</html>
image.png

第六種 $parent$children,適用于父子組件之間的通信

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Vue組件間的通信</title>
  </head>
  <body>
    <div id="app">
      <parent />
    </div>
  </body>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    Vue.component('parent', {
      template: `
        <div>
          <h2>父組件</h2>  
          {{ childMsgFromParent }}
          <button @click="getChildMsgHandle">獲取子組件信息</button>
          <child />
        </div>
      `,
      data() {
        return {
          parentMsg: '父組件的信息',
          childMsgFromParent:''
        }
      },
      methods: {
        getChildMsgHandle() {
          this.childMsgFromParent = this.$children[0].childMsg
          console.log(this.$children[0].childMsg)
        }
      }
    })
    Vue.component('child', {
      template: `
        <div>
          <h3>子組件</h3>  
          {{ parentMsgFromChild }}
          <button @click="getParentMsgHandle">獲取父組件信息</button>
        </div>
      `,
      data() {
        return {
          childMsg: '子組件信息',
          parentMsgFromChild:''
        }
      },
      methods: {
        getParentMsgHandle() {
          this.parentMsgFromChild = this.$parent.parentMsg
          console.log(this.$parent.parentMsg)
        }
      }
    })
    const app = new Vue({
      el: '#app'
    })
  </script>
</html>
image.png

第七種 Vuex,全局狀態(tài)管理察迟,這里暫不做介紹斩狱。詳情請(qǐng)看Vuex

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市扎瓶,隨后出現(xiàn)的幾起案子所踊,更是在濱河造成了極大的恐慌,老刑警劉巖概荷,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秕岛,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡误证,警方通過(guò)查閱死者的電腦和手機(jī)继薛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)愈捅,“玉大人遏考,你說(shuō)我怎么就攤上這事±督鳎” “怎么了灌具?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵青团,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我咖楣,道長(zhǎng)督笆,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任诱贿,我火速辦了婚禮娃肿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘珠十。我一直安慰自己咸作,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布宵睦。 她就那樣靜靜地躺著,像睡著了一般墅诡。 火紅的嫁衣襯著肌膚如雪壳嚎。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,457評(píng)論 1 311
  • 那天末早,我揣著相機(jī)與錄音烟馅,去河邊找鬼。 笑死然磷,一個(gè)胖子當(dāng)著我的面吹牛郑趁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播姿搜,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼寡润,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了舅柜?” 一聲冷哼從身側(cè)響起梭纹,我...
    開(kāi)封第一講書(shū)人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎致份,沒(méi)想到半個(gè)月后变抽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡氮块,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年绍载,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滔蝉。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡击儡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出锰提,到底是詐尸還是另有隱情曙痘,我是刑警寧澤芳悲,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站边坤,受9級(jí)特大地震影響名扛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茧痒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一肮韧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧旺订,春花似錦弄企、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至樱调,卻和暖如春约素,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背笆凌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工圣猎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乞而。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓送悔,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親爪模。 傳聞我的和親對(duì)象是個(gè)殘疾皇子欠啤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容

  • 前言 組件是 vue.js最強(qiáng)大的功能之一,而組件實(shí)例的作用域是相互獨(dú)立的屋灌,這就意味著不同組件之間的數(shù)據(jù)無(wú)法相互引...
    用技術(shù)改變世界閱讀 2,169評(píng)論 1 3
  • 摘要: 總有一款合適的通信方式跪妥。 作者:浪里行舟 Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所有声滥。 前言 組件是 v...
    Fundebug閱讀 15,575評(píng)論 3 57
  • 前言 組件是 vue.js最強(qiáng)大的功能之一眉撵,而組件實(shí)例的作用域是相互獨(dú)立的,這就意味著不同組件之間的數(shù)據(jù)無(wú)法相互引...
    Vicky丶Amor閱讀 5,997評(píng)論 10 162
  • 前言 組件是 vue.js最強(qiáng)大的功能之一落塑,而組件實(shí)例的作用域是相互獨(dú)立的纽疟,這就意味著不同組件之間的數(shù)據(jù)無(wú)法相互引...
    7abbcd54a89d閱讀 713評(píng)論 0 1
  • 背景 ??Vue是單頁(yè)面應(yīng)用,單頁(yè)面應(yīng)用又是由組件構(gòu)成憾赁,各個(gè)組件之間又互相關(guān)聯(lián)污朽,那么如何實(shí)現(xiàn)組件之間通信就顯得尤為...
    A鄭家慶閱讀 902評(píng)論 0 1