发布订阅模式(Vue响应式原理)

879 阅读1分钟

发布订阅模式:订阅者,发布者,信号中心

我们假定,存在一个“信号中心”,某个任务执行完成,就向信号中心"发布"(publish)一个信号,其它任务可以向信号中心“订阅”(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern)

家长向学生所在的班级订阅了获取学生考试成绩的事件,当老师公布学生的成绩后,就会自动通知学生的家长。

在整个案例中,学生所在的班级为信号中心,老师为发布者,家长为订阅者

Vue 的自定义事件就是基于发布订阅模式来实现的。

下面通过Vue中兄弟组件通信过程,来理解发布订阅模式

// eventBus.js
// 事件中心
let eventHub=new Vue()
//ComponentA.vue
addTodo:function(){
    //发布消息(事件)
    eventHub.$emit('add-todo',{text:this.newTodoText})
    this.newTodoText=''
}
//ComponentB.vue
//订阅者
created:function(){
    //订阅消息(事件)
    eventHub.$on('add-todo',this.addTodo)
}

通过以上代码,我们可以理解发布订阅模式中的核心概念。

下面我们模拟Vue中的自定义事件的实现

下面我们先来做一个基本的分析:

先来看如下代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue 自定义事件</title>
  </head>
  <body>
    <script src="./js/vue.js"></script>
    <script>
      //Vue自定义事件
      let vm = new Vue();
      //注册事件(订阅消息)
      vm.$on("dataChange", () => {
        console.log("dataChange");
      });
      vm.$on("dataChange", () => {
        console.log("dataChange");
      });
      //触发事件(发布消息)
      vm.$emit("dataChange");
    </script>
  </body>
</html>

通过上面的代码,我们可以看到$on实现事件的注册,而且可以注册多个事件,那么我们可以推测在其内部有一个对象来存储注册的事件,对象的格式为:

{'click':[fn1,fn2],'change':[fn]}

以上格式说明了,我们注册了两个事件,分别为clickchange. 下面我们根据以上的分析过程,来模拟实现自定义事件。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>发布订阅模式</title>
  </head>
  <body>
    <script>
      class EventEmitter {
        constructor() {
          // {'click':[fn1,fn2],'change':[fn]}
          // 存储事件与处理函数的对应关系
          this.subs = {};
        }
        //注册事件
        //第一个参数为事件名称
        // 第二个参数为处理函数
        // 将对应的处理函数添加到subs对象中
        $on(eventType, fn) {
          //判断对应的eventType是否有相应的处理函数,如果有,直接添加到数组中,如果没有返回一个空数组。
          if (!this.subs[eventType]) {
            this.subs[eventType] = [];
          }
          this.subs[eventType].push(fn);
        }
        //触发事件
        $emit(eventType) {
          if (this.subs[eventType]) {
            this.subs[eventType].forEach((handler) => {
              handler();
            });
          }
        }
      }
      //测试代码
      let em = new EventEmitter();
      em.$on("click", () => {
        console.log("click1");
      });
      em.$on("click", () => {
        console.log("click2");
      });
      em.$emit("click");
    </script>
  </body>
</html>

\