React的事件機制一 --- 註冊

背景

關於事件,相信都不陌生。 咱們再DOM上綁定事件,並給定一個處理函數。在DOM上事件觸發的時候,會執行處理函數。node

固然也有另一種事件綁定機制---代理。react

通常咱們再用ul元素的時候,咱們的事件通常是綁定在li的父元素ul上,這樣讓ul來代理li的事件,這樣只用綁定一次便可,而React的事件也是使用的代理模式,可是,React的全部事件都是綁定在document上的。瀏覽器

React的事件機制分爲兩個模塊

註冊與存儲

分發事件

咱們先來講說 事件的註冊與存儲

事件的註冊與存儲也分爲兩個模塊,註冊與回調函數存儲。

入口 ReactDOMComponent模塊

enqueuePutListener 方法是整個React事件機制的核心方法。這個方法的核心做用是註冊事件到document上,與存儲回調函數到EventPluginHub中

/*
  @param id: 事件綁定的當前元素的id
  @param registrationName: 綁定的事件名稱,onClick。
  @param listener: onClick後的那個回調函數
  @param transaction: 一個事務
*/ 
function enqueuePutListener(id, registrationName, listener, transaction) {
 // id是當前元素的id,就是你綁定的事件,好比onClick綁定在哪一個元素上,那個元素的data-reactid屬性的值。
  var container = ReactMount.findReactContainerForID(id);
  // container就是包裹全部React組件的那個元素。單頁面應用通常只有一個 就是咱們在HTML中定義的那個 <div id="app"></div>

  if (container) {
    var doc = container.nodeType === ELEMENT_NODE_TYPE ? container.ownerDocument : container;
    // doc是一個document
    listenTo(registrationName, doc);
    // 調用listenTo方法註冊事件。注意,這裏只是將事件註冊在了document上,而沒有給咱們定義的回到函數。
    // listenTo方法是ReactBrowserEventEmitter模塊的listenTo方法。
  }
   // 使用事務,將listenr存儲起來。
  transaction.getReactMountReady().enqueue(putListener, {
    id: id,
    registrationName: registrationName,
    listener: listener
  });
}
複製代碼

enqueuePutListener方法的核心有兩個

1 listenTo方法: ReactBrowserEventEmitter.listenTo主要的做用即是註冊事件到document上。bash

2 enqueue方法:主要的做用即是將回調函數存儲起來。app

限於篇幅的做用,咱們先來講說註冊事件到document上的這條路。也就是方法 listenTo

ReactBrowserEventEmitter模塊

listenTo: function (registrationName, contentDocumentHandle) {
    <!--listenTo方法,考慮了太多的可能性,咱們刪除一些無用的代碼,保留核心代碼-->
    
    <!--mountAt就是事件註冊的document(頁面中能夠有多個document)-->
    var mountAt = contentDocumentHandle;
    <!--// isListening就是當前Document---mountAt,對應的一個惟一的對象。-->
    var isListening = getListeningForDocument(mountAt);
    <!--定義咱們寫入的事件與top事件的對應。 onAbort:['topAbort'],dependencies對應的是['topAbort']-->
    <!--這裏的top事件就是EventConstants模塊中定義的,這些top事件是咱們原始的事件類型,在頂層的一種表示方法。只是換了個名字而已。就像是click事件,咱們綁定的時候是寫做onClick的,可是,React要使用頂層來代理這些事件,因此就寫做了topClick。-->
    var dependencies = EventPluginRegistry.registrationNameDependencies[registrationName];
    <!--EventPluginRegistry模塊的registrationNameDependencies屬性是一個對象,主要是保存了咱們寫做的事件與頂層事件的一一對應關係。就像是
    {
        onAbort:['topAbort'],onClick:['topClick']
    }
    -->
    ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(dependency, topEventMapping[dependency], mountAt);
    <!--這裏的核心調用了trapBubbledEvent方法,也有一些狀況是使用 trapCapturedEvent方法顧名思義,他們是在將事件註冊在不一樣的事件流中(捕獲事件流或者是冒泡事件流),下面咱們以冒泡事件流爲例說明。-->
}
複製代碼

ReactEventListener.trapBubbledEvent

//  @param topLevelType:topAbort,topClick等
     //  @param handlerBaseName:abort,click等
     //  @param handle:一個document
    trapBubbledEvent: function (topLevelType, handlerBaseName, handle) {
    var element = handle;
    if (!element) {
      return null;
    }
    /*
      注意:
      listen方法主要是調用了addEventListener方法。而addEventListener方法接受三個參數。
      //  @param eventType:就是click,abort等。
      //  @param callback :就是事件觸發的時候的回調函數。
      //  @param Boolean值: 表示在何時觸發,冒泡階段,仍是捕獲階段默認是false,冒泡階段。
      而到了這裏,咱們沒有拿到一個回調函數callback,你看給的三個參數是:topLevelType, handlerBaseName, handle

      這裏React給了一個函數 ReactEventListener.dispatchEvent.bind(null, topLevelType)來作回調函數。
      這個回調函數,和咱們綁定在頁面上的回調函數不是一回事。
      咱們在頁面上綁定的回調函數,最終是存儲在了EventPluginHub的listenerBank中了。
    */ 
    <!--核心仍是調用了EventListener模塊的listen方法-->
    return EventListener.listen(element, handlerBaseName, ReactEventListener.dispatchEvent.bind(null, topLevelType));
  },

複製代碼

EventListener.listen方法

<!-- 該方法就是調用了註冊方法的API。抹平了不一樣瀏覽器的差別。在Target上註冊了一個事件類型爲eventType的事件
target是上邊傳過來的element,也就是一個document。
eventType:是一個事件類型,好比click,change等。
callback:咱們以前說過,咱們再組件內寫的方法是沒有註冊在這的。
這裏的callback是React提供的一個分發函數。ReactEventListener.dispatchEvent()。而咱們在組件中寫的方法最終是存儲到了EventPluginHub中。

-->
    listen: function (target, eventType, callback) {
        if (target.addEventListener) {
          target.addEventListener(eventType, callback, false);
          return {
            remove: function () {
              target.removeEventListener(eventType, callback, false);
            }
          };
        } else if (target.attachEvent) {
          target.attachEvent('on' + eventType, callback);
          return {
            remove: function () {
              target.detachEvent('on' + eventType, callback);
            }
          };
        }
  },
複製代碼

基於此,React將咱們寫在組件上的事件,註冊到了document上。那麼咱們說的,咱們定義的事件回調函數自己是沒有被註冊到document上的,而是存儲在了EventPluginHub中。

相關文章
相關標籤/搜索