Vue組件間通信7種方式

前言

vue的核心就是組件的使用驾诈,組件是可復(fù)用的vue實(shí)例。如果項(xiàng)目中某一個(gè)部分需要在多個(gè)頁面中使用到溶浴,我們就可以將這部分代碼抽成一個(gè)可復(fù)用的組件乍迄。
然而,組件實(shí)例的作用域之間是相互獨(dú)立的戳葵,如果需要把組件之間的數(shù)據(jù)關(guān)聯(lián)起來就乓,這就需要懂組件之間的通信。

一拱烁、父組件向子組件傳值(通過props)

父組件通過v-bind綁定變量生蚁,子組件通過props方式接收。
例子:在子組件Child.vue中如何獲取到父組件App.vue中的數(shù)據(jù)title: '我是父組件的數(shù)據(jù)'戏自。
父組件

// App.vue 父組件

<template>
  <div id="app">
    <h2>父組件:</h2>
    <!-- :title 是傳到子組件的變量名邦投,便于子組件調(diào)用 -->
    <!-- title 是父組件中的data的屬性值 -->
    <Child :title="title"/>
  </div>
</template>

<script>
import Child from './components/Child'

export default {
  name: 'App',
  data () {
    return {
      title: '我是父組件的數(shù)據(jù)'
    }
  },
  components: {
    Child
  }
}
</script>

<style>
</style>

子組件

// Child.vue 子組件

<template>
  <div class="child">
    <h3>子組件:{{title}}</h3>
  </div>
</template>

<script>
export default {
  name: 'Child',
  // 接收父組件的值
  props: {
    title: String
  }
}
</script>

<style scoped>
</style>

實(shí)現(xiàn)效果

父組件向子組件傳值

總結(jié):這種方式只能由父組件向子組件傳遞,子組件不能更新父組件內(nèi)的data擅笔。也就是說志衣,當(dāng)父組件的屬性發(fā)生變化時(shí),將傳遞給子組件猛们,但不會(huì)反過來念脯,因?yàn)閜rops是單向綁定的。

二弯淘、子組件向父組件傳值(通過$emit)

相當(dāng)于子組件調(diào)用父組件的方法绿店。

子組件通過$emit發(fā)射一個(gè)方法,父組件通過v-on實(shí)現(xiàn)庐橙。
例子:當(dāng)我們點(diǎn)擊子組件的調(diào)用父組件中的方法按鈕時(shí)假勿,父組件中的內(nèi)容我是父組件的內(nèi)容修改成父組件的內(nèi)容被修改了,從而實(shí)現(xiàn)子組件向父組件傳值态鳖。
子組件

// Child.vue 子組件

<template>
  <div class="child">
    <h3>子組件:</h3>
    // 定義一個(gè)子組件傳值的方法 handleClick
    <input type="button" value="調(diào)用父組件中的方法" @click="handleClick">
  </div>
</template>

<script>
export default {
  name: 'Child',
  data () {
    return {
      title: '父組件的內(nèi)容被修改了'
    }
  },
  methods: {
    handleClick () {
      // titleChanged 自定義事件名
      // this.title 需要傳給父組件的值
      this.$emit("titleChanged", this.title)
    }
  }
}
</script>

<style scoped>
</style>

父組件

// App.vue 父組件

<template>
  <div id="app">
    <h3>父組件 -- {{title}}</h3>
    <!-- titleChanged 與子組件中自定義事件名保持一致 -->
    <!-- updateTitle 方法名转培,需要接收子組件傳遞過來的值 -->
    <Child @titleChanged="updateTitle"/>
  </div>
</template>

<script>
import Child from './components/Child'

export default {
  name: 'App',
  data () {
    return {
      title: '我是父組件的內(nèi)容'
    }
  },
  components: {
    Child
  },
  methods: {
    updateTitle (e) {
      // e 就是子組件傳遞過來的值
      this.title = e;
    }
  }
}
</script>

<style>
</style>

實(shí)現(xiàn)效果

子組件向父組件傳值

三、父組件調(diào)用子組件的方法或訪問數(shù)據(jù)(通過$ref調(diào)用)

例子:在父組件App.vue中訪問子組件Child.vue中的title數(shù)據(jù)和調(diào)用childAlert方法浆竭。
子組件

// Child.vue

<template>
  <div class="child">
  </div>
</template>

<script>
export default {
  name: 'Child',
  data () {
    return {
      title: '我是子組件的內(nèi)容'
    }
  },
  methods: {
    childAlert () {
      window.alert('我是子組件里面的彈窗浸须!')
    }
  }
}
</script>

