Regular進階: 跨元件通訊
本文由作者鄭海波授權網易雲社群釋出。
背景
在元件化不斷深入的大環境下,無論使用哪種 MDV 框架都最終會遇到一個頭疼的問題,就是「跨元件通訊」。
下圖是個簡單的例子
這裡包含「事件通訊」和「資料通訊」兩個維度。
事件傳遞
為了將事件 click
從 <LeafNode />
傳遞到最外層元件,需要依次通過 <SubNode />
和 <Sub />
等可能本不關心這個事件的元件(即使例子裡已經使用了proxy的簡化語法)
資料傳遞
為了從 <Top />
title
這個 prop 到 <LeafNode />
, 需要層層跨越 <Sub />
、<SubNode />
這些本不需要關心 title
屬性 的元件。
以上處理方式除了帶來效能上的損耗之外,更麻煩的就是造成了可維護性的急速下降。
顯而易見的事件通訊解決方案
最直接的做法就是引入一個「中介者」,簡而言之就是一個全域性的「跳板」,下例就是一個事件中介者
mediator.js
const Regular = require('regularjs');const emitter = new Regular;//每個Regular元件都是一個事件發射器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
引用,通過它實現了直接通訊.
更麻煩的兄弟節點之間的通訊當然也可以這樣來解決。
顯而易見的解決方案引出的另一個顯而易見的問題
上述中介者的引入的最大問題就是,所有相關元件都在 定義時 引入了對emitter
的 全域性耦合, 這個將導致元件無法在多工程間被複用。
一種合理的解決方案就是將對emitter
的耦合, 延遲到例項化階段。
在Regular之前的版本里,很多朋友會通過this.$parent
或this.$outer
等可控性很差的方式來實現,在v0.6
有了一種更好的方式。
modifyBodyComponent
新生命週期
在 Regular 的 v0.6
引入了一個新的生命週期叫 modifyBodyComponent
,它用來劫持到元件包裹的所有內部元件的初始化週期。
我們用一個簡單例子來實現下emitter
的動態注入
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
) 動態傳入了一個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來解決跨元件的資料通訊的方式
免費領取驗證碼、內容安全、簡訊傳送、直播點播體驗包及雲伺服器等套餐
更多網易技術、產品、運營經驗分享請訪問網易雲社群。
相關文章:
【推薦】 容器平臺選型的十大模式:Docker、DC/OS、K8S 誰與當先?
【推薦】 spring的父子上下文容器及配置