Riot.js riot.observable源码解析

369 阅读2分钟

介绍

riot.observable是riot的一个订阅发布模式的实现。那么在中什么时候会用到这个,其实在riot源码自身大量运用到了这个,我们也可以自己手动去创建一个具有发布订阅的实体对象。我们先看看在riot源码自身是何时用到这个。

在这篇文章中,我们就遇到过一次:

看下他的输入输出:

再看源码:

 /* istanbul ignore next */
  var observable = function(el) {

    /**
     * Extend the original object or create a new empty one
     * @type { Object }
     */

    el = el || {};

    /**
     * Private variables
     */
    var callbacks = {},
      slice = Array.prototype.slice;

    /**
     * Public Api
     */

    // extend the el object adding the observable methods
    Object.defineProperties(el, {
      /**
       * Listen to the given `event` ands
       * execute the `callback` each time an event is triggered.
       * @param  { String } event - event id
       * @param  { Function } fn - callback function
       * @returns { Object } el
       */
      on: {
        value: function(event, fn) {
          if (typeof fn == 'function')
            { (callbacks[event] = callbacks[event] || []).push(fn); }
          return el
        },
        enumerable: false,
        writable: false,
        configurable: false
      },

      /**
       * Removes the given `event` listeners
       * @param   { String } event - event id
       * @param   { Function } fn - callback function
       * @returns { Object } el
       */
      off: {
        value: function(event, fn) {
          if (event == '*' && !fn) { callbacks = {}; }
          else {
            if (fn) {
              var arr = callbacks[event];
              for (var i = 0, cb; cb = arr && arr[i]; ++i) {
                if (cb == fn) { arr.splice(i--, 1); }
              }
            } else { delete callbacks[event]; }
          }
          return el
        },
        enumerable: false,
        writable: false,
        configurable: false
      },

      /**
       * Listen to the given `event` and
       * execute the `callback` at most once
       * @param   { String } event - event id
       * @param   { Function } fn - callback function
       * @returns { Object } el
       */
      one: {
        value: function(event, fn) {
          function on() {
            el.off(event, on);
            fn.apply(el, arguments);
          }
          return el.on(event, on)
        },
        enumerable: false,
        writable: false,
        configurable: false
      },

      /**
       * Execute all callback functions that listen to
       * the given `event`
       * @param   { String } event - event id
       * @returns { Object } el
       */
      trigger: {
        value: function(event) {
          var arguments$1 = arguments;


          // getting the arguments
          var arglen = arguments.length - 1,
            args = new Array(arglen),
            fns,
            fn,
            i;

          for (i = 0; i < arglen; i++) {
            args[i] = arguments$1[i + 1]; // skip first argument
          }

          fns = slice.call(callbacks[event] || [], 0);

          for (i = 0; fn = fns[i]; ++i) {
            fn.apply(el, args);
          }

          if (callbacks['*'] && event != '*')
            { el.trigger.apply(el, ['*', event].concat(args)); }

          return el
        },
        enumerable: false,
        writable: false,
        configurable: false
      }
    });

    return el

  };

发现其实就是使用Object.defineProperty定义了四个函数,on/off/one/trigger

  • on : 订阅事件,其实就是用一个对象把你要监听的函数以[事件名-fn]的形式存起来
  • off:删除事件
  • one:订阅只发生一次的事件
  • trrigger:触发已经订阅好的事件 其实就是把订阅好的事件数组取出来一个一个执行。

然后再riot源码中,还有一个地方用到了:

这里的意思是,把创建的每一个tag对象都绑定上发布订阅模式,也就是说,我们在代码中的

this.on('mount', () => {
    // ...
});
this.on('update', () => {]\
    // ...
})

都是订阅一个事件。然后riot会在合适的时机,去trigger调用这些已经订阅好的事件。具体的后面再说。