<style scoped>
</style>

父組件

// App.vue

<template>
  <div id="app">
    <Child ref="childRef"/>
  </div>
</template>

<script>
import Child from './components/Child'

export default {
  name: 'App',
  data () {
    return {
    }
  },
  components: {
    Child
  },
  mounted () {
    // 訪問子組件的 title
    console.log(this.$refs.childRef.title)  // 輸出‘我是子組件的內(nèi)容’
    // 調(diào)用子組件的 childAlert 方法
    this.$refs.childRef.childAlert()
  }
}
</script>

<style>
</style>

總結(jié):ref如果直接在普通的DOM元素上使用惨寿,引用所指向的就是DOM元素,如果在子組件上使用羽戒,引用所指向的就是組件實(shí)例缤沦。
實(shí)現(xiàn)效果

父組件調(diào)用子組件的方法或訪問數(shù)據(jù)

四、非父子組件之間的通信(中央事件總線)

該方法通過一個(gè)空的Vue實(shí)例作為中央事件總線易稠,才能使用$emit獲取$on的數(shù)據(jù)參數(shù)缸废,實(shí)現(xiàn)組件通信。
創(chuàng)建一個(gè)空的Vue實(shí)例文件eventBus.js驶社,也就是一個(gè)中央事件總線企量。

// eventBus.js

import Vue from 'vue'
export default new Vue()

創(chuàng)建A.vue組件,引入eventBus.js文件亡电。

// A.vue 組件

<template>
  <div class="a_com">
    <h3>A組件:</h3>
    <input type="button" value="點(diǎn)擊按鈕給B組件傳遞數(shù)據(jù)" @click="emitBCom">
  </div>
</template>

<script>
// 引入空的 vue 實(shí)例
import eventBus from '../js/eventBus'

export default {
  name: 'A',
  data () {
    return {
      msg: '我是A組件的內(nèi)容'
    }
  },
  methods: {
    emitBCom () {
      // bComHandle 自定義事件名届巩,觸發(fā)一個(gè)可以讓B組件監(jiān)聽的方法
      // this.msg 要傳給B組件的值
      eventBus.$emit('bComHandle', this.msg)
    }
  }
}
</script>

<style scoped>
</style>

創(chuàng)建B.vue組件,引入eventBus.js文件份乒。

// B.vue 組件

<template>
  <div class="b_com">
    <h3>B組件:</h3>
    <p>接收A組件傳遞過來的值:{{msg}}</p>
  </div>
</template>

<script>
// 引入空的 vue 實(shí)例
import eventBus from '../js/eventBus'

export default {
  name: 'B',
  data () {
    return {
      msg: '',
    }
  },
  mounted () {
    // 監(jiān)聽A組件的自定義事件
    // data 這個(gè)data就是A組件傳遞過來的值
    eventBus.$on('bComHandle', data => this.msg = data)
  }
}
</script>

<style scoped>
</style>

App.vue中引入A.vueB.vue兩個(gè)組件恕汇,并掛載到頁面上。

// App.vue

<template>
  <div id="app">
    <a-com />
    <b-com />
  </div>
</template>

<script>
const ACom = () => import('./components/A')
const BCom = () => import('./components/B')

export default {
  name: 'App',
  components: {
    ACom,
    BCom
  }
}
</script>

<style>
</style>

整個(gè)過程的步驟:
新建一個(gè)空的Vue實(shí)例文件eventBus.js或辖;
在組件中引入定義的實(shí)例瘾英;
通過$emit觸發(fā)一個(gè)自定義事件,并傳遞數(shù)據(jù)颂暇,eventBus.$emit(自定義事件名稱缺谴,要傳遞的值)
把傳遞過來的自定義事件通過$on監(jiān)聽回調(diào)函數(shù)耳鸯,eventBus.$on(自定義事件名稱, () => {})湿蛔。

總結(jié):這種只用一個(gè)Vue實(shí)例來作為中央事件總線來管理非父子組件通信的方法只適用于通信需求簡單一點(diǎn)的項(xiàng)目,對于更加復(fù)雜的情況县爬,需要使用Vue提供的狀態(tài)管理模式Vuex來進(jìn)行處理阳啥。
實(shí)現(xiàn)效果

非父子組件之間的通信

五、子組件調(diào)用父組件的方法或訪問數(shù)據(jù)(另外一種方法:通過$parent)

