Vue組件通信

簡介

組件是Vue的核心,而組件間的狀態(tài)管理和數(shù)據(jù)傳遞是開發(fā)繞不開的問題坦仍。在Vue中湿颅,組件和組件之間是相互獨(dú)立的,所以需要一定的方法才能進(jìn)行Vue的組件間的通信床蜘。

基礎(chǔ)技能 props和$emit

如果你連這個(gè)都不會(huì)用的話就一定要好好看一下這篇文章和官方文檔

結(jié)合場景分析:現(xiàn)在我們有一個(gè)獲取客戶列表的功能辙培,并且要展示客戶的信息。
那么我們先來開發(fā)一個(gè)customer-item的客戶詳情組件

<template>
<!-- 客戶信息組件 -->
  <div>
    <input type="text" v-model="selfName" /> <button @click="rename">Rename</button>{{name}}
  </div>
</template>

<script>
export default {
  props: {
    name: String,
    index: Number
  },
  data() {
    return {
      selfName: ''
    }
  },
  mounted() {
    this.selfName = this.name
  },
  methods: {
    rename() {
      this.$emit('rename', this.index, this.selfName)
    }
  },
}
</script>


customer可以通過props來接收父組件向下通過屬性傳播的值邢锯,這就是父向子的一個(gè)通信扬蕊。而子組件如果想要更新父組件的狀態(tài),是不能直接去向上修改狀態(tài)的丹擎。這時(shí)候就需要通過事件來完成這個(gè)操作尾抑,也就是通過$emit來觸發(fā)父組件定義的一個(gè)事件(在上面代碼中就是觸發(fā)父組件的rename事件),同時(shí)可以將要修改的數(shù)據(jù)當(dāng)作參數(shù)傳遞蒂培,即可在父組件中完成狀態(tài)更新再愈。

進(jìn)階技能1 $attrs$listeners

首先,這兩個(gè)是Vue 2.4.0 版本新出的屬性护戳,所以如果沒有接觸過的話就需要好好補(bǔ)充一下自己的技能包了翎冲。

這兩個(gè)屬性可以說是 props和$emit 的一個(gè)補(bǔ)充增強(qiáng)。按照我們之前的寫法媳荒,父組件像自組件傳遞參數(shù)其實(shí)是按照屬性的形式向下傳遞抗悍。那么其實(shí)對(duì)于自組件而言,我們可以無需關(guān)心父組件具體向我們傳遞了哪些東西钳枕,我們能夠拿到這些屬性值即可缴渊。

第二個(gè)cutomer組件:

<!--父組件-->
<Customer v-for="(item,index) in customers" :key="item.name" :index="index" @add="addOrder" :name="item.name" :order="item.order"/> 

<!--子組件-->
<template>
  <div>
    <div>姓名: {{$attrs.name}}</div>
    <div>訂單數(shù): {{$attrs.order}}<button @click="add">增加</button></div> 
  </div>
</template>
<script>
export default {
  data() {return {}},
  methods: {
    add() {
      this.$listeners.add(this.$attrs.index)
    }
  },
}
</script>

這樣,自組件就通用了很多么伯,不需要在props去聲明那么多的屬性疟暖,而觸發(fā)方法的emit也可以通過$listeners來引用父組件中綁定在自組件的事件卡儒,即可完成父子組件的通信工作田柔。而這兩個(gè)屬性 最便捷的作用還是可以通過v-bind="$attrs"來完成$attrs的一個(gè)屬性傳遞和v-on="$listeners"的一個(gè)事件傳遞來實(shí)現(xiàn)子組件與祖先組件的一個(gè)通信。如上述的代碼骨望,我可以改造為:

<!--Customer組件-->
<template>
  <div>
    <div>姓名: {{$attrs.name}}</div>
    <Order v-bind="$attrs" v-on="$listeners"/>
  </div>
</template>
<script>
import Order from './Order.vue'
export default {
  data() {return {}},
  components: {
    Order
  }
}
</script>
<!--Order組件-->
<template>
  <div>
    訂單數(shù): {{$attrs.order}}<button @click="add">增加</button>
  </div>
</template>
<script>
export default {
  methods: {
    add() {
      this.$listeners.add(this.$attrs.index)
    }
  },
}
</script>

那么硬爆,在設(shè)計(jì)一些較為復(fù)雜的組件時(shí),使用$attrs$listeners要比props+$emit的組合要好用太多了

Tips: 在使用$attrs來進(jìn)行向下的屬性傳遞時(shí)擎鸠,會(huì)默認(rèn)將這些屬性附加到自組件上缀磕,如圖

inheritAttrs.png

這種行為是默認(rèn)的,如果不希望默認(rèn)這種行為,則可以通過設(shè)置inheritAttrs為false來阻止這個(gè)默認(rèn)行為:


