本文由作者鄭海波授權網易云社區(qū)發(fā)布。
背景
在組件化不斷深入的大環(huán)境下,無論使用哪種 MDV 框架都最終會遇到一個頭疼的問題,就是「跨組件通信」。
下圖是個簡單的例子

這里包含「事件通信」和「數據通信」兩個維度。
事件傳遞
為了將事件?click?從?<LeafNode />?傳遞到最外層組件,需要依次通過?<SubNode />?和?<Sub />?等可能本不關心這個事件的組件(即使例子里已經使用了proxy的簡化語法)
數據傳遞
為了從?<Top />?傳遞?title?這個 prop 到?<LeafNode />?, 需要層層跨越?<Sub />、<SubNode />?這些本不需要關心?title屬性 的組件。
以上處理方式除了帶來性能上的損耗之外,更麻煩的就是造成了可維護性的急速下降。
顯而易見的事件通信解決方案
最直接的做法就是引入一個「中介者」,簡而言之就是一個全局的「跳板」,下例就是一個事件中介者
mediator.js
const?Regular?=?require('regularjs');const?emitter?=?new?Regular;//每個Regular組件都是一個事件發(fā)射器module.exports?=?{
????broadcast:?emiter.$emit.bind(emiter),
????subscribe:?emiter.$on.bind(emiter)
}Top.js
const?{?broadcast,?subscribe?}?=?require('./mediator')const?Regular?=?require('regularjs');const?Top?=?Regular.extend({
????name:?'Top',
????init(){
????????subscribe('check',?ev?=>{????????????//?通過emitter廣播事件
????????})
????}
})LeafNode.js
const?{?broadcast,?subscribe?}?=?require('path/to/mediator')const?Regular?=?require('regularjs');const?LeafNode?=?Regular.extend({
????template:?`<div?on-click={?this.onClick()?}?></div>`,
????name:?'LeafNode',
????onClick(){
????????broadcast(?'check',?{?type:?'leafnode'?}?)
????}
})mediator?作為一個全局單例直接被?LeafNode?和?Top?引用,通過它實現(xiàn)了直接通信.
更麻煩的兄弟節(jié)點之間的通信當然也可以這樣來解決。
顯而易見的解決方案引出的另一個顯而易見的問題
上述中介者的引入的最大問題就是,所有相關組件都在?定義時?引入了對emitter的?全局耦合, 這個將導致組件無法在多工程間被復用。
一種合理的解決方案就是將對emitter的耦合, 延遲到實例化階段。
在Regular之前的版本里,很多朋友會通過this.$parent或this.$outer等可控性很差的方式來實現(xiàn),在v0.6有了一種更好的方式。
modifyBodyComponent?新生命周期
在 Regular 的?v0.6?引入了一個新的生命周期叫?modifyBodyComponent?,它用來劫持到組件包裹的所有內部組件的初始化周期。
我們用一個簡單例子來實現(xiàn)下emitter的動態(tài)注入
Broadcastor.js
const?Regular?=?require('regularjs');const?Broadcastor?=?Regular.extend({
????name:?'Broadcastor',
????config(?data?){????????const?emitter?=?data.emitter;????????this._broadcast?=?emitter.$emit.bind(emitter),????????this._subscribe?=??emitter.$on.bind(emitter)
????},
????modifyBodyComponent(?component,?next?){
????????component.$broadcast?=?this._broadcast;
????????component.$subscribe?=?this._subscribe;
????????next(component)?//?交給外層的包裝器
????}
})Top.js
//?const?{?broadcast,?subscribe?}?=?require('./mediator')const?Regular?=?require('regularjs');const?Top?=?Regular.extend({
????name:?'Top',
????template:?'略...',
????init(){????????this.$subscribe('check',?ev?=>{????????????//?通過emitter廣播事件
????????})
????}
})LeafNode.js
//?const?{?broadcast,?subscribe?}?=?require('path/to/mediator')const?Regular?=?require('regularjs');const?LeafNode?=?Regular.extend({
????template:?`<div?on-click={?this.onClick()?}?></div>`,
????name:?'LeafNode',
????onClick(){
????????this.$broadcast(?'check',?{?type:?'leafnode'?}?)
????}
})main.js?(入口)
new?Regular({
????template:`
????????<Broadcastor?emitter={emitter}>????????????<!--?其中LeafNode?在Top內部?-->
????????????<Top?/>
????????</Broadcastor>
????`,
????data:?{
????????emitter:?new?Regular
????}
})這樣所有的組件聲明都取消了對全局?emitter?的直接依賴,而是在入口(main.js) 動態(tài)傳入了一個emitter。
生命周期
需要注意的是modifyBodyComponent?會在 component本身compile之后運行, 但在init之前運行。以上面的例子為代表, 完整生命周期如下.
Broadcastor.config?->?Broadcastor.compile ????-?Top.config?->?Top.compile ????????-?LeafNode.config?->?LeafNode.compile ????????????-?Broadcastor.modifyBodyComponent(LeafNode) ????????-?LeafNode.init ????????-?Broadcastor.modifyBodyComponent(Top) ????-?Top.init -?Broadcastor.init
下一篇,應該會以redux(rgl-redux)為例,介紹一種基于modifyBodyComponent來解決跨組件的數據通信的方式
免費領取驗證碼、內容安全、短信發(fā)送、直播點播體驗包及云服務器等套餐
更多網易技術、產品、運營經驗分享請訪問網易云社區(qū)。
相關文章:
【推薦】?數據庫路由中間件MyCat - 源代碼篇(10)