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

相关文章
相关标签/搜索