React事件機制三---事件分發

根據前兩篇文章咱們獲得了事件的註冊與回調函數存儲的流程。事件最終被React註冊到了document上,而註冊到document上的事件的偵聽器是React的提供的一個分發方法。

dispatchEvent

/*
    這裏React爲每一咱們註冊的個事件好比onClick,提供了一個方法,來代替了咱們寫的回調函數。
  */ 
  dispatchEvent: function (topLevelType, nativeEvent) {
  
   <!--
        bookKeeping:是建立了一個對象,這個對象裏包含了一個nativeEvent(源事件對象。)一個當前的top事件。好比topAbort。
        一個數組 ancestor。用來存儲觸發元素的祖先層次結構。
        在初始條件下。ancestor是空數組,並且nativeEvent是沒有賦值的。
   -->
   
    var bookKeeping = TopLevelCallbackBookKeeping.getPooled(topLevelType, nativeEvent);
    
    <!-
        TopLevelCallbackBookKeeping.getPooled() 是用了React的池化技術,這些在隨後的文章中會有,這裏,你只須要理解爲,該方法創造了一個對象,期內包含了一些輔助屬性
    --->

    try {
      
      <!--
         這裏真正重要的是 ReactUpdates.batchedUpdates方法。
      -->
      
      ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
      
      <!--
        這裏 ReactUpdates.batchedUpdates是採用React的批處理策略,來處理參數。
        主要是使用使用bookKeeping做爲handleTopLevelImpl的參數來調用該方法
      -->
    } finally {
    <!--
        使用池化技術釋放了實例以供後續使用。
    -->
      TopLevelCallbackBookKeeping.release(bookKeeping);
    }
  }
複製代碼

解釋

當事件在原事件對象被觸發的時候,document會代理到事件,然後dispatchEvent方法就會被執行。此時事件就被分發了。React採用了批處理的方式來處理。node

核心

ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
    
    <!--
        咱們來講說
        ReactUpdates.batchedUpdates()
        這個方法的做用只是以bookKeeping做爲handleTopLevelImpl的參數,來執行 handleTopLevelImpl 方法。
        
    -->
複製代碼

handleTopLevelImpl 方法

<!--
    handleTopLevelImpl方法最終是執行了下邊這個方法
    path屬性是現代瀏覽器的事件的一個屬性,該屬性包含了當前觸發元素冒泡的全過程。 chrome有,Safari麼有該屬性。
    現代的瀏覽器提供了一個方法來返回當前事件的冒泡全過程DOM。event.composedPath()
-->
function handleTopLevelWithoutPath(bookKeeping) {
  <!--
        getEventTarget方法做用是根據源事件,找到觸發源事件的DOM元素。
        getFirstReactDOM獲得距離觸發事件的源對象最近的dom,通常是其自身,也考慮到觸發事件的多是text_node,那就要向上找到text_node的父元素返回。
  -->
  var topLevelTarget = ReactMount.getFirstReactDOM(getEventTarget(bookKeeping.nativeEvent)) || window;
<!--
    topLevelTarget是觸發事件的當前元素,這裏的方法就是爲了找到最近的一個DOM元素
-->

  while (ancestor) {
    bookKeeping.ancestors.push(ancestor);
    ancestor = findParent(ancestor);
  }
   // ancestors 裏通常是存了當前觸發事件的元素

  for (var i = 0; i < bookKeeping.ancestors.length; i++) {
    topLevelTarget = bookKeeping.ancestors[i];
    <!--
        topLevelTarget 是事件觸發的源元素。
    -->
    var topLevelTargetID = ReactMount.getID(topLevelTarget) || '';
    <!--
        topLevelTargetID源元素的id
    -->
    <!-
        'ReactEventListener._handleTopLevel()方法是核心'
    --->
    ReactEventListener._handleTopLevel(bookKeeping.topLevelType, topLevelTarget, topLevelTargetID, bookKeeping.nativeEvent, getEventTarget(bookKeeping.nativeEvent));
     <!--
        ReactEventListener._handleTopLevel方法最終是調用了
        ReactEventEmitterMixin.handleTopLevel方法。
        而ReactEventEmitterMixin.handleTopLevel方法是調用EventPluginHub.extractEvents方法,生成
        合成事件。
        然後將合成事件放入隊列中,然後一個個的去執行這些事件。
        用到的方法是。
         EventPluginHub.enqueueEvents(events);
        EventPluginHub.processEventQueue(false);
     -->
      
  }
}

複製代碼

ReactEventListener._handleTopLevel()

<!--
        ReactEventListener._handleTopLevel方法最終是調用了 ReactEventEmitterMixin模塊的handleTopLevel方法。
    -->
    
    handleTopLevel: function (topLevelType, topLevelTarget, topLevelTargetID, nativeEvent, nativeEventTarget) {
   
         var events = EventPluginHub.extractEvents(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent, nativeEventTarget);
    <!--
        這裏使用EventPluginHu模塊來選擇不一樣的事件插件來將觸發的事件處理爲合成事件。並將合成事件放入隊列。然後執行這些合成事件。
    -->
    
        runEventQueueInBatch(events);
  }
    
複製代碼

runEventQueueInBatch方法

<!--
    分別調用EventPluginHub模塊的入隊方法和執行隊列方法。
-->
function runEventQueueInBatch(events) {
        EventPluginHub.enqueueEvents(events);
        EventPluginHub.processEventQueue(false);
}
複製代碼

當用戶觸發了某一事件,會調用document代理的某一事件,執行 dispatchEvent 方法。而dispatchEvent方法的最終做用是 根據topLevelType:好比topAbort,topLevelTarget:觸發事件的元素,topLevelTargetID:觸發事件元素的id,nativeEvent:源事件對象,nativeEventTarget:源事件target。生成合成事件放入隊列,然後執行隊列裏的合成事件。

這裏有一個問題,合成事件如何和咱們定義的事件監聽器對應起來。

這裏就要說到合成事件了。chrome

相關文章
相關標籤/搜索