vueイベント関連インスタンスメソッドソース分析

24137 ワード

vm.$on
-vueインスタンス登録イベントのapi
Vue.prototype.$on = function (event, fn) {
     
      var vm = this;
      if (Array.isArray(event)) {
     
        for (var i = 0, l = event.length; i < l; i++) {
     
          vm.$on(event[i], fn);
        }
      } else {
     
        (vm._events[event] || (vm._events[event] = [])).push(fn);
        // optimize hook:event cost by using a boolean flag marked at registration
        // instead of a hash lookup
        if (hookRE.test(event)) {
     
          vm._hasHookEvent = true;
        }
      }
      return vm
    };

まず,伝達されたeventがイベント配列であるか否かを判断し,もしそうであれば再帰的な考え方でvueインスタンスにイベントを登録する.単一のイベント文字列の場合、イベントが登録されているかどうかを判断し、登録されていない場合は、イベントに空の配列を初期化します.その後、fnすなわちコールバック関数を配列に押し込む.
vm.$off
-vueインスタンスログアウトイベントのapi
    Vue.prototype.$off = function (event, fn) {
     
      var vm = this;
      // all
      if (!arguments.length) {
     
        vm._events = Object.create(null);
        return vm
      }
      // array of events
      if (Array.isArray(event)) {
     
        for (var i$1 = 0, l = event.length; i$1 < l; i$1++) {
     
          vm.$off(event[i$1], fn);
        }
        return vm
      }
      // specific event
      var cbs = vm._events[event];
      if (!cbs) {
     
        return vm
      }
      if (!fn) {
     
        vm._events[event] = null;
        return vm
      }
      // specific handler
      var cb;
      var i = cbs.length;
      while (i--) {
     
        cb = cbs[i];
        if (cb === fn || cb.fn === fn) {
     
          cbs.splice(i, 1);
          break
        }
      }
      return vm
    };

分別討論の思想.1.パラメータが入力されていない場合は、イベントリストを直接空にします.作者の考え方は、Object.create方式は、新しいイベントを格納するvueインスタンス属性を生成する.2.入力がイベント配列である場合、構想はonと同じであり、再帰的にログアウトする.3.特判部分(specific event部分)は、このイベントが登録されていない場合は、直接戻ります.ログアウトしたい指定したイベントのコールバックがない場合は、対応するイベント配列を空にします.4.最後に、イベントが送信され、パラメータが送信された場合について説明します.著者らの構想は,逆シーケンスで配列を遍歴し,spliceでイベントコールバックを削除することである.どうしてそうするの?もし遍歴していて、1つの数を削除したばかりで、後ろの数が自動的に前にシフトしたら、私たちは遍歴したことのないコールバックをスキップします.つまり、最後のコールバックを削除する以外は、順方向に遍歴し、削除するたびにコールバックをスキップして処理しません.
vm.$once
    Vue.prototype.$once = function (event, fn) {
     
      var vm = this;
      function on () {
     
        vm.$off(event, on);
        fn.apply(vm, arguments);
      }
      on.fn = fn;
      vm.$on(event, on);
      return vm
    };


明らかに、onceの下位層はonとoffを用いて実現される.一度だけトリガーされたイベントコールバックをon関数、すなわちユーザが自分で追加した属性にマウントし、対応するイベント配列にon関数を押し込む.イベントがトリガーされるとon関数内の論理を実行し、イベント配列を空にし、applyの形式でfnを実行する.
vue.$emit
    Vue.prototype.$emit = function (event) {
     
      var vm = this;
      {
     
        var lowerCaseEvent = event.toLowerCase();
        if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
     
          tip(
            "Event \"" + lowerCaseEvent + "\" is emitted in component " +
            (formatComponentName(vm)) + " but the handler is registered for \"" + event + "\". " +
            "Note that HTML attributes are case-insensitive and you cannot use " +
            "v-on to listen to camelCase events when using in-DOM templates. " +
            "You should probably use \"" + (hyphenate(event)) + "\" instead of \"" + event + "\"."
          );
        }
      }
      var cbs = vm._events[event];
      if (cbs) {
     
        cbs = cbs.length > 1 ? toArray(cbs) : cbs;
        var args = toArray(arguments, 1);
        var info = "event handler for \"" + event + "\"";
        for (var i = 0, l = cbs.length; i < l; i++) {
     
          invokeWithErrorHandling(cbs[i], vm, args, vm, info);
        }
      }
      return vm
    };
  }
  function toArray (list, start) {
     
    start = start || 0;
    var i = list.length - start;
    var ret = new Array(i);
    while (i--) {
     
      ret[i] = list[i + start];
    }
    return ret
  }

toArray関数の役割は、提供されるサイズstartに基づいて、配列長-start個の配列要素を後方から前方にコピーすることです.
  function invokeWithErrorHandling (
    handler,
    context,
    args,
    vm,
    info
  ) {
     
    var res;
    try {
     
      res = args ? handler.apply(context, args) : handler.call(context);
      if (res && !res._isVue && isPromise(res) && !res._handled) {
     
        res.catch(function (e) {
      return handleError(e, vm, info + " (Promise/async)"); });
        // issue #9511
        // avoid catch triggering multiple times when nested calls
        res._handled = true;
      }
    } catch (e) {
     
      handleError(e, vm, info);
    }
    return res
  }

この関数の論理は大体コールバックを処理することである.emitボディに戻ると、まずイベントに対応するコールバックリストを取得し、イベント配列の長さが1より大きい場合はtoArrayを呼び出して直接コピーします.次に配列をループし、コールバックを処理します.