概述
上次研究了父子組件和有層次關(guān)系的組件之間是如何通信的,那么任意兩個(gè)組件之間是如何通信的呢虑鼎?
Demo
Demo 結(jié)構(gòu)如下圖所示:
之后需要使 Son2
組件和 Son3
組件之間更新一個(gè)數(shù)據(jù)辱匿,所以需要構(gòu)造一個(gè)數(shù)據(jù) money
键痛,這個(gè) money
變量作為全局變量,需要傳給所有的組件匾七,將 money
傳給所有組件并在線運(yùn)行
現(xiàn)在可以在 Son2
組件和 Son3
組件之間更新數(shù)據(jù) money
了絮短。當(dāng) Son2
組件更改 money
數(shù)據(jù)時(shí),如何通知其他組件呢昨忆?例如 Son3
組件丁频?
如果 Son2
組件更改了全局變量 money
之后再 setState
組件 Son2
是可以進(jìn)行數(shù)據(jù)的更新的,但是其他組件都接收不到更新邑贴,因?yàn)槠渌M件都沒有進(jìn)行 setState
所以當(dāng)組件 Son2
要進(jìn)行更新時(shí)席里,需要引入一個(gè) event center role
,Son2
組件將更新告知 event center role
拢驾,event center role
將通知其他組件奖磁,例如 Son3
組件
發(fā)布訂閱模式
通過上述的 demo 了解到需要一個(gè) event center role
,這個(gè) event center role
便是 發(fā)布訂閱模式( EventHub
|| EventBus
)繁疤。以下是實(shí)現(xiàn) EventHub
的代碼
let events = {};
let eventHub = {
emit(eventName, data) {
if(!events[eventName]) {
return;
}
events[eventName].forEach(callback => {
callback.call(null, data);
})
},
on(eventName, callback) {
if(!events[eventName]) {
events[eventName] = []
}
events[eventName].push(callback);
}
}
通過上述 EventHub
代碼署穗,便可以進(jìn)行組件之間的通信了
當(dāng)
Son2
組件需要更新數(shù)據(jù)時(shí),僅僅需要發(fā)布(emit
)一個(gè)事件(eventName
)嵌洼,之后將會(huì)執(zhí)行已經(jīng)訂閱(on
)了這個(gè)事件(eventName
)的組件的回調(diào)函數(shù)(callback
),并且將一些必要的數(shù)據(jù)傳遞給回調(diào)函數(shù)(callback
)封恰,僅僅需要Son3
組件訂閱(on
)這個(gè)事件(eventName
)即可實(shí)現(xiàn)組件之間的通信demo 加入
EventHub
并在線運(yùn)行麻养,現(xiàn)在Son2
組件可以和Son3
組件通信了,那如果其他所有組件都要知道更新呢诺舔?是不是需要在每個(gè)組件上都要訂閱(on
)這個(gè)事件(eventName
)呢鳖昌?怎么可能讓每個(gè)組件上都要訂閱(
on
)這個(gè)事件(eventName
)呢,所以應(yīng)該是其他的問題最終發(fā)現(xiàn)是因?yàn)榻M件
Son2
私自更改了數(shù)據(jù)低飒,然后去發(fā)布(emit
)事件(eventName
)许昨。所以這個(gè)數(shù)據(jù)組件是不可以進(jìn)行更改的,那么組件如何進(jìn)行更新數(shù)據(jù)呢褥赊?此時(shí)需要另一個(gè)角色
steward role
來解決這個(gè)問題糕档,組件不可以存儲(chǔ)數(shù)據(jù),所有數(shù)據(jù)通過props
來傳遞給各個(gè)組件拌喉,當(dāng)要更新數(shù)據(jù)時(shí)速那,組件可以去發(fā)布(emit
)一個(gè)事件(eventName
)來告知steward role
我要進(jìn)行更新數(shù)據(jù),那么steward role
應(yīng)該如何去操作呢尿背?-
steward role
有一個(gè)初始化方法端仰,這個(gè)初始化方法主要是訂閱(on
)一個(gè)事件(eventName
),如果有組件觸發(fā)事件田藐,它則拿到組件傳給它的data
并去更新數(shù)據(jù)荔烧,更新數(shù)據(jù)之后重新render
整個(gè)App
吱七,demo代碼并在線運(yùn)行let steward = { init() { eventHub.on('I want to consume', (data) => { money.amount -= data; render(); }) } } steward.init();
此時(shí)如果組件
Son2
想要更新數(shù)據(jù),那么所有的組件都會(huì)進(jìn)行更新鹤竭,并且重新render()
的時(shí)候只有數(shù)據(jù)會(huì)更新踊餐,其他的部分不會(huì)更新,這就是 React 的DOM diff
诺擅,它只會(huì)更新需要更新的地方
單項(xiàng)數(shù)據(jù)流
根據(jù)上述 demo 了解到現(xiàn)在進(jìn)行數(shù)據(jù)更新的方法是:首先有一個(gè) event center role
市袖,它主要負(fù)責(zé)訂閱( on
)、發(fā)布( emit
)事件烁涌,其次需要一個(gè) steward role
苍碟,它會(huì)在開始的時(shí)候去訂閱( on
)事件,只要觸發(fā)這個(gè)事件撮执,那么 steward role
則去更新數(shù)據(jù)并且重新 render
整個(gè) App
微峰,那么當(dāng)組件發(fā)布( emit
)這個(gè)事件( eventName
)后,steward role
將會(huì)處理這個(gè)事件抒钱。此時(shí)數(shù)據(jù)流便是單項(xiàng)數(shù)據(jù)流
特點(diǎn):
- 所有的動(dòng)作都是通過事件來溝通
- 數(shù)據(jù)必須放置在頂層組件
缺點(diǎn)
- 如果后代組件有需要那么所有數(shù)據(jù)都要從頂層一直傳遞到有需要的組件
Redux
名詞
-
store
-> 表示所有的數(shù)據(jù)蜓肆,它是存儲(chǔ)數(shù)據(jù)的地方,并且將store
傳遞下去即可谋币,上述 demo 中可以進(jìn)行改造即可let store = { money: { amount: 1000 } }
-
action
-> 動(dòng)作仗扬,分為action type
和action payload
-
action type
->emit
的事件名,即eventName
-
action payload
->emit
的數(shù)據(jù)蕾额,即data
-
reducer
-> 對(duì)數(shù)據(jù)的變動(dòng) -
subscribe
-> 訂閱(on
) ||eventHub.on
-
dispatch
-> 發(fā)布(emit
) ||eventHub.emit
使用 Redux
將上述 demo 使用 Redux 改寫(改寫的不同之處)并在線運(yùn)行早芭,此次需要引入 Redux 庫(kù),通過 Redux 官網(wǎng)(中文)找到 Counter 示例诅蝶,在示例中查看 index.js 文件退个,通過 index.js 文件進(jìn)行改寫。
注意: Redux 內(nèi)置了 EventHub
调炬,其中的 reducer
作用和 steward role
相似
優(yōu)點(diǎn)
- 將事件名稱(
action type
)和事件參數(shù)(action payload
)做了約束语盈,并且將所有的事件名稱(action type
)收集起來,放在一個(gè)文件中 - Redux 中
store.getState()
想要使得store
是只讀的( 雖然不能禁止缰泡,但是因?yàn)槭?JavaScript bug )刀荒,并且將只讀數(shù)據(jù)通過props
的形式放在頂層
缺點(diǎn)
- 如果后代組件有需要那么所有數(shù)據(jù)都要從頂層一直傳遞到有需要的組件