创建顶层事件回调

每次执行React.renderComponent时,React框架都会尝试创建顶层事件回调。如果此时尚未创建顶层事件回调,React框架就是调用listenAtTopLevel方法,开启顶层事件监听。

几乎所有的顶层监听都注册在document上,通过trapBubbledEvent依次注册下表中的事件

  • mouseover
  • mousedown
  • mouseup
  • mousemove
  • mouseout
  • click
  • dbclick
  • mousewheel
  • keyup
  • keypress
  • keydown
  • change
  • DOMCharacterDataModified
  • DOMMouseScroll
  • scroll,如果document支持就注册在document上,否则注册在window上
  • focus/blur或者focusin/focusout 若当前环境支持触摸(touch)事件,还会额外注册4个跟touch相关的监听
  • touchstart
  • touchend
  • touchmove
  • touchcancel 相关代码如下

    function listenAtTopLevel(touchNotMouse) {
    invariant(
      !_isListening,
      'listenAtTopLevel(...): Cannot setup top-level listener more than once.'
    );
    var mountAt = document;
    
    registerDocumentScrollListener();
    registerDocumentResizeListener();
    trapBubbledEvent(topLevelTypes.topMouseOver, 'mouseover', mountAt);
    trapBubbledEvent(topLevelTypes.topMouseDown, 'mousedown', mountAt);
    trapBubbledEvent(topLevelTypes.topMouseUp, 'mouseup', mountAt);
    trapBubbledEvent(topLevelTypes.topMouseMove, 'mousemove', mountAt);
    trapBubbledEvent(topLevelTypes.topMouseOut, 'mouseout', mountAt);
    trapBubbledEvent(topLevelTypes.topClick, 'click', mountAt);
    trapBubbledEvent(topLevelTypes.topDoubleClick, 'dblclick', mountAt);
    trapBubbledEvent(topLevelTypes.topMouseWheel, 'mousewheel', mountAt);
    if (touchNotMouse) {
      trapBubbledEvent(topLevelTypes.topTouchStart, 'touchstart', mountAt);
      trapBubbledEvent(topLevelTypes.topTouchEnd, 'touchend', mountAt);
      trapBubbledEvent(topLevelTypes.topTouchMove, 'touchmove', mountAt);
      trapBubbledEvent(topLevelTypes.topTouchCancel, 'touchcancel', mountAt);
    }
    trapBubbledEvent(topLevelTypes.topKeyUp, 'keyup', mountAt);
    trapBubbledEvent(topLevelTypes.topKeyPress, 'keypress', mountAt);
    trapBubbledEvent(topLevelTypes.topKeyDown, 'keydown', mountAt);
    trapBubbledEvent(topLevelTypes.topChange, 'change', mountAt);
    trapBubbledEvent(
      topLevelTypes.topDOMCharacterDataModified,
      'DOMCharacterDataModified',
      mountAt
    );
    
    // Firefox needs to capture a different mouse scroll event.
    // @see http://www.quirksmode.org/dom/events/tests/scroll.html
    trapBubbledEvent(topLevelTypes.topMouseWheel, 'DOMMouseScroll', mountAt);
    // IE < 9 doesn't support capturing so just trap the bubbled event there.
    if (isEventSupported('scroll', true)) {
      trapCapturedEvent(topLevelTypes.topScroll, 'scroll', mountAt);
    } else {
      trapBubbledEvent(topLevelTypes.topScroll, 'scroll', window);
    }
    
    if (isEventSupported('focus', true)) {
      trapCapturedEvent(topLevelTypes.topFocus, 'focus', mountAt);
      trapCapturedEvent(topLevelTypes.topBlur, 'blur', mountAt);
    } else if (isEventSupported('focusin')) {
      // IE has `focusin` and `focusout` events which bubble.
      // @see http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
      trapBubbledEvent(topLevelTypes.topFocus, 'focusin', mountAt);
      trapBubbledEvent(topLevelTypes.topBlur, 'focusout', mountAt);
    }
    }
    

    而trapBubbledEvent的内容则十分简单

    function trapBubbledEvent(topLevelType, handlerBaseName, onWhat) {
    listen(
      onWhat,
      handlerBaseName,
      ReactEvent.TopLevelCallbackCreator.createTopLevelCallback(topLevelType)
    );
    }
    

    Listen函数由src/event/NormalizedEventListener.js提供,这个函数所做的,就是将TopLevelCallbackCreator生成的回调函数外面再包上一层规范化的处理回调,以保证传递给顶层函数回调的事件是跨浏览器兼容的

    listen: function(el, handlerBaseName, cb) {
      EventListener.listen(el, handlerBaseName, createNormalizedCallback(cb));
    },
    

    createNormalizedCallback其实也不复杂,其实现如下。 ```javascript function normalizeEvent(eventParam) { var normalized = eventParam || window.event; var hasTargetProperty = 'target' in normalized; var eventTarget = normalized.target || normalized.srcElement || window; var textNodeNormalizedTarget = (eventTarget.nodeType === 3) ? eventTarget.parentNode : eventTarget; if (!hasTargetProperty || normalized.target !== textNodeNormalizedTarget) { normalized = Object.create(normalized); normalized.target = textNodeNormalizedTarget; } return normalized; }

function createNormalizedCallback(cb) { return function(unfixedNativeEvent) { cb(normalizeEvent(unfixedNativeEvent)); }; }


而在NormalizedEventListener中调用的EventListener.listen函数是由src/vendor/stubs/EventListener.js提供的跨浏览器事件绑定功能,一般都是绑定在document上,而事件的相应函数,则是由TopLevelCallbackCreator创建。

ReactEventTopLevelCallback.js中定义了creatTopLevelCallback函数,它以一个函数作为其返回值。这个实际的回调函数,负责根据规范化后的事件,根据事件的target查找到对应的React组件实例,并调用ReatEvent.handleTopLevel进行实际的事件处理。
```javascript
  createTopLevelCallback: function(topLevelType) {
    return function(fixedNativeEvent) {
      if (!_topLevelListenersEnabled) {
        return;
      }
      var renderedTarget = ReactInstanceHandles.getFirstReactDOM(
        fixedNativeEvent.target
      ) || ExecutionEnvironment.global;
      var renderedTargetID = getDOMNodeID(renderedTarget);
      var event = fixedNativeEvent;
      var target = renderedTarget;
      ReactEvent.handleTopLevel(topLevelType, event, renderedTargetID, target);
    };
  }

results matching ""

    No results matching ""