Skip to content

模块间通信

上一节中实现的发布—订阅模式的实现,是基于一个全局的 Event 对象,我们利用它可以在 两个封装良好的模块中进行通信,这两个模块可以完全不知道对方的存在。就如同有了中介公司 之后,我们不再需要知道房子开售的消息来自哪个售楼处。

比如现在有两个模块,a 模块里面有一个按钮,每次点击按钮之后,b 模块里的 div 中会显示 按钮的总点击次数,我们用全局发布—订阅模式完成下面的代码,使得 a 模块和 b 模块可以在保 持封装性的前提下进行通信。

html
<body>
  <button id="btn">btn</button>
  <div id="div"></div>

  <script>
    var Event = (function () {
      var clientList = {},
        listen,
        trigger,
        remove;
      listen = function (key, fn) {
        if (!clientList[key]) {
          clientList[key] = [];
        }
        clientList[key].push(fn);
      };
      trigger = function () {
        var key = Array.prototype.shift.call(arguments),
          fns = clientList[key];
        if (!fns || fns.length === 0) {
          return false;
        }
        for (var i = 0, fn; fn = fns[i++];) {
          fn.apply(this, arguments);
        }
      };
      remove = function (key, fn) {
        var fns = clientList[key];
        if (!fns) {
          return false;
        }
        if (!fn) {
          fns && (fns.length = 0);
        } else {
          for (var l = fns.length - 1; l >= 0; l--) {
            var _fn = fns[l];
            if (_fn === fn) {
              fns.splice(l, 1);
            }
          }
        }
      };
      return {
        listen: listen,
        trigger: trigger,
        remove: remove
      }
    })();

    const a = (function () {
      let count = 0

      btn.addEventListener('click', () => {
        count++

        Event.trigger('add', count)
      })
      
    })()

    const b = (function () {
      Event.listen('add', function (count) {
        div.innerText = count
      })
    })()
  </script>
</body>

但在这里我们要留意另一个问题,模块之间如果用了太多的全局发布—订阅模式来通信,那 么模块与模块之间的联系就被隐藏到了背后。我们最终会搞不清楚消息来自哪个模块,或者消息 会流向哪些模块,这又会给我们的维护带来一些麻烦,也许某个模块的作用就是暴露一些接口给 其他模块调用。