Vue組件通信的幾種方式【轉】

Vue組件通信的幾種方式【轉】

組件通信主要有以下幾種方式:?props,$emit和$on,vuex,$attrs和$listeners,provide和inject,\$parent,$children與 ref,evenbus以及?$root?按使用場景可以劃分為以下三類:

父子組件通信

props

$emit和$on

$parent和$children

ref

$attrs和$listeners

兄弟組件通信

$parent

$root

eventBus

vuex

跨層級通信

eventBus

provide和inject

vuex

1、props

這是父子間組件通信的常見方式,一般用于父組件向子組件傳遞數據,并且是響應式的。一般情況下子組件不會去直接改變從父組件同過?prop?傳過來的數據,如果想改變的話,一般會在子組件?data?中定義一個變量來進行接收:

注意:這種通信方式是響應式的,一般情況下是用于單向通信的(父向子傳遞數據),但是如果在通過props特性傳的是一個引用類型的數據(如?Object?和?Array?)時,在子組件修改該引用類型的數據時,也會改變父組件中該props的值。所以這種方式也是可以在兄弟間通信的。如下面的例子,在child1或者child2中修改user?都會觸發?user的更新。

//parent<template><child1:user="user"/><child2:user="user"/></template>importchild1from"./child1"importchild2from"./child2"exportdefault{data(){return{user:{userName:'james'}}},components:{child1,child2},}//child1.vue<template><span>{{userInfo.uaerName}}</span><button@click="change">修改</button></template>exportdeafult{props:{user:Object}data(){return{userInfo:this.user}},methods:{change(){this.userInfo.userName="username被修改了"}}}//child2.vue<template><span>{{userInfo.uaerName}}</span></template>exportdefault{props:{user:Object}data(){return{userInfo:this.user}}}

2、 \$emit和$on

這種通信方式主要是?解決子組件向父組件傳遞數據?的問題。不適合數據隔代傳遞(跨組件)。

//子組件中<button@click="send"></button>...methods:{send(){this.$emit("sendMsg","我是子組件的數據")}}//父組件中created(){this.$on("sendMsg",data=>{console.log(data);//"我是子組件的數據"})}

3、vuex

這種通信方式屬于全局通信方式,一般vuex用在中大型項目中。 主要是有以下幾個核心概念:

1、state:?用于保存整個項目中用到的全局變量,是響應式的。與之對應的是mapState函數(當一個組件需要獲取多個狀態時候,將這些狀態都聲明為計算屬性會有些重復和冗余。為了解決這個問題,我們可以使用?mapState?輔助函數幫助我們生成計算屬性)具體的用法可以參考官方文檔。Vuex

2、getters:?可以認為是 store 的計算屬性,getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變才會被重新計算。?mapGetters:?mapGetters 輔助函數僅僅是將 store 中的 getter 映射到局部計算屬性。

3、Mutation:?更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。Vuex 中的 mutation 非常類似于事件:每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是我們實際進行狀態更改的地方,并且它會接受 state 作為第一個參數。

注意:?1、Mutation 必須是同步函數;2、使用常量替代 Mutation 事件類型;3、Mutation 需遵守 Vue 的響應規則。

mapMutations?輔助函數將組件中的 methods 映射為 store.commit 調用(需要在根節點注入 store)。

4、Action:?Action 提交的是 mutation,而不是直接變更狀態。 Action 可以包含任意異步操作。Action 通過 store.dispatch 方法觸發。 在組件中分發 Action:this.$store.dispatch('xxx') 分發 action,或者使用?mapActions?輔助函數將組件的 methods 映射為 store.dispatch 調用(需要先在根節點注入 store)。

5、Module?Vuex 允許我們將 store 分割成模塊(module)。每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進行同樣方式的分割。

4、$attrs和$listeners

attrs:?包含了父作用域中不被 props 所識別 (且獲取) 的特性綁定?(class 和 style 除外)。當一個組件沒有聲明任何?props?時,這里會包含所有父作用域的綁定 (class?和?style?除外),并且可以通過?v-bind="$attrs"?傳入內部組件。通常配合?inheritAttrs?選項一起使用。?inheritAttrs為?true?時,表示在該組件上一html元素屬性顯示那些非?props?屬性,為?false?則不顯示。

listeners:?包含了父作用域中的 (不含?.native?修飾器的)?v-on?事件監聽器。它可以通過?v-on="$listeners"?傳入內部組件。

