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

本文介绍

本文主要讲解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');
组件之间成功通信

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容