inheritAttrs2.png

進(jìn)階技能2 eventBus($emit, $on

上面的兩種方法都是均常用于長輩組件和晚輩組件的數(shù)據(jù)通信袜蚕,而對(duì)于兄弟組件而言糟把,要想通過上述的方法實(shí)現(xiàn)效果,就需要父組件做一個(gè)中轉(zhuǎn)站(父組件用來管理狀態(tài)牲剃,A組件修改狀態(tài)遣疯,通過事件通知父組件,父組件再修改狀態(tài)來達(dá)到修改B組件狀態(tài)的效果)凿傅,這無疑是一個(gè)沒必要的開銷缠犀,而且如果兄弟組件多的時(shí)候,父組件中的狀態(tài)會(huì)非常的冗余聪舒。

eventBus可以作為一個(gè)事件的轉(zhuǎn)發(fā)中心辨液,對(duì)于組件而言,均可以注冊(cè)eventBus箱残,而某個(gè)組件觸發(fā)事件時(shí)滔迈,注冊(cè)了這個(gè)事件的組件均會(huì)觸發(fā)事件并且執(zhí)行對(duì)應(yīng)的方法。這個(gè)過程可以通過下面這個(gè)圖理解:

eventBus.png

那么其實(shí)在eventBus中被辑,我們不再關(guān)心組件和組件間具體是父子還是兄弟還是祖先的關(guān)系亡鼠,而是將重點(diǎn)放在通過事件來進(jìn)行組件間的交互。當(dāng)然敷待,eventBus還是最常用語兄弟組件或者跨級(jí)組件這種場景间涵。

// 先創(chuàng)建一個(gè)eventBus,Vue的實(shí)例對(duì)象就是一個(gè)天然的eventBus

import Vue from 'vue'
const eventVue = new Vue()
export default eventVue

// 組件A
<script>
import eventBus from './eventBus'
export default {
  data() {
    return {
      msg: ''
    }
  },
  methods: {
    sendMsg() {
    // 觸發(fā)事件
      eventBus.$emit('sendMsg', this.msg)
    }
  },
}
</script>

// 組件B
<script>
import eventBus from './eventBus'
export default {
  data() {
    return {
      msg: ''
    }
  },
  mounted() {
    // 注冊(cè)事件
    eventBus.$on('sendMsg', val => {
      this.msg = val
    })
  }
}
</script>

進(jìn)階技能3 provide&inject

掌握上面的三個(gè)技能榜揖,在大多數(shù)的組件交互中已經(jīng)夠用了勾哩。不過在組件給其他組件傳遞狀態(tài)時(shí),不管是使用props還是$attrs都是有一些繁瑣举哟。在Vue 2.2 的時(shí)候新增的provide思劳,inject屬性就很適合這種數(shù)據(jù)傳遞的場景。

僅從字面意思上來理解這個(gè)東西妨猩,組件A提供幾個(gè)狀態(tài)潜叛,組件B注入這些狀態(tài)。不過僅在使用provide&inject是沒有辦法實(shí)現(xiàn)數(shù)據(jù)的響應(yīng)式的壶硅。最基本的用法如下所示:

語法:
provide:Object | () => Object
inject:Array<string> | { [key: string]: string | Symbol | Object }


// 父組件
<script>
import Customer from '../components/provide/Customer'
export default {
  data() {
    return {
      info: {
        name: 'wyh',
        order: 18
      }
    }
  },
  components: {
    Customer
  },
  provide() {
    return {
      name: this.info.name,
      order: this.info.order
    }
  }
}
</script>

// 子組件
<template>
  <div>
    姓名: {{name}}
    <Order />
  </div>
</template>
export default {
  inject: ['name']
}

// 孫組件
export default {
  inject: ['order']
}

效果:


provide1.png

inject注入狀態(tài)的寫法一種是上面的字符串?dāng)?shù)組形式的威兜。
另一種是對(duì)象形式。如果你對(duì)當(dāng)前組件中注入的這個(gè)狀態(tài)有更多修飾時(shí)使用庐椒,比如重命名等椒舵,代碼如下:

// 重命名
inject: {
  selfOrder: 'order'
}
// 設(shè)置默認(rèn)值
inject: {
 order: {
    from: 'order',
    default: 17
  }
}

provide和inject不是響應(yīng)式的,不過如果傳入的是一個(gè)可以監(jiān)聽的對(duì)象约谈,那么其屬性就也可以實(shí)現(xiàn)成響應(yīng)式笔宿。這里要使用Vue提供的靜態(tài)方法Vue.observable(2.6版本后可用)代碼如下:

