總結(jié)vue組件通信傳值的六種方式

1、props / $emit (這是最常用的一種父子組件之間傳遞數(shù)據(jù)的方式)

父組件通過綁定屬性來向子組件傳遞數(shù)據(jù),子組件通過 props 屬性來獲取對應(yīng)的數(shù)據(jù)切诀;子組件通過 $emit 事件向父組件發(fā)送消息,將自己的數(shù)據(jù)傳遞給父組件搔弄。

使用方式:

// 父組件compA

<template>

? ? <div>

? ? ? ? <compB

? ? ? ? :title="value"

? ? ? ? :moreBtn="moreBtn"

? ? ? ? @more="onMore"/>

? ? </div>


</template>

<script>

import compB from './compB'

export default{

? ? name: 'compA',

? ? components:{

? ? ? ? compB

? ? },

? ? data(){

? ? ? ? return {

? ? ? ? ? ? value: '',

? ? ? ? ? ? moreBtn: true

? ? ? ? }

? ? },

? ? method:{

? ? ? ? onMore(value){

? ? ? ? ? ? console.log('value', value)

? ? ? ? ? ? //點(diǎn)擊查看更多按鈕

? ? ? ? }

? ? }

}

</script>


//子組件compB

<template>

? ? <div>

? ? ? ? <div class="title">{{value}}</div>

? ? ? ? <div v-if="moreBtn" @click="handleMore">查看更多</div>

? ? </div>

</template>

<script>

export default{

? ? name: 'compB',

? ? data(){

? ? ? ? return {

? ? ? ? }

? ? },

? ? props: {

? ? ? ? title: {

? ? ? ? ? ? type: String,

? ? ? ? ? ? default: '標(biāo)題'

? ? ? ? },

? ? ? ? moreBtn: {

? ? ? ? ? ? type: Boolean,

? ? ? ? ? ? default: false

? ? ? ? }

? ? }

? ? method:{

? ? ? ? handleMore(){

? ? ? ? ? ? this.$emit('more', '點(diǎn)擊查看更多按鈕')

? ? ? ? }

? ? }

}

</script>

props使得父子之間形成一種單向數(shù)據(jù)流幅虑,父元素更新的時候,子元素的狀態(tài)也會隨之改變顾犹。但反之會導(dǎo)致你的應(yīng)用的數(shù)據(jù)流向難以理解倒庵。

注意在 JavaScript 中對象和數(shù)組是通過引用傳入的,所以對于一個數(shù)組或?qū)ο箢愋偷?prop 來說炫刷,在子組件中改變這個對象或數(shù)組本身將會影響到父組件的狀態(tài)擎宝。

如果通過prop傳遞的數(shù)據(jù)是數(shù)組或者是對象,則是可以改變父對象的值浑玛,但是不推薦绍申。如果傳遞的是個字符串,還要改變父組件的值顾彰,那么Vue 會在瀏覽器的控制臺中發(fā)出警告极阅。

在某些情況下,需要子組件改變父組件的值涨享,推薦使用(2.3.0+)版本新增的.sync語法糖筋搏。

使用方法:

// A.vue

<template>

? ? <add-modal

? ? ? v-if="modalVisiable"

? ? ? :visiable.sync='modalVisiable'

? ? ? @submit="saveForm"

? ? />

</template>

<script>

export default {

? ? name: 'A',

? ? data(){

? ? ? ? return {

? ? ? ? ? ? modalVisiable: false

? ? ? ? }

? ? }

}

</script>


// B.vue

<template>

? ? <Modal

? ? ? v-model="show"

? ? ? width="600"

? ? ? title="彈框"

? ? ? :loading='true'

? ? ? @on-cancel="$emit('update:visiable', false)"

? ? >

? ? </Modal>

</template>

<script>

export default {

? ? name: 'A',

? ? data(){

? ? ? ? return {

? ? ? ? ? ? show: false,

? ? ? ? }

? ? },

? ? created(){

? ? ? ? this.show = this.visiable

? ? },

? ? props: {

? ? ? ? visiable: {

? ? ? ? ? type: Boolean,

? ? ? ? ? default: false

? ? ? ? }

? ? },

}

