背景

在组件化不断深入的大环境下,无论使用哪种 MDV 框架都最终会遇到一个头疼的问题,就是「跨组件通信」。

下图是个简单的例子( 为了方便起见,我把所有组件的模板都放在了一张图中进行描述 )

这里包含「事件通信」和「数据通信」两个维度。

事件传递

为了将 click 事件 从 <LeafNode /> 传递到最外层组件,需要依次通过 <SubNode /><Sub /> 等可能本不关心这个事件的组件(即便例子里已经使用了简化语法

数据传递

为了从 <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 作为一个全局单例被 LeafNodeTop 直接引用,通过它实现了通信.

更复杂的兄弟节点之间的通信当然也可以这样来解决。

显而易见的解决方案引出的另一个显而易见的问题

上述引入全局中介者的最大问题就是,所有相关组件都在 定义时 引入了对emitter全局耦合, 这将导致组件无法被跨工程复用。

一种合理的解决方案就是将对emitter的耦合, 延迟到实例化阶段。

在Regular之前的版本里,很多朋友会通过this.$parentthis.$outer等可控性很差的方式来实现,在v0.6有了一种更好的方式。

modifyBodyComponent 新的生命周期

Regular 在 v0.6 版本引入了一个新的生命周期 modifyBodyComponent ,可以用它来劫持组件包裹的所有内部组件的初始化过程。

让我们用一个简单的例子来说明下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

results matching ""

    No results matching ""