ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

通过发布订阅模式实现组件间的通信

2021-02-26 11:36:29  阅读:271  来源: 互联网

标签:订阅 fns handlers 通信 eventName 组件 fn


前言

各种技术框架,比如 vue、react 和小程序,实现父子组件和兄弟组件通信的方案有很多,大多方案都强依赖于框架本身。这里介绍一种通过发布和订阅的方式来实现组件通信的方案,纯 JavaScript 实现,可以适用于各种框架。

发布订阅模式

发布订阅模式包含三部分内容,发布者、订阅者和数据处理中心。订阅者把自己想监听的事件和回调函数信息写入到数据处理中心去;当事件触发时,发布者发布该事件到数据处理中心,由数据处理中心统一执行订阅者写入到数据处理中心对应事件的回调函数。

比如A组件需要向B组件传递数据,通过发布订阅模式来实现的思路是:

  • 在B组件里添加(on,事件监听)一个事件订阅,监听的事件名称和回调函数;
  • 当A组件需要给B组件传递数据时,可发布(emit)对应的事件名称并携带相应的数据,数据处理中心根据相应的事件执行回调函数并传递数据给B组件

定义数据处理中心

用来存储事件和回调函数信息

class Emitter {
    constructor() {
        // 数据处理中心,用来存储事件和回调函数信息
        this.handlers = {}
    }
}

实现订阅功能

需要传入两个参数,订阅的事件名称和事件触发时的回调函数

// 订阅
on(eventName, fn) {
    if (typeof fn !== "function") { console.error('fn must be a function') }
    if (!this.handlers[eventName]) {
        this.handlers[eventName] = []
    }
    this.handlers[eventName].push(fn)
}

实现发布功能

遍历数据处理中心的数据,找到并执行对应事件名称的回调函数

// 发布
emit(eventName) {
    const fns = this.handlers[eventName]
    if (fns && fns.length) {
        // arguments,携带一些数据信息;将arguments函数参数列表(类数组对象)转为数组
        const args = [].slice.call(arguments)
        args.shift() // 参数去掉事件名称
        fns.forEach((fn) => {
            // 给回调方法传参
            fn.apply(null, args)
        })
    }
}

取消订阅

删除数据处理中心数组中对应事件的回调函数

// 注销订阅
off(eventName, fn) {
    // 若是没有传参,注销所有的订阅
    if (!arguments.length) {
        this.handlers = {};
        return this;
    }
    const fns = this.handlers[eventName]
    if (!fns) return
    // 若是只传eventName,不传fn,删除对应事件名称下的所有回调函数
    if (arguments.length === 1) {
      delete this.handlers[eventName]
        return
    }
    if(fns && fns.includes(fn)){
        fns.splice(fns.indexOf(fn), 1)
    }
}

完整代码如下:

class Emitter {
    constructor() {
        // 数据处理中心,用来存储事件和回调函数信息
        this.handlers = {}
    }
    // 订阅
    on(eventName, fn) {
        if (typeof fn !== "function") { console.error('fn must be a function') }
        if (!this.handlers[eventName]) {
            this.handlers[eventName] = []
        }
        this.handlers[eventName].push(fn)
    }
    // 发布
    emit(eventName) {
        const fns = this.handlers[eventName]
        if (fns && fns.length) {
            // arguments,携带一些数据信息;将arguments函数参数列表(类数组对象)转为数组
            const args = [].slice.call(arguments)
            args.shift() // 参数去掉事件名称
            fns.forEach((fn) => {
                // 给回调方法传参
                fn.apply(null, args)
            })
        }
    }
    // 注销订阅
    off(eventName, fn) {
        // 若是没有传参,注销所有的订阅
        if (!arguments.length) {
            this.handlers = {};
            return this;
        }
        const fns = this.handlers[eventName]
        if (!fns) return
        // 若是只传eventName,不传fn,删除对应事件名称下的所有回调函数
        if (arguments.length === 1) {
          delete this.handlers[eventName]
            return
        }
        if(fns && fns.includes(fn)){
            fns.splice(fns.indexOf(fn), 1)
        }
    }
}

module.exports = new Emitter()

代码示例

A组件需要向B组件传递数据

  • B组件
// B组件
import emitter from 'emitter'
export default {
  data() {
    return {
      pageIndex: 1
    }
  },
  created() {
    // 添加'getAdata'事件订阅
    emitter.on('getAData' + this.pageIndex, this.getAData.bind(this))
  },
  // 路由页面销毁
  destroyed() {
    emitter.on('off' + this.pageIndex)
  },
  methods: {
    getAData(data) {
      console.log(data.info, '获取到A组件传给B组件的数据')
    }
  }
}
  • A组件
// A组件
import emitter from 'emitter'
export default {
  data() {
    return {
      pageIndex: 1
    }
  },
  created() {
    setTimeout(() => {
      // 发布'getAdata'事件并携带数据
      emitter.eimit('getAData' + this.pageIndex, {
        info: 'A组件发送给B组件的数据'
      })
    }, 2000)
  }
}

原文:https://yolkpie.net/2020/11/26/通过发布订阅模式实现组件间的通信/

标签:订阅,fns,handlers,通信,eventName,组件,fn
来源: https://www.cnblogs.com/yolkpie/p/14451399.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有