react事件机制分为两个部分:1、事件注册 2、事件分发
- 事件注册部分,所有的事件都会注册到document上,拥有统一的回调函数dispatchEvent来执行事件分发
- 事件分发部分,首先生成合成事件,注意同一种事件类型只能生成一个合成事件Event,如onclick这个类型的事件,dom上所有带有通过jsx绑定的onClick的回调函数都会按顺序(冒泡或者捕获)会放到Event._dispatchListeners 这个数组里,后面依次执行它。
事件注册
- ReactDOMComponent在进行组件加载(mountComponent)、更新(updateComponent)的时候,需要对
props
进行处理(_updateDOMProperties):
ReactDOMComponent.Mixin = {
_updateDOMProperties: function (lastProps, nextProps, transaction) {
...
for (propKey in nextProps) {
// 判断是否为事件属性
if (registrationNameModules.hasOwnProperty(propKey)) {
enqueuePutListener(this, propKey, nextProp, transaction);
}
}
}
}
function enqueuePutListener(inst, registrationName, listener, transaction) {
...
var doc = isDocumentFragment ? containerInfo._node : containerInfo._ownerDocument;
// 在document上注册事件,同一个事件类型只会被注册一次
listenTo(registrationName, doc);
// mountReady之后将回调函数存在ListernBank中
transaction.getReactMountReady().enqueue(putListener, {
inst: inst,
registrationName: registrationName,
listener: listener
});
function putListener() {
var listenerToPut = this;
EventPluginHub.putListener(listenerToPut.inst, listenerToPut.registrationName, listenerToPut.listener);
}
}
- enqueuePutListener进行两件事情: 在document上注册相关的事件;对事件进行存储
document的事件注册入口位于ReactBrowserEventEmitter:
// ReactBrowserEventEmitter.js
listenTo: function (registrationName, contentDocumentHandle) {
...
if (...) {
ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(...);
} else if (...) {
ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(...);
}
...
}
// ReactEventListener.js
var ReactEventListener = {
...
trapBubbledEvent: function (topLevelType, handlerBaseName, element) {
...
var handler = ReactEventListener.dispatchEvent.bind(null, topLevelType);
return EventListener.listen(element, handlerBaseName, handler);
},
trapCapturedEvent: function (topLevelType, handlerBaseName, element) {
var handler = ReactEventListener.dispatchEvent.bind(null, topLevelType);
return EventListener.capture(element, handlerBaseName, handler);
}
dispatchEvent: function (topLevelType, nativeEvent) {
...
ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
...
}
}
function handleTopLevelImpl(bookKeeping) {
...
ReactEventListener._handleTopLevel(bookKeeping.topLevelType, targetInst, bookKeeping.nativeEvent, getEventTarget(bookKeeping.nativeEvent));
...
}
- 事件的注册、触发,具体是在ReactEventListener中实现的
- 事件的注册有两个方法: 支持冒泡(trapBubbledEvent)、捕获(trapCapturedEvent)
- document不管注册的是什么事件,具有统一的回调函数- handleTopLevelImpl
- document的回调函数中不包含任何的事物处理,只起到事件分发的作用
事件分发
事件注册完之后,就可以依据事件委托进行事件的执行。由事件注册可以知道,几乎所有的事件均委托到document上,而document上事件的回调函数只有一个: ReactEventListener.dispatchEvent,然后进行相关的分发:
var ReactEventListener = {
dispatchEvent: function (topLevelType, nativeEvent) {
...
ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
...
}
}
function handleTopLevelImpl(bookKeeping) {
var nativeEventTarget = getEventTarget(bookKeeping.nativeEvent);
var targetInst = ReactDOMComponentTree.getClosestInstanceFromNode(nativeEventTarget);
// 初始化时用ReactEventEmitterMixin注入进来的
ReactEventListener._handleTopLevel(..., nativeEventTarget, targetInst);
}
// ReactEventEmitterMixin.js
var ReactEventEmitterMixin = {
handleTopLevel: function (...) {
var events = EventPluginHub.extractEvents(...);
runEventQueueInBatch(events);
}
}
function runEventQueueInBatch(events) {
EventPluginHub.enqueueEvents(events);
EventPluginHub.processEventQueue(false);
}
- handleTopLevelImpl: 根据原生的事件对象,找到事件触发的dom元素以及该dom对应的compoennt对象
- ReactEventEmitterMixin: 一方面生成合成的事件对象,另一方面批量执行定义的回调函数
- runEventQueueInBatch: 进行批量更新