在vue項目中瞭解組件間通信很重要,也是最基礎的面試題,能夠大體總結爲如下幾種狀況和方法:html
父 to 子
:經過動態綁定屬性
的方式,子組件在props
中去接收,佔爲已用vue
// father.vue <child :name="name" /> data() { return { name:'Echoyya', } }
// child.vue export default { props: ['name'], mounted() { console.log(this.name) // Echoyya } }
子 to 父
:經過.emit('事件名稱', 傳遞的參數)事件分發
的方式, 父組件當中,調用子組件標籤上綁定自定義事件,其中包含一個參數
,即子組件傳遞過來的數據面試
emit 只能接受兩個參數,其他不生效,第一個參數:事件名稱,第二個: 傳遞的數據vuex
事件分發,不必定要經過點擊事件,也可以使用鉤子函數等數組
須要傳遞多個參數時,emit第二個參數可選擇數組或是對象promise
// father.vue <child @send="getChildData" /> methods: { getChildData(data){ console.log('子組件傳遞過來的數據:' + data); } }
// child.vue <button @click="send">發送數據</button> export default { data() { return { year:2021 }; }, methods:{ send(){ this.$emit('send',this.year) } } };
除了平時常見的emit 事件分發的方式實現子向父傳值,還有一種不太常見的方式緩存
在前面有提到過,父向子傳值能夠經過在標籤上動態綁定屬性的方式,一樣也能夠動態綁定方法
,在子組件props
中接收安全
在根據實際狀況,在子組件中調用該方法,並傳值,此時父組件執行對應的方法及參數的處理,該參數即是子向父傳遞的數據服務器
// father.vue <child :fn="getChildData" /> methods: { getChildData(data){ // 子組件傳遞過來的數據 console.log(data); // 子組件 - 2021 } }
// child.vue export default { props: ['fn'], mounted(){ this.fn('子組件 - 2021') } };
父組件中調用子組件,綁定一個ref
屬性app
主動獲取屬性:this.$refs.ref名稱.屬性
主動調用方法:this.$refs.ref名稱.方法()
// father.vue <child :name="name" ref="child" /> mounted() { console.log(this.$refs.child.year); // 2021 this.$refs.child.showName() // Echoyya:2021 }
// child.vue export default { data(){ return { year:2021 } }, methods:{ showName(){ console.log('Echoyya:'+ this.year); } } };
子組件中調用父組件,使用 this.$parent
主動獲取屬性:this.$parent.屬性
主動調用方法:this.$parent.方法()
,還能夠向父組件傳遞參數
// father.vue <div id="app"> <child /> </div> <script> import child from "./components/child"; export default { components: { child }, data() { return { name:'Echoyya', }; }, methods: { parentMethod(data){ // 能夠接收子組件調用時傳遞的參數 console.log(data); // 2021 } } }; </script>
// child.vue export default { mounted(){ console.log(this.$parent.name); // Echoyya this.$parent.parentMethod(2021); // 2021 } };
多層級組件之間相互傳值,或兄弟組件之間傳值,一般使用EventBus
,實際上就是 vm,即 Vue 的實例,經過發佈訂閱者模式,實現任意兩組件之間的通信,父子亦可。
首先建立EventBus文件,導出vm 實例
在須要通信的兩組件中分別引入該文件
經過發佈訂閱 .$emit
和 .$on
實現傳值 ,操做的事件名需相同。
// EventBus.js import Vue from 'vue' var vm = new Vue() export default vm
// App.vue <template> <div id="app"> <bus-one /> <bus-two /> </div> </template> <script> import busOne from "./components/bus1"; import busTwo from "./components/bus2"; export default { components: { busOne, busTwo } } </script>
// bus1.vue <template> <div> 組件 1 發佈 <button @click="sendNum">通信發送數據</button> </div> </template> <script> import eb from "../EventBus"; export default { methods:{ sendNum(){ eb.$emit('sendData',111) } } }; </script>
// bus2.vue <template> <div> 組件 2 訂閱 </div> </template> <script> import eb from "../EventBus"; export default { created(){ eb.$on('sendData',function(data){ console.log(data); // 111 }) } }; </script>
手動模擬一個 EventBus,實現發佈訂閱的效果,建立一個 myEventBus 文件
建立一個對象,分別設置發佈、訂閱的事件,以及事件存儲的對象,經過事件訂閱將事件存儲至事件對象中,
事件發佈時,自動調用事件對象中相同 key 的全部事件
myEventBus.html
<script> var eventbus = { fnObj:{ //abc:[fn1,fn2], 能夠屢次訂閱同一個事件,所以每一個 key 對應一個數組 }, $on: function (id, fn) { if(!this.fnObj[id]){ this.fnObj[id] = [fn] }else{ this.fnObj[id].push(fn) } }, $emit: function (id, data) { if(this.fnObj[id]){ var fnArr = this.fnObj[id] // 當事件觸發時,循環執行每一個 key 對應的 全部function for(var i = 0;i<fnArr.length;i++){ var fn = fnArr[i] fn(data) } }else{ console.log('異常,函數不存在') } } } eventbus.$on('abc',function(data){ console.log(1) }) eventbus.$on('abc',function(data){ console.log(2) }) eventbus.$on('abc',function(data){ console.log(3) }) eventbus.$on('abc',function(data){ console.log(4) }) eventbus.$emit('abc','123') </script>
Vuex 應用程序開發的 狀態管理模式,集中式管理應用全部組件的狀態,進行組件通信
安裝插件,引入 Vuex,建立 store 實例,配置到 Vue 實例中
爲防止狀態,方法聲明等過度重複和冗餘,Vuex 提供了一些輔助函數,mapState
,mapGetters
,mapMutations
,mapActions
,可以使用擴展運算符展開,其中:
mapState
,mapGetters
聲明在 computed 中
mapMutations
,mapActions
聲明在 methods 中
建立 store 實例,核心屬性:
State
:一個對象,包含所有的應用狀態。做爲惟一數據源
存在。store實例會注入到根組件下的全部子組件
Getters
:state 中數據派生出一些狀態相似計算屬性,返回值會根據它的依賴被緩存起來,只有依賴發生改變時纔會被從新計算,state 做爲其第一個參數.
Mutations
:更改 store 中的狀態,store.commit('事件名',payload參數可選)
觸發,只作同步操做,
Actions
:相似於Mutations, 提交的是 mutation,而不是直接變動狀態,能夠包含任意異步操做。this.$store.dispatch
分發,異步執行完畢,返回封裝的promise 對象。接受一個與 store 實例具備相同方法和屬性的對象
Modules
:爲防止 store 對象過於複雜,可將其分割成模塊,每一個模塊都有本身的State,Getters,Mutations,Actions,甚至能夠嵌套子模塊
// main.js import Vue from 'vue' import App from './App.vue' import Vuex from 'vuex' import numModule from './numModule.js' Vue.use(Vuex) var store = new Vuex.Store({ modules:{ numModule } }) new Vue({ store, render: h => h(App) }).$mount('#app')
// numModule.js export default { state: { num: 1 }, getters: { getNum(state) { // 能夠返回一些派生狀態 return state.num } }, mutations: { // 同步修改狀態的函數 changeNum(state, payload) { state.num += payload.num }, // 異步時,開發工具會遺漏快照,不便於調試(不推薦) testAsync(state, data) { // 模擬服務器獲取數據 setTimeout(function () { state.num += data }, 0) } }, actions: { // 異步獲取數據,提交給 mutation 進行修改 incNumByService(store) { setTimeout(function () { var serviceData = 0.1 store.commit('changeNum', { num: serviceData }) }, 0) } } }
// App.vue <template> <div id="app"> 第一組件 <p>state:{{$store.state.numModule.num}}</p> <!-- getters:方式爲只讀,數據更安全 --> <p>getters:{{$store.getters.getNum}}</p> <p>MapGetters:{{gn}}</p> <button @click="changeNum">mutation同步</button> <button @click="testAsync">mutation異步</button> <button @click="testActions">action異步</button> <Son /> </div> </template> <script> import { mapGetters } from "vuex"; import Son from "./components/son"; export default { components: { Son }, methods: { changeNum() { this.$store.commit({ type: "changeNum", num: 1 }); //效果同上 // this.$store.commit("changeNum",{ // num: 1 // }); }, testAsync() { this.$store.commit("testAsync", 10); }, testActions() { // dispatch 異步執行完畢,返回 封裝的promise 對象 this.$store.dispatch("incNumByService").then(res => { alert("dispatch執行完畢"); }); } }, computed: { //使用對象展開運算符將 getter 混入 computed 對象中 //若是想將一個 getter 屬性另取一個名字,使用對象形式: ...mapGetters({ gn: "getNum" // 把 `this.gn` 映射爲 `this.$store.getters.getNum` }) } }; </script>
// Son.vue <template> <div class="hello"> 第二組件 <button>state:{{$store.state.numModule.num}}</button> <!-- getters:方式爲只讀,數據更安全 --> <button>getters:{{$store.getters.getNum}}</button> <button @click="clickMe">MapGetters:{{gn}}</button> </div> </template> <script> import { mapGetters } from "vuex"; export default { computed: { ...mapGetters({ gn: "getNum" }) } }; </script>