</script>

通過this.$emit('update:visiable', false)來改變父元素的狀態(tài)

2、$emit/$on

eventBus事件總線

描述:

這個方法是通過創(chuàng)建一個空的 vue 實(shí)例厕隧,當(dāng)做 $emit 事件的處理中心(事件總線)奔脐,通過他來觸發(fā)以及監(jiān)聽事件俄周,方便的實(shí)現(xiàn)了任意組件間的通信,包含父子髓迎,兄弟峦朗,隔代組件。

以下圖為例竖般,我們要實(shí)現(xiàn)的是A組件和C組件向B組件傳輸數(shù)據(jù)甚垦。

使用方法:

?

? ?
我是父元素,我的num的值是:{{num}}
? ? ? ? ? ? ?
import Vue from'vue';import aComp from'./a.vue';import bComp from'./b.vue';import cComp from'./c.vue';// 創(chuàng)建一個空的vue實(shí)例const bus = new Vue();exportdefault {'name':'example',data() {return{? ? ? bus,'num': 1? ? };? },'components': {? ? aComp,? ? bComp,? ? cComp? }};<!--home.vue-->

<template>

? <div>

? ? <div>我是父元素涣雕,我的num的值是:{{num}}</div>

? ? <a-comp :event="bus" :num.sync='num'></a-comp>

? ? <c-comp :event="bus" :num='num'></c-comp>

? ? <b-comp :event="bus"></b-comp>

? </div>

</template>

<script>

import Vue from 'vue';

import aComp from './a.vue';

import bComp from './b.vue';

import cComp from './c.vue';

// 創(chuàng)建一個空的vue實(shí)例

const bus = new Vue();

export default {

? 'name': 'example',

? data() {

? ? return {

? ? ? bus,

? ? ? 'num': 1

? ? };

? },

? 'components': {

? ? aComp,

? ? bComp,

? ? cComp

? }

};

</script>

?

