关于事件,相信都不陌生。 咱们再DOM上绑定事件,并给定一个处理函数。在DOM上事件触发的时候,会执行处理函数。node
固然也有另一种事件绑定机制---代理。react
通常咱们再用ul元素的时候,咱们的事件通常是绑定在li的父元素ul上,这样让ul来代理li的事件,这样只用绑定一次便可,而React的事件也是使用的代理模式,可是,React的全部事件都是绑定在document上的。浏览器
/*
@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
});
}
复制代码
1 listenTo方法: ReactBrowserEventEmitter.listenTo主要的做用即是注册事件到document上。bash
2 enqueue方法:主要的做用即是将回调函数存储起来。app
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方法顾名思义,他们是在将事件注册在不一样的事件流中(捕获事件流或者是冒泡事件流),下面咱们以冒泡事件流为例说明。-->
}
复制代码
// @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));
},
复制代码
<!-- 该方法就是调用了注册方法的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);
}
};
}
},
复制代码