Regular进阶: 跨组件通讯

本文由做者郑海波受权网易云社区发布。php


背景

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

下图是个简单的例子node

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

事件传递安全

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

数据传递服务器

为了从 <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 ,它用来劫持到组件包裹的全部内部组件的初始化周期。

咱们用一个简单例子来实现下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来解决跨组件的数据通讯的方式


免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请访问网易云社区


相关文章:
【推荐】 flex布局之flex-grow和flex-shrink如何计算

相关文章
相关标签/搜索