組件通信
父子組件通信
父傳子 props屬性
子傳父 $emit事件
這兩種官方文檔里有很詳細(xì)的介紹就不解釋了
還是舉個(gè)栗子:
//parent.vue父組件
<template>
<div>
parent: {{money}}
<Son1 v-model="money"></Son1>
<!-- <Son1 :value.sync = "money"></Son1> -->
//兩種寫法都可以截碴,是語法躺捌显;.sync表示同步欢摄,如果只傳value 用v-model即可
</div>
</template>
<script>
import Son1 from './Son1'
export default {
components:{
Son1
},
data(){
return{
money:100
}
}
}
</script>
//子組件SON
//單項(xiàng)數(shù)據(jù)流廊移,父給子綁定一個(gè)事件
<template>
<div>
son:{{value}}
<button @click="change">點(diǎn)擊</button>
</div>
</template>
<script>
export default {
methods:{
change(){
this.$emit('input', this.value+100)
}
},
props:{
value:{
type:Number,
default:1
}
},
data(){
return{
}
}
}
</script>
多層級傳遞數(shù)據(jù)
$dispatch 和 $broadcast (Vue1中可以用來實(shí)現(xiàn)基于組件樹結(jié)構(gòu)的事件流通信扭粱,vue2中已經(jīng)被移除)
(父要傳到孫舵鳞;孫要傳到父;)
dispatch 是一個(gè)事件琢蛤,首先會在自己實(shí)例本身上觸發(fā)蜓堕,然后沿父鏈向上傳播抛虏。
broadcast 是一個(gè)事件,它向下傳播到當(dāng)前實(shí)例的所有后代套才。由于后代擴(kuò)展為多個(gè)子樹迂猴,事件傳播將會遵循許多不同的“路徑”。 除非回調(diào)返回 true背伴,否則在沿該路徑觸發(fā)偵聽器回調(diào)時(shí)沸毁,每個(gè)路徑的傳播將會停止。
//grandoon.vue組件
<template>
<div>
grandson:{{value}}
<button @click="changeParent">修改parent</button>
</div>
</template>
<script>
export default {
methods:{
changeParent(){
// this.$parent.$emit('input',300)
// this.$parent.$parent.$emit('input',300)
this.$dispatch('input',300)
}
},
props:{
value:{
type:Number
}
}
}
</script>
//dispatch實(shí)現(xiàn)
/**
* Recursively propagate an event up the parent chain.
* 遞歸地在父鏈上傳播事件挂据。
* @param {String} event
* @param {...*} additional arguments
*/
// $dispatch 方法是定義在 Vue 的 prototype 上的
// 接受一個(gè)字符串類型的事件名稱
Vue.prototype.$dispatch = function (event) {
// 首先執(zhí)行 $emit 觸發(fā)事件,將返回值保存在 shouldPropagate 中
var shouldPropagate = this.$emit.apply(this, arguments)
// 如果首次執(zhí)行的 $emit 方法返回的值不是 true 就直接返回
// 如果返回值不是 true 就說明組件邏輯不希望事件繼續(xù)往父組件進(jìn)行傳遞
if (!shouldPropagate) return
// 如果首次執(zhí)行 $emit 方法返回值是 true 就獲取當(dāng)前組件的 parent 組件實(shí)例
var parent = this.$parent
// 將函數(shù)接受的參數(shù)轉(zhuǎn)換成數(shù)組
var args = toArray(arguments)
// use object event to indicate non-source emit on parents
// 根據(jù)傳入的事件名稱的參數(shù)組裝成 object
args[0] = { name: event, source: this }
// 循環(huán)知道組件的父組件
while (parent) {
// 在父組件中執(zhí)行 $emit 觸發(fā)事件
shouldPropagate = parent.$emit.apply(parent, args)
// 如果父組件 $emit 返回的是 true 就繼續(xù)遞歸祖父組件儿普,否則就停止循環(huán)
parent = shouldPropagate ? parent.$parent : null
}
// 最后返回當(dāng)前組件實(shí)例
return this
}
broadcast 實(shí)現(xiàn)
Vue.prototype.$broadcast = function (event) {
// 獲取傳入事件的類型崎逃,判斷是否為字符串
var isSource = typeof event === 'string'
// 校正 event 的值,當(dāng)接受 event 的類型為字符串時(shí)就直接使用眉孩,如果不是字符串就使用 event 上的 name 屬性
event = isSource ? event : event.name
// if no child has registered for this event,
// then there's no need to broadcast.
// 如果當(dāng)前組件的子組件沒有注冊該事件个绍,就直接返回,并不用 broadcast
if (!this._eventsCount[event]) return
// 獲取當(dāng)前組件的子組件
var children = this.$children
// 將函數(shù)接受的參數(shù)轉(zhuǎn)換成數(shù)組
var args = toArray(arguments)
// 如果傳入事件為字符串
if (isSource) {
// use object event to indicate non-source emit
// on children
// 根據(jù)傳入的事件名稱的參數(shù)組裝成 object
args[0] = { name: event, source: this }
}
組件傳值浪汪,尤其是祖孫組件有跨度的傳值巴柿。
現(xiàn)在我們來討論一種情況,A組件與C組件怎么通信死遭,我們有多少種解決方案广恢?
- 我們使用VueX來進(jìn)行數(shù)據(jù)管理,但是如果項(xiàng)目中多個(gè)組件共享狀態(tài)比較少呀潭,項(xiàng)目比較小钉迷,并且全局狀態(tài)比較少,那使用VueX來實(shí)現(xiàn)該功能,并沒有發(fā)揮出VueX的威力钠署。
- 使用B來做中轉(zhuǎn)站糠聪,當(dāng)A組件需要把信息傳給C組件時(shí),B接受A組件的信息谐鼎,然后利用屬性傳給C組件舰蟆,這是一種解決方案,但是如果嵌套的組件過多狸棍,會導(dǎo)致代碼繁瑣身害,代碼維護(hù)比較困難;如果C中狀態(tài)的改變需要傳遞給A, 使用事件系統(tǒng)一級級往上傳遞 。
- 自定義一個(gè)Vue 中央數(shù)據(jù)總線草戈,這個(gè)情況適合碰到組件跨級傳遞消息题造,但是使用VueX感覺又有點(diǎn)浪費(fèi)的項(xiàng)目中,但是缺點(diǎn)是猾瘸,碰到多人合作時(shí)界赔,代碼的維護(hù)性較低丢习,代碼可讀性低
listeners
在vue2.4中,為了解決該需求淮悼,引入了listeners 咐低, 新增了inheritAttrs 選項(xiàng)。
$attrs (屬性集合)
$listeners (方法集合)
v-bind="$attrs", v-on="$listeners"
$attrs包含了父作用域中不作為 prop 被識別 (且獲取) 的特性綁定 (class 和 style 除外)
provide inject
1.provide就相當(dāng)于加強(qiáng)版父組件prop
2.inject就相當(dāng)于加強(qiáng)版子組件的props
因?yàn)橐陨蟽烧呖梢栽诟附M件與子組件袜腥、孫子組件见擦、曾孫子...組件數(shù)據(jù)交互,也就是說不僅限于prop的父子組件數(shù)據(jù)交互羹令,只要在上一層級的聲明的provide鲤屡,那么下一層級無論多深都能夠通過inject來訪問到provide的數(shù)據(jù)
//父組件
<template>
<div class="test">
<son prop="data"></son>
</div>
</template>
<script>
export default {
name: 'Test',
provide: {
name: 'Garrett'
}
//孫組件 (父組件--子組件--根組件)
<template>
<div>
{{name}}
</div>
</template>
<script>
export default {
name: 'Grandson',
inject: [name]
}
</script>
缺點(diǎn):
這么做也是有明顯的缺點(diǎn)的,在任意層級都能訪問導(dǎo)致數(shù)據(jù)追蹤比較困難福侈,不知道是哪一個(gè)層級聲明了這個(gè)或者不知道哪一層級或若干個(gè)層級使用了酒来,因此這個(gè)屬性通常并不建議使用能用vuex的使用vuex,不能用的多傳參幾層肪凛,但是在做組件庫開發(fā)時(shí)堰汉,不對vuex進(jìn)行依賴,且不知道用戶使用環(huán)境的情況下可以很好的使用
event bus
實(shí)現(xiàn)途徑是在要相互通信的兄弟組件之中伟墙,都引入一個(gè)新的vue實(shí)例翘鸭,然后通過分別調(diào)用這個(gè)實(shí)例的事件觸發(fā)和監(jiān)聽來實(shí)現(xiàn)通信和參數(shù)傳遞。
有了eventbus后
發(fā)送組件中
EventBus.$emit("hello", this.number);
接受組件中
EventBus.$on("hello", (number) = > { console.log(number) });
注意
- $bus.on應(yīng)該在created鉤子內(nèi)使用戳葵,如果在mounted使用就乓,有可能接收不到其他組件來自created鉤子內(nèi)發(fā)出的事件。
- 使用了bus.off解除档址,因?yàn)榻M件銷毀后,沒有必要把監(jiān)聽句柄存儲在vue-bus里了邻梆。