碼文不易啊,轉載請帶上本文連結呀,感謝感謝 https://www.cnblogs.com/echoyya/p/14404397.html
在vue專案中瞭解元件間通訊很重要,也是最基礎的面試題,可以大致總結為以下幾種情況和方法:
一、父向子傳值
父 to 子
:通過動態繫結屬性
的方式,子元件在props
中去接收,佔為已用
// father.vue
<child :name="name" />
data() {
return {
name:'Echoyya',
}
}
// child.vue
export default {
props: ['name'],
mounted() {
console.log(this.name) // Echoyya
}
}
二、子向父傳值 - 1. 事件分發 emit
子 to 父
:通過.emit('事件名稱', 傳遞的引數)事件分發
的方式, 父元件當中,呼叫子元件標籤上繫結自定義事件,其中包含一個引數
,即子元件傳遞過來的資料
-
emit 只能接受兩個引數,其餘不生效,第一個引數:事件名稱,第二個: 傳遞的資料
-
事件分發,不一定要通過點選事件,也可使用鉤子函式等
-
需要傳遞多個引數時,emit第二個引數可選擇陣列或是物件
// 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)
}
}
};
三、子向父傳值 - 2. 父元件動態繫結方法
-
除了平時常見的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
屬性 -
主動獲取屬性:
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 傳值
多層級元件之間相互傳值,或兄弟元件之間傳值,通常使用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 內部實現原理
-
手動模擬一個 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 應用程式開發的 狀態管理模式,集中式管理應用所有元件的狀態,進行元件通訊
-
安裝外掛,引入 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>