簡介
組件是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)將這些屬性附加到自組件上缀磕,如圖
這種行為是默認(rèn)的,如果不希望默認(rèn)這種行為,則可以通過設(shè)置inheritAttrs為false來阻止這個(gè)默認(rèn)行為:
進(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è)圖理解:
那么其實(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']
}
效果:
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>
效果:
最終技能 Vuex
Vuex的內(nèi)容就太多了犁钟。不如放幾個(gè)思考題。
- Vuex如何進(jìn)行狀態(tài)管理泼橘,它的map工具是否會(huì)使用
- Vuex是如何完成狀態(tài)更新的
- mutation和action的區(qū)別涝动,為何mutation一定要是同步的
- Vuex模塊化方案
- 能否對(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í)筆記