例子:在子組件Child.vue中訪問父組件App.vue中的msg數(shù)據(jù)和調(diào)用show方法财喳。
父組件

// App.vue

<template>
  <div id="app">
    <child />
  </div>
</template>

<script>
const Child = () => import('./components/Child')

export default {
  name: 'App',
  data () {
    return {
      msg: '我是父組件中的內(nèi)容'
    }
  },
  methods: {
    show () {
      console.log('我是父組件的方法察迟!')
    }
  },
  components: {
    Child
  }
}
</script>

<style>
</style>

子組件

// Child.vue

<template>
  <div class="child">
    <h3>我是子組件:</h3>
    <p>訪問父組件中的msg數(shù)據(jù):{{msg}}</p>
  </div>
</template>

<script>
export default {
  name: 'Child',
  data () {
    return {
      msg: ''
    }
  },
  mounted () {
    // 訪問父組件中的 msg 數(shù)據(jù)
    this.msg = this.$parent.msg
    // 調(diào)用父組件中的 show 方法
    this.$parent.show()
  }
}
</script>

<style scoped>
</style>

總結(jié):用此方法前提得知道父組件是誰,如果項(xiàng)目中組件嵌套非常多的話纲缓,不推薦使用這個(gè)方法!
實(shí)現(xiàn)效果

子組件調(diào)用父組件的方法或訪問數(shù)據(jù)

六喊废、跨級組件間的通信(通過provide/inject)

這對選項(xiàng)需要一起使用祝高,以允許一個(gè)祖先組件向其所有子孫后代注入一個(gè)依賴,不論組件層次有多深污筷,并在起上下游關(guān)系成立的時(shí)間里始終生效工闺。
provide選項(xiàng)應(yīng)該是:一個(gè)對象或返回一個(gè)對象的函數(shù)乍赫。該對象包含可注入其子孫的屬性。
inject選項(xiàng)應(yīng)該是:一個(gè)字符串?dāng)?shù)組陆蟆,或一個(gè)對象雷厂,對象的key是本地的綁定名,value是:在可用的注入內(nèi)容中搜索用的key(字符串或Symbol)叠殷,或一個(gè)對象改鲫,該對象的:from屬性是在可用的注入內(nèi)容中搜索用的 key(字符串或 Symbol),default屬性是降級情況下使用的value林束。
假設(shè)我們有兩個(gè)組件Child.vueGrandparent.vue像棘,來看一下比較簡單的用法:
祖先級組件

// Grandparent.vue組件

<template>
  <div class="grandparent">
    <child />
  </div>
</template>

<script>
const Child = () => import('./Child')

export default {
  name: 'Grandparent',
  provide: {
    name: 'allen'
  },
  components: {
    Child
  }
}
</script>

<style scoped>
</style>

子孫級組件

// Child.vue

<template>
  <div class="child">
    <!-- 獲取 Grandparent 組件中的 name 值 -->
    <h2>{{gp_name}}</h2>
  </div>
</template>

<script>

export default {
  name: 'Child',
  data () {
    return {
      gp_name: ''
    }
  },
  inject: ['name'],
  mounted () {
    this.gp_name = this.name
    console.log(this.name)      // 輸出 allen
  }
}
</script>

<style scoped>
</style>

我們在祖先級組件中設(shè)置了一個(gè)provide:name,值為allen壶冒,它的作用就是將name這個(gè)變量提供給它的所有子孫級組件缕题。而在子孫級組件中通過inject注入了從上級組件中提供的name變量,那么在子孫級組件中胖腾,就可以直接通過this.name來訪問了烟零。

提示:provideinject綁定并不是可響應(yīng)的。這是刻意為之的咸作。然而锨阿,如果你傳入了一個(gè)可監(jiān)聽的對象,那么其對象的屬性還是可響應(yīng)的性宏。

關(guān)于這對選項(xiàng)的深入用法群井,大家可以去看看官方文檔哦!

七毫胜、兄弟組件之間的通信(通過Vuex)

Vuex是一個(gè)專為Vue.js應(yīng)用程序開發(fā)的狀態(tài)管理模式书斜。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化酵使。
父組件App.vue

// App.vue

<template>
  <div id="app">
    <child-a />
    <child-b />
  </div>
</template>

<script>
const ChildA = () => import('./components/ChildA')  // 導(dǎo)入 ChildA 組件
const ChildB = () => import('./components/ChildB')  // 導(dǎo)入 ChildB 組件

export default {
  name: 'App',
  components: {
    ChildA,
    ChildB
  }
}
</script>

