事件的监听(订阅)和事件的触发(发布)

  • on():负责注册事件的监听器,指定事件触发时的回调函数。
  • emit():负责触发事件,可以通过传参使其在触发的时候携带数据 。

    1.映射

事件和监听函数的对应关系“映射”,处理“映射”我们大部分情况下都是用对象来做的。所以说在全局我们需要设置一个对象,来存储事件和监听函数之间的关系:

  1. constructor() {
  2. // eventMap 用来存储事件和监听函数之间的关系
  3. this.eventMap = {};
  4. }

2.订阅

把事件和对应的监听函数写入到 eventMap 里面去:

  1. // type 这里就代表事件的名称
  2. on(type, handler) {
  3. // hanlder 必须是一个函数,如果不是直接报错
  4. if(!(handler instanceof Function)) {
  5. throw new Error("需要传一个函数")
  6. }
  7. // 判断 type 事件对应的队列是否存在
  8. if(!this.eventMap[type]) {
  9. // 若不存在,新建该队列
  10. this.eventMap[type] = []
  11. }
  12. // 若存在,直接往队列里推入 handler
  13. this.eventMap[type].push(handler)
  14. }

3.发布

发布操作就是一个“读”操作。

  1. // 别忘了我们前面说过触发时是可以携带数据的,params 就是数据的载体
  2. emit(type, params) {
  3. // 假设该事件是有订阅的(对应的事件队列存在)
  4. if(this.eventMap[type]) {
  5. // 将事件队列里的 handler 依次执行出队
  6. this.eventMap[type].forEach((handler, index)=> {
  7. // 注意别忘了读取 params
  8. handler(params)
  9. })
  10. }
  11. }

4.关闭

关闭就是一个出队列的操作。

  1. off(type, handler) {
  2. if(this.eventMap[type]) {
  3. this.eventMap[type].splice(this.eventMap[type].indexOf(handler)>>>0,1)
  4. }
  5. }

5.测试

完整代码

  1. class myEventEmitter {
  2. constructor() {
  3. this.eventMap = {};
  4. }
  5. on(type, handler) {
  6. if (!handler instanceof Function) {
  7. throw new Error("请传一个函数");
  8. }
  9. if (!this.eventMap[type]) {
  10. this.eventMap[type] = []
  11. }
  12. this.eventMap[type].push(handler)
  13. }
  14. emit(type, params) {
  15. if (this.eventMap[type]) {
  16. this.eventMap[type].forEach((handler) => {
  17. handler(params);
  18. })
  19. }
  20. }
  21. off(type, handler) {
  22. if (this.eventMap[type]) {
  23. // 位运算 负数返回无限大的数,否则返回本身
  24. this.eventMap[type].splice(this.eventMap[type].indexOf(handler) >>> 0, 1);
  25. }
  26. }
  27. }
  28. const myEvent = new myEventEmitter();
  29. // 编写一个简单的 handler
  30. const testHandler = function (params) {
  31. console.log(`test事件被触发了,testHandler 接收到的入参是${params}`);
  32. };
  33. // 监听 test 事件
  34. myEvent.on("test", testHandler);
  35. // 在触发 test 事件的同时,传入希望 testHandler 感知的参数
  36. myEvent.emit("test", "123");
  37. // myEvent.off("test", testHandler);
  38. console.log(`object`, myEvent.eventMap)

image.png

6.在React中应用

  1. // index.jsx
  2. import React, { Component } from 'react'
  3. import A from './A'
  4. import B from './B'
  5. import event from './event.js'
  6. class index extends Component {
  7. render() {
  8. React.$myEvent = new event()
  9. return (
  10. <div>
  11. <A></A>
  12. <B></B>
  13. </div>
  14. )
  15. }
  16. }
  17. export default index
  1. // event.js
  2. class myEventEmitter {
  3. constructor() {
  4. this.eventMap = {};
  5. }
  6. on(type, handler) {
  7. if (!handler instanceof Function) {
  8. throw new Error("请传一个函数");
  9. }
  10. if (!this.eventMap[type]) {
  11. this.eventMap[type] = []
  12. }
  13. this.eventMap[type].push(handler)
  14. }
  15. emit(type, params) {
  16. if (this.eventMap[type]) {
  17. this.eventMap[type].forEach((handler) => {
  18. handler(params);
  19. })
  20. }
  21. }
  22. off(type, handler) {
  23. if (this.eventMap[type]) {
  24. this.eventMap[type].splice(this.eventMap[type].indexOf(handler) >>> 0, 1);
  25. }
  26. }
  27. }
  28. export default myEventEmitter
  1. // A
  2. import React from "react";
  3. class A extends React.Component {
  4. state = {
  5. newParams: "",
  6. };
  7. handler = (params) => {
  8. this.setState({
  9. newParams: params,
  10. });
  11. };
  12. bindHandler = () => {
  13. React.$myEvent.on("someEvent", this.handler);
  14. };
  15. render() {
  16. return (
  17. <div>
  18. <button onClick={this.bindHandler}>点我监听A的动作</button>
  19. <div>A传入的内容是[{this.state.newParams}]</div>
  20. </div>
  21. );
  22. }
  23. }
  24. export default A;
  1. // B
  2. import React from "react";
  3. class B extends React.Component {
  4. state = {
  5. infoToB: "哈哈哈哈我来自A",
  6. };
  7. reportToB = () => {
  8. React.$myEvent.emit("someEvent", this.state.infoToB);
  9. };
  10. render() {
  11. return <button onClick={this.reportToB}>点我把state传递给B</button>;
  12. }
  13. }
  14. export default B;

image.png