//parent<template><div><child1:user="userInfo"class="test":userName="userInfo.userName":age='userInfo.age'></child1><button@click="change">調用子組件方法</button></div></template><script>importchild1from"./child1"exportdefault{name:"parent",data(){return{userInfo:{userName:'james',age:27}}},components:{child1},methods:{getUserName(){returnthis.userInfo.userName},change(){this.$children[0].change()}},}</script>//child1<template><div><span>{{userInfo.userName}}</span><button@click="change">修改</button><button@click="getUserInfo">調用父組件的方法</button></div></template><script>exportdefault{name:"child1",props:{user:Object},data(){return{userInfo:this.user}},created(){console.log(this.$attrs)//{age: 27,userName: "james"}console.log(this.$listeners)//可以直接調用this.$listeners.click()},methods:{change(){this.userInfo.userName="userName改變了"},getUserInfo(){constuserName=this.$parent.getUserName()console.log(userName)}}}</script>

5、provide和inject

provide?選項應該是一個對象或返回一個對象的函數。該對象包含可注入其子孫的屬性。 子孫組件通過?inject?注入獲取到祖級和父級?provide?的數據。

注意:provide 和 inject 綁定并不是可響應的。這是刻意為之的。然而,如果你傳入了一個可監聽的對象,那么其對象的屬性還是可響應的。

provide與inject 實現數據響應式的兩種方式:

1、provide祖先組件的實例,然后在子孫組件中注入依賴,這樣就可以在子孫組件中直接修改祖先組件的實例的屬性,不過這種方法有個缺點就是這個實例上掛載很多沒有必要的東西比如?props,methods?2、使用2.6最新API?Vue.observable?優化響應式?provide?(推薦)

//parent.vue<template><div><child1:user="userInfo":userName="userInfo.userName"class="test":age='userInfo.age'></child1>{{userInfo}}</div></template><script>importchild1from"./child1"exportdefault{name:"parent",data(){return{userInfo:{userName:'james',age:27}}},provide(){return{user:this.userInfo}},watch:{'userInfo.userName':()=>{//在子組件中改變該屬性,也會被監聽到console.log("userName改變了")}},components:{child1},methods:{getUserName(){returnthis.userInfo.userName},},}</script>//child1.vue<template><div><span>{{user.userName}}</span><button@click="change">修改</button></div></template><script>exportdefault{name:"child1",inject:['user'],created(){console.log(this.user)},methods:{change(){this.user.userName="test"}}}</script>

6、$parent,$children與 ref

ref:如果在普通的?DOM?元素上使用,引用指向的就是?DOM?元素;如果用在子組件上,引用就指向組件實例。

parent / children:訪問父 / 子實例 這兩種都是直接得到組件實例,使用后可以直接調用組件的方法或訪問數據。這兩種方法的弊端是,無法在跨級或兄弟間通信。如果是封裝通用組件時?parent / children?這種通信手段就顯得不足了。

//parent<template><div><child1:user="userInfo"></child1><button@click="change">調用子組件方法</button></div></template><script>importchild1from"./child1"exportdefault{name:"parent",data(){return{userInfo:{userName:'james',}}},components:{child1},methods:{getUserName(){returnthis.userInfo.userName},change(){this.$children[0].change()}},}</script>//child<template><div><span>{{userInfo.userName}}</span><button@click="change">修改</button><button@click="getUserInfo">調用父組件的方法</button><!--<slotname='child1'childData="{test:'name'}"></slot> --></div></template><script>exportdefault{name:"child1",props:{user:Object},data(){return{userInfo:this.user}},methods:{change(){this.userInfo.userName="userName改變了"},getUserInfo(){//調用父組件方法constuserName=this.$parent.getUserName()console.log(userName)}}}</script>

element-ui在封裝通用表單通用組件時,并沒有在組件中直接通過?this.$parent、this.$children?來調用父/子組件的方法。為實現組件解耦,做了一層封裝。具體封裝封裝代碼如下。

exportdefault{methods:{dispatch(componentName,eventName,params){varparent=this.$parent||this.$root;varname=parent.$options.componentName;//父組件存在,父組件的componentName跟你傳進來的componentName要一致while(parent&&(!name||name!==componentName)){parent=parent.$parent;if(parent){name=parent.$options.componentName;}}if(parent){parent.$emit.apply(parent,[eventName].concat(params));}},broadcast(componentName,eventName,params){this.$children.forEach(child=>{varname=child.$options.componentName;if(name===componentName){child.$emit.apply(child,[eventName].concat(params));}else{//調用自身broadcast.apply(child,[componentName,eventName].concat([params]));}});}}};

7、eventBus

這種通信方式可以跨組件通訊,經常用于兄弟組件間通訊。這種通訊方式的實現是通過新建一個?vue?實例,然后在需要通信的組件間引入,通過?emit?方法觸發事件,通過?on?來監聽相應事件來實現通訊的功能。更詳細的請參考大神的介紹——vue組件間通信六種方式

//evenBus.jsimportVuefrom"vue"exportdefaultnewVue();

這是vue專題系列文章中的一篇。更多文章可以訪問我的個人博客個人博客github地址。如果覺得總結的還可以,大家也可以fork我的框架。希望大家star支持一下。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。