<style>
</style>

子組件ChildA.vue

// ChildA.vue

<template>
  <div class="child-a">
    <h3>A組件:</h3>
    <input type="button" value="點(diǎn)擊按鈕讓B組件接收數(shù)據(jù)" @click="AComHandler">
    <p>B組件的數(shù)據(jù):{{bmsg}}</p>
  </div>
</template>

<script>

export default {
  name: 'ChildA',
  data () {
    return {
      amsg: '我是A組件的內(nèi)容'
    }
  },
  computed: {
    bmsg () {
      return this.$store.state.BComMsg
    }
  },
  methods: {
    AComHandler () {
      this.$store.commit('transferAComMsg', {
        AComMsg: this.amsg
      })
    }
  }
}
</script>

<style scoped>
</style>

子組件ChildB.vue

// ChildB.vue

<template>
  <div class="child-b">
    <h3>B組件:</h3>
    <input type="button" value="點(diǎn)擊按鈕讓A組件接收到數(shù)據(jù)" @click="AComHandler">
    <p>A組件的數(shù)據(jù):{{amsg}}</p>
  </div>
</template>

<script>

export default {
  name: 'ChildB',
  data () {
    return {
      bmsg: '我是B組件的內(nèi)容'
    }
  },
  computed: {
    amsg () {
      return this.$store.state.AComMsg
    }
  },
  methods: {
    AComHandler () {
      this.$store.commit('transferBComMsg', {
        BComMsg: this.bmsg
      })
    }
  }
}
</script>

<style scoped>
</style>

引入vuex模塊

> npm install vuex --save

src文件夾下創(chuàng)建store文件夾荐吉,并創(chuàng)建index.js文件

// index.js 

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    // 初始化A組件和B組件的數(shù)據(jù)
    AComMsg: '',
    BComMsg: ''
  },
  mutations: {
    // 將A組件數(shù)據(jù)存放到state中
    transferAComMsg (state, payload) {
      state.AComMsg = payload.AComMsg
    },
    // 將B組件數(shù)據(jù)存放到state中
    transferBComMsg (state, payload) {
      state.BComMsg = payload.BComMsg
    }
  }
})

export default store

main.js導(dǎo)入

// main.js

import Vue from 'vue'
import App from './App'

import store from './store/index'

Vue.config.productionTip = false

new Vue({
  el: '#app',
  store,
  render: h => h(App)
})

參考資料
Vue.js 官方文檔
Vue.js API官方文檔
Vue.js 組件通信方式
Vuex 官方文檔使用
Vue進(jìn)行兄弟組件通信

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市口渔,隨后出現(xiàn)的幾起案子样屠,更是在濱河造成了極大的恐慌,老刑警劉巖缺脉,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痪欲,死亡現(xiàn)場離奇詭異,居然都是意外死亡攻礼,警方通過查閱死者的電腦和手機(jī)业踢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來礁扮,“玉大人知举,你說我怎么就攤上這事瞬沦。” “怎么了雇锡?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵逛钻,是天一觀的道長。 經(jīng)常有香客問我锰提,道長曙痘,這世上最難降的妖魔是什么羡洁? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任袜炕,我火速辦了婚禮堪簿,結(jié)果婚禮上敞掘,老公的妹妹穿的比我還像新娘冯吓。我一直安慰自己窟蓝,他們只是感情好挠阁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布乱灵。 她就那樣靜靜地躺著踢故,像睡著了一般文黎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上殿较,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天耸峭,我揣著相機(jī)與錄音,去河邊找鬼淋纲。 笑死劳闹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的洽瞬。 我是一名探鬼主播本涕,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼伙窃!你這毒婦竟也來了菩颖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤为障,失蹤者是張志新(化名)和其女友劉穎晦闰,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鳍怨,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡呻右,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鞋喇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片声滥。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖确徙,靈堂內(nèi)的尸體忽然破棺而出醒串,到底是詐尸還是另有隱情,我是刑警寧澤鄙皇,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布芜赌,位于F島的核電站,受9級特大地震影響伴逸,放射性物質(zhì)發(fā)生泄漏缠沈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一错蝴、第九天 我趴在偏房一處隱蔽的房頂上張望洲愤。 院中可真熱鬧,春花似錦顷锰、人聲如沸柬赐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肛宋。三九已至,卻和暖如春束世,著一層夾襖步出監(jiān)牢的瞬間酝陈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工毁涉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沉帮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓贫堰,卻偏偏與公主長得像穆壕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子严嗜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355