? ? 我是組件A艰亮,? ? 點(diǎn)擊我向B發(fā)送東西?
exportdefault {'name':'aComp','props': ['event','num'],data() {return{'nb': 0};? },created() {? ? this.nb = this.num;? },'methods': {send() {? ? ? this.nb = this.nb + 1;? ? ? this.$emit('update:num', this.nb);? ? ? // 通過$emit來觸發(fā)phone-a事件? ? ? this.event.$emit('phone-a','我是組件A啊', this.nb);? ? }? }};<!--a.vue-->

<template>

? <div class="a-content">

? ? 我是組件A,

? ? <el-button type="primary" @click="send">點(diǎn)擊我向B發(fā)送東西</el-button>

? </div>

</template>

<script>

export default {

? 'name': 'aComp',

? 'props': [

? ? 'event',

? ? 'num'

? ],

? data() {

? ? return {'nb': 0};

? },

? created() {

? ? this.nb = this.num;

? },

? 'methods': {

? ? send() {

? ? ? this.nb = this.nb + 1;

? ? ? this.$emit('update:num', this.nb);

? ? ? // 通過$emit 來觸發(fā)phone-a事件

? ? ? this.event.$emit('phone-a', '我是組件A啊', this.nb);

? ? }

? }

};

</script>

?

? ? 我是組件C挣郭,? ? 點(diǎn)擊我向B發(fā)送東西?
exportdefault {'name':'cComp','props': ['event','num'],data() {return{};? },'methods': {send() {? ? ? console.log(this.num);? ? ? this.event.$emit('phone-c', `我是組件C啊${this.num}`);? ? }? }};<!--c.vue-->

<template>

? <div>



? ? 我是組件C迄埃,

? ? <el-button type="primary" @click="send">點(diǎn)擊我向B發(fā)送東西</el-button>

? </div>

</template>

<script>

export default {

? 'name': 'cComp',

? 'props': [

? ? 'event',

? ? 'num'

? ],

? data() {

? ? return {};

? },

? 'methods': {

? ? send() {

? ? ? console.log(this.num);

? ? ? this.event.$emit('phone-c', `我是組件C啊${this.num}`);

? ? }

? }

};

</script>



?

? ?
我是組件B,我會接收組件A及組件C
? ?
? ? ? A: {{a}}? ?
? ?
? ? ? B: {{c}}? ?
?
exportdefault {'name':'bComp',data() {return{'a':'','c':''};? },'props': ['event'],mounted() {? ? // 通過$on來監(jiān)聽 phone-a兑障、phone-c 事件? ? this.event.$on('phone-a', (a, num) => {? ? ? this.a = `${a},我在改變父元素傳過來的值num:${num}`;? ? });? ? this.event.$on('phone-c', c => {? ? ? this.c = c;? ? });? }};<!--b.vue-->

<template>

? <div class="b-content">

? ? <div>我是組件B侄非,我會接收組件A及組件C</div>

? ? <div>

? ? ? A: {{a}}

? ? </div>

? ? <div>

? ? ? B: {{c}}

? ? </div>

? </div>

</template>

<script>

export default {

? 'name': 'bComp',

? data() {

? ? return {

? ? ? 'a': '',

? ? ? 'c': ''

? ? };

? },

? 'props': ['event'],

? mounted() {

? ? // 通過$on來監(jiān)聽 phone-a 、phone-c 事件

? ? this.event.$on('phone-a', (a, num) => {

? ? ? this.a = `${a},我在改變父元素傳過來的值num: ${num}`;

? ? });

? ? this.event.$on('phone-c', c => {

? ? ? this.c = c;

? ? });

? }

};

</script>

其中最重要的是他們必須要在公共的實(shí)例中才能實(shí)現(xiàn)數(shù)據(jù)的互相傳遞流译。

3逞怨、provide / inject

描述:

這對選項(xiàng)需要一起使用,父組件使用 provide 向下提供數(shù)據(jù)福澡,其下所有子組件都可以通過inject注入叠赦。不管中間隔了多少代,都可以注入多個來自不同父級提供的數(shù)據(jù)

provide 選項(xiàng)是一個對象或返回一個對象的函數(shù)革砸。該對象包含可注入其子孫的屬性

inject 選項(xiàng)是一個字符串?dāng)?shù)組除秀,或一個對象

使用方法:

// 父組件

<template>

? <div>

? ? <com-a></com-a>

? </div>

</template>

<script>

import ComA from './a';

export default {

? 'name': 'home',

? 'components': {ComA},

? provide() {

? ? return {

? ? ? 'a': 'Hello',

? ? ? 'show': val => !val

? ? };

? }

};

</script>


// 子組件

<template>

? <div>

? ? <el-button @click="showFn(textShow)">點(diǎn)擊我切換下面文字展示及隱藏</el-button>

? ? <div v-if="textShow">我是一段隨意被操控的文字{{a}}</div>

? </div>

</template>

<script>

export default {

? 'name': 'ComA',

? data() {

? ? return {'textShow': true};

? },

? 'inject': [

? ? 'a',

? ? 'show'

? ],

? 'methods': {

? ? showFn(val) {

? ? ? this.textShow = this.show(val);

? ? }

? }

};

</script>

如果是在app.vue 文件(根文件)里面全局注冊信息,就可以在整個路由里面去引用

(類似于全局的數(shù)據(jù)管理vuex)

? ?

? ? ? ? ? ?
exportdefault {? ? name:'app',provide(){return{? ? ? ? ? ? app: this? ? ? ? }? ? }}<template>

? ? <div>

? ? ? ? <router-view></router-view>

? ? </div>

</template>

<script>

export default {

? ? name: 'app',

? ? provide(){

? ? ? ? return {

? ? ? ? ? ? app: this

? ? ? ? }

? ? }

}

</script>

接下來任何組件只要通過inject注入app的話算利,都可以直接通過this.app.xx 來訪問app.vue 的所有實(shí)例册踩。

4、$parent / $children

描述:

$parent 可以用來從一個子組件訪問父組件的實(shí)例效拭。它提供了一種機(jī)會暂吉,可以在后期隨時觸達(dá)父級組件,來替代將數(shù)據(jù)以 prop 的方式傳入子組件的方式

?

? ?
我是子組件
? ? {{msgText}}?
exportdefault {'name':'childComp',data() {return{'msgText':'','childMsg':'來自子元素的吶喊'};? },created() {? ? this.msgText = this.$parent.parentMsg;? ? // MsgText: 來自父組件的呵呵? }};<template>

? <div class="b-content">

? ? <div>我是子組件</div>

? ? <span>{{msgText}}</span>

? </div>

</template>

<script>

export default {

? 'name': 'childComp',

? data() {

? ? return {

? ? ? 'msgText': '',

? ? ? 'childMsg': '來自子元素的吶喊'

? ? };

? },

? created() {

? ? this.msgText = this.$parent.parentMsg;

? ? // MsgText: 來自父組件的呵呵

? }

};

</script>

$children可以遍歷全部子組件允耿,需要注意 $children 并不保證順序借笙,也不是響應(yīng)式的。

?

? ?
我是父組件
? ? ?
import childComp from'./child';exportdefault {'name':'parentComp',data() {return{'parentMsg':'來自父組件的呵呵'};? },'components': {childComp},mounted() {? ? // 讀取子組件數(shù)據(jù)较锡,注意$children子組件的排序是不安全的? ? console.log(this.$children[0].childMsg);? ? // 來自子元素的吶喊? }};<template>

? <div class="b-content">

? ? <div>我是父組件</div>

? ? <child-comp></child-comp>

? </div>

</template>

<script>

import childComp from './child';

export default {

? 'name': 'parentComp',

? data() {

? ? return {'parentMsg': '來自父組件的呵呵'};

? },

? 'components': {childComp},

? mounted() {

? ? // 讀取子組件數(shù)據(jù),注意$children子組件的排序是不安全的

? ? console.log(this.$children[0].childMsg);

? ? // 來自子元素的吶喊

? }

};

</script>

5盗痒、$root & refs

描述:

$root 屬性是在每個 new Vue 實(shí)例的子組件中蚂蕴,其根實(shí)例可以通過 $root 屬性進(jìn)行訪問低散。例如,在這個根實(shí)例中:

// Vue 根實(shí)例

new Vue({

? data: {

? ? foo: 1

? },

? computed: {

? ? bar: function () { /* ... */ }

? },

? methods: {

? ? baz: function () { /* ... */ }

? }

})

所有的子組件都可以將這個實(shí)例作為一個全局 store 來訪問或使用骡楼。

// 獲取根組件的數(shù)據(jù)

this.$root.foo

// 寫入根組件的數(shù)據(jù)

this.$root.foo = 2

// 訪問根組件的計(jì)算屬性

this.$root.bar

// 調(diào)用根組件的方法

this.$root.baz()


$refs屬性當(dāng)你需要在 JavaScript 里直接訪問一個子組件熔号。你可以通過 ref 這個 attribute 為子組件賦予一個 ID 引用。 一個對象鸟整,持有注冊過 ref 特性 的所有 DOM 元素和組件實(shí)例引镊。例如:

<my-component ref="childrenCompA"></my-component>

訪問子組件:this.$refs.childrenCompA

6、Vuex

在做中大型的單頁應(yīng)用的時候篮条,例如需要多人協(xié)作開發(fā)弟头,全局維護(hù)登錄狀態(tài)等,我們可以選擇vuex來進(jìn)行狀態(tài)管理涉茧。

state里面是存儲在vuex里的數(shù)據(jù)赴恨,通過在根實(shí)例中注冊 store 選項(xiàng),該 store 實(shí)例會注入到根組件下的所有子組件中伴栓,且子組件能通過 this.$store 訪問到伦连。

當(dāng)需要獲取多個數(shù)據(jù)時,可以通過下面這種方式:

import {mapState} from'vuex'

computed:{? ?

...mapState('commissionSetting', ['listData'])

},



不是所有的狀態(tài)都適合放在vuex里面钳垮,有些狀態(tài)只屬于單個組件惑淳,所以還是要視情況來定。

mutations 更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation

const mutations = {?

????? updateListData(state, payload){? ?

????????? state.listData = payload?

?? ?? }

}

不能直接調(diào)用updateListData饺窿,需要以相應(yīng)的 type 調(diào)用 store.commit 方法:

store.commit('updateListData', data)復(fù)制代碼

actions 提交的是 mutation歧焦,而不是直接變更狀態(tài)。

Action 可以包含任意異步操作短荐。

寫一個簡單的action:

async getListAction({commit}, params){

? ? const result = await getList(params)

? ? const {code, message, data} = result

? ? if(code === SUCCESS && data){

? ? ? // 提交更改數(shù)據(jù)

? ? ? commit('updateListData', data.rows)

? ? }else{

? ? ? vueInit.$Message.error({

? ? ? ? content: message || '請您稍后再試~'

? ? ? });

? ? }

? ? return result

? },

Vuex 并不限制你的代碼結(jié)構(gòu)倚舀。但是,它規(guī)定了一些需要遵守的規(guī)則:

應(yīng)用層級的狀態(tài)應(yīng)該集中到單個 store 對象中忍宋。

提交 mutation 是更改狀態(tài)的唯一方法痕貌,并且這個過程是同步的。

異步邏輯都應(yīng)該封裝到 action 里面糠排。

只要你遵守以上規(guī)則舵稠,如何組織代碼隨你便。如果你的 store 文件太大入宦,只需將 action哺徊、mutation 和 getter 分割到單獨(dú)的文件。

作者:快狗打車前端團(tuán)隊(duì)

鏈接:https://juejin.im/post/5e5f7705f265da576f5310c7

來源:掘金

著作權(quán)歸作者所有乾闰。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)落追,非商業(yè)轉(zhuǎn)載請注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末涯肩,一起剝皮案震驚了整個濱河市轿钠,隨后出現(xiàn)的幾起案子巢钓,更是在濱河造成了極大的恐慌,老刑警劉巖疗垛,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件症汹,死亡現(xiàn)場離奇詭異,居然都是意外死亡贷腕,警方通過查閱死者的電腦和手機(jī)背镇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泽裳,“玉大人瞒斩,你說我怎么就攤上這事」畋冢” “怎么了济瓢?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長妹卿。 經(jīng)常有香客問我旺矾,道長,這世上最難降的妖魔是什么夺克? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任箕宙,我火速辦了婚禮,結(jié)果婚禮上铺纽,老公的妹妹穿的比我還像新娘柬帕。我一直安慰自己,他們只是感情好狡门,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布陷寝。 她就那樣靜靜地躺著,像睡著了一般其馏。 火紅的嫁衣襯著肌膚如雪凤跑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天叛复,我揣著相機(jī)與錄音仔引,去河邊找鬼。 笑死褐奥,一個胖子當(dāng)著我的面吹牛咖耘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播撬码,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼儿倒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了呜笑?” 一聲冷哼從身側(cè)響起义桂,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤找筝,失蹤者是張志新(化名)和其女友劉穎蹈垢,沒想到半個月后慷吊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡曹抬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年溉瓶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谤民。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡堰酿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出张足,到底是詐尸還是另有隱情触创,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布为牍,位于F島的核電站哼绑,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏碉咆。R本人自食惡果不足惜抖韩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望疫铜。 院中可真熱鬧茂浮,春花似錦、人聲如沸壳咕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谓厘。三九已至幌羞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間庞呕,已是汗流浹背新翎。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留住练,地道東北人地啰。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像讲逛,于是被迫代替她去往敵國和親亏吝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容