美文网首页让前端飞Web前端之路前端开发那些事
实现组件之间的通信 - (发布-订阅模式)

实现组件之间的通信 - (发布-订阅模式)

作者: 不洗头的野人 | 来源:发表于2017-05-08 10:45 被阅读769次

    本文介绍

    本文主要讲解JavaScript中的发布-订阅模式,也可以叫做观察者模式,个人觉得前者叫法更适合从代码的角度来理解。

    文章最终目的是实现组件(模块)之间的通信,其中会额外实现一下组件的生命周期功能。因为目前某几大框架实在是太火了,所以最近写的文章,都围绕着这些框架所用的知识点来写。

    本文代码风格: ES5

    内容

    发布-订阅模式

    在讲组件间通信前,我们先从其实现的基础讲起,那就是发布-订阅模式

    下面我们就来实现一下发布-订阅的功能,那么实现之前,先来分析下该功能的需求:

    注意:需求有很多种,下面只是其中一种,这种会比较贴近我们最终的需求(组件通信)

    1. 有一个发布者
    2. 发布者拥有一个记事本,上面记录着与订阅者间约定好事情和该事情的类型(事情就是函数)
    3. 发布者有一个广播的方法,该方法接受一个类型参数。每次广播时,就会把记事本上该类型的事情都执行一遍
    4. 发布者可以在记事本中添加某件事情,并且要指定该事情的类型
    5. 发布者也可以从记事本删除掉某件事情(需要知道该事情的类型)

    注意我上面加粗的文字!

    搞清楚需求后,就开始写代码:

    // 1.发布者
    var publisher = {};
    // 2.拥有一个笔记本,这里使用对象而不使用数组
    // 因为 "类型": "事情",属于键值对
    publisher.notebook = {}
    // 3.拥有广播方法,会将类型的事情都执行一遍
    publisher.broadcast = function (type) {
        //取出事情。一个类型,可以有多件事情
        // funArray是数组,里面装的是函数
        var funArray = publisher.notebook[type];
        // 把所有事情执行一遍(事情就是函数)
        for (var i = 0; i < funArray.length; i++) {
            funArray[i]();
        }
    }
    
    // 4.记事本可以添加事情并指定类型
    publisher.addFun = function (type, fun) {
        // 为了代码更容易阅读,没做容错处理,前面和后面也是
        publisher.notebook[type].push(fun)
    }
    
    // 5.记事本可以删除事情
    publisher.removeFun = function (type, fun) {
        var funArray = publisher.notebook[type];
        for (var i = 0; i < funArray.length; i++) {
            if (fun === item) {
                funArray.splice(i, 1)
            }
        }
    }
    

    功能写完了,来测试一下效果:

    // 订阅者
    var user = {
        // 订阅者要约的事情
        yue: function () {
            console.log('正在执行要约的事');
        }
    }
    // 添加到发布者的笔记本,随意给的类型
    publisher.addFun('type1' , user.yue)
    // 广播 
    publisher.broadcast('type1')
    
    成功打印

    上面就是发布-订阅模式,已成功实现。其实同一种设计模式,可以有很多不同的用法,但是其核心思想不变。

    组件之间的通信

    上面的发布-订阅模式中,有一个对象充当发布者,有一个对象充当订阅者。但是组件间的通信好像完全不是这么回事,因为似乎每个组件(对象)即可以充当发布者,也可以充当订阅者。

    没错,按照上面的例子,只要你为订阅者user也添加发布者的功能,那么这两个对象就能实现通信了。

    不过这样做的话实在太麻烦了,哪怕引用mixin的概念来实现,也是浪费性能。

    所以这里再引入一个中介者的概念,这个中介者就是刚才的发布者publisher,它是个全局对象,在哪里都可以引用。(在框架中,一般为类的静态属性,如Vue.xxx,React.xxx)

    组件通过中介者(全局发布者),来注册事件(约定事情),也可以主动通知(调用)中介者,让其执行某类型的事情。

    基于上面代码,我们来实现一下两个组件之间的通信:

    如果对下面代码阅读比较吃力的话(我觉得我注释写的挺好的,你应该看得懂),可以看下我另一篇关于生命周期的文章,会帮助你更好的理解组件大概是怎么一回事。

    // 模拟一个Vue React那样的组件
    function Component (option) {
        // 把传递进来的option对象的属性和方法揽到自己身上
        for (var key in option) {
            this[key] = option[key];
        }
        // 开始执行生命周期方法
        this.init();
    }
    // 定义一些生命周期方法,并让其按顺序进行
    Component.prototype = {
        constructor: Component,
        init: function () {
            // 组件渲染
            this.render();
            // 组件挂载完毕
            this.componentDidMount();
        },
        // 默认有此方法,但render方法一定要自己写,不然报错
        componentDidMount: function () {
    
        }
    }
    
    // 现在我们的目的是:创建两个组件,让两个组件实现通信(传输数据),下面是component1给component2传递数据
    
    // 由于涉及到传递数据,这里我们小幅度修改publisher的广播方法,让它多一个data参数
    
    // 其他均不变,多了一个data参数
    publisher.broadcast = function (type, data) {
       var funArray = publisher.notebook[type];
       for (var i = 0; i < funArray.length; i++) {
            // 执行函数时,传递data
            funArray[i](data);
        }
    }
    
    // 组件1
    var component1 =  new Component({
        // 该组件的数据
        data: {
            passValue: '我是component1传递过来的值'
        },
        // 重写render
        render: function () {
          
        },
         // 自定义的一个方法,该方法可以通知中介者执行广播
        _emit: function (type) {
            publisher.broadcast(type, this.data.passValue);
        }
    
    })
    
    var component2 = new Component({
            data: {
                
            },
            render: function () {
            
            },
        // 组件挂载完后,注册事件
        componentDidMount: function () {
            // 利用中介者注册事件
            publisher.addFun('log', function (data) {
                console.log('我是组件2,接受到的数据是:' + data);
            })
        }
        
    })
    // 这里手动触发,在框架中,一般会有按钮,点击即可触发this._emit('log')
    component1._emit('log');
    
    组件之间成功通信

    代码建议复制份自己跑一下,会有助于你理解组件的原理。

    相关文章

      网友评论

        本文标题:实现组件之间的通信 - (发布-订阅模式)

        本文链接:https://www.haomeiwen.com/subject/gekmtxtx.html