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中。

相关文章
相关标签/搜索