前言
在項目中,經常會遇到各種組件與組件之間,父子組件,子父組件之間的數據交互問題,而單個組件的實例出來的數據又是相互獨立的,這就意味着不同組件之間的數據無法相互引用,在項目中,大部分的項目中,一般會有幾種方式,需要我們去解決,解決了項目的組件通信問題,也就意味着,我們可以把很多的公共的部分進行封裝,然後供其它組件之間的引用。
針對不同的使用場景,在項目中有幾種方式可以使用,如:props、 $emit/ $on、vuex、 $parent / $children、
$attrs/ $listeners和provide/inject,以通俗易懂的實例講述這其中的差別及使用場景,希望對小夥伴有些許幫助。
一、 props/ $emit
父組件 A 通過props的方式向子組件B傳遞,B to A 通過 B 組件中 $emit事件在 A 組件中綁定(v-on)的方式實現。
1、父組件向子組件傳值(props)
父組件
<template>
<!--父組件 -->
<div class="app-container">
<aside>父組件與子組件之間的雙向數據通信</aside>
<el-button type="primary" plain @click="addData">添加數據</el-button>
<div>
<child :itemData="listData" />
<!-- 在子組件的Prop裏定義itemData綁定listData傳遞給子組件 -->
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import child from './components/child.vue'
@Component({
name: 'Test',
components: {
child
}
})
export default class extends Vue {
private listData: any = [
{
name: 'aaaaaa',
value: 1
},
{
name: 'bbbbbb',
value: 2
}
]
private addData() {
this.listData.push({ name: 'cccc', value: 3 })
}
}
</script>
<style lang="scss" scoped>
</style>
子組件
<template>
<div>
<div>
<div class="prop-line">
1、通過Prop傳遞過來的值
</div>
<div v-for="(item,index) in itemData" :key="index">{{item.name}}</div>
<!-- 遍歷傳遞過來的值,然後呈現到頁面 -->
<div class="get-line">
2、通過get方式返回傳遞過來的值
<div>{{getData}}</div>
</div>
<div class="get-line">
3、通過set方式對傳遞過來的值進行計算
<el-button type="primary" plain @click="computeData">計算數據</el-button>
<div>{{getNewData}}</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator'
@Component({
name: 'Test'
})
export default class extends Vue {
@Prop({ default: [] }) private itemData!: any[]
private getNewData: any[] = []
get getData() {
return this.itemData
}
set getData(val) {
this.getNewData = val;
}
private computeData() {
this.getData = [{ name: 'eeeee', value: 5 }]
}
}
</script>
<style lang="scss" scoped>
.prop-line {
padding: 10px 0;
display: block;
}
.get-line {
padding: 10px 0;
margin-top: 20px;
display: block;
}
</style>
父組件通過props向下傳遞數據給子組件共有三種形式:data、props、computed
2、子組件向父組件傳值(通過$emit事件)
在項目中有這麼一個需求,當我們引用子組件時,需要點擊子組件的(click,change…)事件,來傳遞數據給父組件,那麼父組件中需要拿到這個子組件的數據進行整合,可以使用$emit事件
父組件
<template>
<div>
<div>
<div class="prop-line">
1、通過Prop傳遞過來的值
</div>
<div v-for="(item,index) in itemData" :key="index">{{item.name}}</div>
<!-- 遍歷傳遞過來的值,然後呈現到頁面 -->
<div class="get-line">
2、通過get方式返回傳遞過來的值
<div>{{getData}}</div>
</div>
<div class="get-line">
3、通過set方式對傳遞過來的值進行計算
<el-button type="primary" plain @click="computeData">計算數據</el-button>
<div>{{getNewData}}</div>
</div>
<div class="get-line">
4、通過$emit事件傳遞參數給父組件
<el-button type="primary" plain @click="handleEmit">子傳參給父組件</el-button>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator'
@Component({
name: 'Test'
})
export default class extends Vue {
@Prop({ default: [] }) private itemData!: any[]
private getNewData: any[] = []
get getData() {
return this.itemData
}
set getData(val) {
this.getNewData = val;
}
private computeData() {
this.getData = [{ name: 'eeeee', value: 5 }]
}
private handleEmit() {
this.$emit('setDataToIndex','這是子組件傳給父組件的數據')
}
}
</script>
<style lang="scss" scoped>
.prop-line {
padding: 10px 0;
display: block;
}
.get-line {
padding: 10px 0;
margin-top: 20px;
display: block;
}
</style>
子組件通過events給父組件發送消息,實際上就是子組件把自己的數據發送到父組件。
三、 組件與組件之間的通信($emit/ $on)
這種方法通過一個空的Vue實例作爲中央事件總線(事件中心),用它來觸發事件和監聽事件,巧妙而輕量地實現了任何組件間的通信,包括父子、兄弟、跨級。當我們的項目比較大時,可以選擇更好的狀態管理解決方案vuex。
bus.js
import Vue from 'vue';
// 使用 Event Bus
const bus = new Vue();
export default bus;
引入bus
import bus from '../common/bus';
bus.$emit(事件名,要傳遞的數據);
bus.$on(事件名,data => {});
//$emit定義的事件名與$on接收的事件名必須一致
$on 監聽了自定義事件 data,因爲有時不確定何時會觸發事件,一般會在 mounted 或 created 鉤子中來監聽。
四、通過狀態管理器vuex
1、簡要介紹Vuex原理
Vuex實現了一個單向數據流,在全局擁有一個State存放數據,當組件要更改State中的數據時,必須通過執行Mutation進行,Mutation同時提供了訂閱者模式供外部插件調用獲取State數據的更新。而當所有異步操作常見於調用後端接口異步獲取更新數據或批量的同步操作需要走Action,但Action也是無法直接修改State的,還是需要通過Mutation來修改State的數據。最後,根據State的變化,渲染到視圖上。
2、各模塊在流程中的功能
- Vue Components:Vue組件。HTML頁面上,負責接收用戶操作等交互行爲,執行dispatch方法觸發對應action進行迴應。
- dispatch:操作行爲觸發方法,是唯一能執行action的方法。
- actions:操作行爲處理模塊,由組件中的 $store.dispatch(‘action 名稱’,data1)來觸發。然後由commit()來觸發mutation的調用 , 間接更新 state。負責處理Vue Components接收到的所有交互行爲。包含同步/異步操作,支持多個同名方法,按照註冊的順序依次觸發。向後臺API請求的操作就在這個模塊中進行,包括觸發其他action以及提交mutation的操作。該模塊提供了Promise的封裝,以支持action的鏈式觸發。
- commit:狀態改變提交操作方法。對mutation進行提交,是唯一能執行mutation的方法。
- mutations:狀態改變操作方法,由actions中的 commit(‘mutation 名稱’)來觸發。是Vuex修改state的唯一推薦方法。該方法只能進行同步操作,且方法名只能全局唯一。操作之中會有一些hook暴露出來,以進行state的監控等。
- state:頁面狀態管理容器對象。集中存儲Vue components中data對象的零散數據,全局唯一,以進行統一的狀態管理。頁面顯示所需的數據從該對象中進行讀取,利用Vue的細粒度數據響應機制來進行高效的狀態更新。
- getters:state對象讀取方法。圖中沒有單獨列出該模塊,應該被包含在了render中,Vue Components通過該方法讀取全局state對象。
3、Vuex與localStorage
vuex 是 vue 的狀態管理器,存儲的數據是響應式的。但是並不會保存起來,刷新之後就回到了初始狀態,具體做法應該在vuex裏數據改變的時候把數據拷貝一份保存到localStorage裏面,刷新之後,如果localStorage裏有保存的數據,取出來再替換store裏的state。