// 父組件
provide() {
    // 生成響應(yīng)式對(duì)象
    this.data = Vue.observable(this.info)
    return {
      name: this.info.name,
      order: this.info.order,
      data: this.data
    }
  }

// 子組件
<template>
  <div>
    姓名: {{name}} <br />
    響應(yīng)式的姓名: {{data.name}}
    <Order />
  </div>
</template>

<script>
import Order from './Order'
export default {
  inject: ['name', 'data'],
  components: {
    Order
  }
}
</script>

效果:


observable.gif

最終技能 Vuex

Vuex的內(nèi)容就太多了犁钟。不如放幾個(gè)思考題。

  1. Vuex如何進(jìn)行狀態(tài)管理泼橘,它的map工具是否會(huì)使用
  2. Vuex是如何完成狀態(tài)更新的
  3. mutation和action的區(qū)別涝动,為何mutation一定要是同步的
  4. Vuex模塊化方案
  5. 能否對(duì)比一下redux

還有個(gè)黑科技 $ref $parent $children

由于Vue可以通過上面的三個(gè)屬性來獲取父組件,子組件的實(shí)例炬灭,也就可以直接去進(jìn)行一些組件間的交互了捧存。比如我修改一下props demo中的代碼:

// 修改名稱代碼黑科技
rename() {
      // this.$emit('rename', this.index, this.selfName)
      this.$parent.$set(this.$parent.$data.names, this.index, this.selfName)
    }

依然是可以進(jìn)行組件間的數(shù)據(jù)交互的。不過黑科技就是黑科技担败,還是不推薦使用昔穴,除非上面的幾種形式均不滿足要求的時(shí)候。

麻煩點(diǎn)個(gè)start咯 github地址 這個(gè)代碼后續(xù)會(huì)完善Vuex的一些學(xué)習(xí)筆記

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末提前,一起剝皮案震驚了整個(gè)濱河市吗货,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌狈网,老刑警劉巖宙搬,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異拓哺,居然都是意外死亡勇垛,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門士鸥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闲孤,“玉大人,你說我怎么就攤上這事烤礁∷匣” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵脚仔,是天一觀的道長勤众。 經(jīng)常有香客問我,道長鲤脏,這世上最難降的妖魔是什么们颜? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮猎醇,結(jié)果婚禮上窥突,老公的妹妹穿的比我還像新娘。我一直安慰自己姑食,他們只是感情好波岛,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著音半,像睡著了一般则拷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上曹鸠,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天煌茬,我揣著相機(jī)與錄音,去河邊找鬼彻桃。 笑死坛善,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的邻眷。 我是一名探鬼主播眠屎,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼肆饶!你這毒婦竟也來了改衩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤驯镊,失蹤者是張志新(化名)和其女友劉穎葫督,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體板惑,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡橄镜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了冯乘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洽胶。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖裆馒,靈堂內(nèi)的尸體忽然破棺而出妖异,到底是詐尸還是另有隱情,我是刑警寧澤领追,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布他膳,位于F島的核電站,受9級(jí)特大地震影響绒窑,放射性物質(zhì)發(fā)生泄漏棕孙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一些膨、第九天 我趴在偏房一處隱蔽的房頂上張望蟀俊。 院中可真熱鬧,春花似錦订雾、人聲如沸肢预。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烫映。三九已至沼本,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锭沟,已是汗流浹背抽兆。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留族淮,地道東北人辫红。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像祝辣,于是被迫代替她去往敵國和親贴妻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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

  • 對(duì)于vue來說蝙斜,組件之間的消息傳遞是非常重要的名惩,下面是我對(duì)組件之間消息傳遞的各種方式的總結(jié),總共有8種方式乍炉。 1....
    edc余悸閱讀 350評(píng)論 0 3
  • 組件作為Vue中的核心概念绢片,是值得我們深入研究的課題之一,通過研究它岛琼,我們可以理解更高深的思想底循,可以提升自己的開發(fā)...
    北辰_狼月閱讀 754評(píng)論 2 7
  • 父子組件通信 1、父子組件通過prop傳遞數(shù)據(jù) 父組件可以將一條數(shù)據(jù)傳遞給子組件槐瑞,這條數(shù)據(jù)可以是動(dòng)態(tài)的熙涤,父組件的數(shù)...
    視覺派Pie閱讀 1,255評(píng)論 0 18
  • 前言 組件是 vue.js最強(qiáng)大的功能之一,而組件實(shí)例的作用域是相互獨(dú)立的困檩,這就意味著不同組件之間的數(shù)據(jù)無法相互引...
    用技術(shù)改變世界閱讀 2,152評(píng)論 1 3
  • 摘要: 總有一款合適的通信方式祠挫。 作者:浪里行舟 Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所有悼沿。 前言 組件是 v...
    Fundebug閱讀 15,571評(píng)論 3 57