紧接上文事件分发
从上文我们知道,我们绑定的事件侦听器(就是回调函数比如jsx中onClick中花括号内的函数。)本身是存储在EventPluginHub模块的listenerBank中的。我们在jsx中注册的事件是被document代理的,而document代理的事件的侦听器是React自定义的事件分发方法。如下:
ReactEventListener 模块
<!--
这里React为每一我们注册的个事件比如onClick,提供了一个方法,来代替了我们写的回调函数。
-->
dispatchEvent: function (topLevelType, nativeEvent) {
<!--
创建了一个对象,这个对象里包含了一个nativeEvent(源事件对象)一个当前的top事件。比如topAbort。
一个数组 ancestor。用来存储触发元素的祖先层次结构。
在初始条件下。ancestor是空数组,而且nativeEvent是没有赋值的。
这个对象的作用就是用来存储的。为每一次的事件触发,存储一些内容。
bookKeeping是 TopLevelCallbackBookKeeping 采用React的池化技术生产的实例。
实例主要包含内容为
{
this.topLevelType = topLevelType; 触发的顶层事件,比如topClick。是原生事件在document上的表现形式。onClick==>topClick
this.nativeEvent = nativeEvent; 原生事件对象。触发事件的那个元素的event对象。
this.ancestors = [];用来存储祖先的层次结构
}
只有记得这个bookKeeping对象里存了一个触发事件的顶级事件名称,一个触发事件的源元素的event对象,一个空数组就够了。
-->
var bookKeeping = TopLevelCallbackBookKeeping.getPooled(topLevelType, nativeEvent);
try {
<!--
ReactUpdates.batchedUpdates是 使用ReactDefaultBatchingStrategy模块的批处理策略。
batchedUpdates
-->
ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
<!--
这里 ReactUpdates.batchedUpdates 是采用React的批处理策略,来处理参数。
主要是使用使用bookKeeping作为 handleTopLevelImpl方法 的参数来调用该方法
-->
} finally {
TopLevelCallbackBookKeeping.release(bookKeeping);
}
}
ReactEventListener模块的dispatchEvent方法的核心是调用了ReactUpdates.batchedUpdates 方法。
ReactUpdates模块的 batchedUpdates方法
'ReactUpdates的batchedUpdates方法是调用了 batchingStrategy 的batchedUpdates方法,如下:'
function batchedUpdates(callback, a, b, c, d, e) {
ensureInjected();
batchingStrategy.batchedUpdates(callback, a, b, c, d, e);
}
'而batchingStrategy 模块是使用了注入机制注入进来的,如下 '
var ReactUpdatesInjection = {
c'c'c'c: function (_batchingStrategy) {
batchingStrategy = _batchingStrategy;
}
};
'injectBatchingStrategy 方法是在ReactDefaultInjection模块中通过如下的方式被调用,而后注入了进来ReactDefaultBatchingStrategy模块'
ReactInjection.Updates.injectBatchingStrategy(ReactDefaultBatchingStrategy);
'看一下注入的 ReactDefaultBatchingStrategy '
var ReactDefaultBatchingStrategy = {
<!--
这个变量用来表示当期是否处于批处理状态
-->
isBatchingUpdates: false,
<!--
batchedUpdates方法的作用是以给的参数调用了callback。
-->
batchedUpdates: function (callback, a, b, c, d, e) {
var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
ReactDefaultBatchingStrategy.isBatchingUpdates = true;
if (alreadyBatchingUpdates) {
callback(a, b, c, d, e);
} else {
transaction.perform(callback, null, a, b, c, d, e);
}
}
如上 ReactUpdates.batchedUpdates方法接收两个参数,一个是函数,一个是参数。而该方法最终的作用是是以传入的参数作为传入的方法的参数,执行这个方法。核心是使用了React的批处理策略。关于React的批处理策略我们以后会说,现在我们继续往下:
handleTopLevelImpl 函数
'handleTopLevelImpl方法只是简单的调用了handleTopLevelWithoutPath方法'
function handleTopLevelImpl(bookKeeping) {
handleTopLevelWithoutPath (bookKeeping);
}
'看一下handleTopLevelWithoutPath方法'
function handleTopLevelWithoutPath(bookKeeping) {
<!--
getEventTarget方法作用是根据源事件,找到触发源事件的DOM元素。
getFirstReactDOM得到距离触发事件的源对象最近的dom,一般是其自身,也考虑到触发事件的可能是text_node
-->
var topLevelTarget = ReactMount.getFirstReactDOM(getEventTarget(bookKeeping.nativeEvent)) || window;
<!--
topLevelTarget是触发事件的当前元素,这里的方法就是为了找到最近的一个DOM元素,
-->
<!--
这里的遍历就是为了防止有嵌套层次。比如。我们有一个根元素是
<div id= "app"></div>这个元素,我们这个遍历的作用是保证不会再嵌套一个元素 <div id="app1"></div>作为根元素的根元素。担心可能是多页面应用。
因为事件可能会改变DOM所以,这里要存储一下源事件DOM。
-->
var ancestor = topLevelTarget;
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(
bookKeeping.topLevelType,
topLevelTarget,
topLevelTargetID,
bookKeeping.nativeEvent,
getEventTarget(bookKeeping.nativeEvent)
);
}
handleTopLevelImpl最终是调用了如下这个方法
ReactEventListener._handleTopLevel(
bookKeeping.topLevelType,
topLevelTarget,
topLevelTargetID,
bookKeeping.nativeEvent,
getEventTarget(bookKeeping.nativeEvent)
);
<!--
bookKeeping.topLevelType:存储document触发的时候的事件类型,比如topAbort。
topLevelTarget:就是触发事件的原始元素,一般是一个元素,如果触发元素的是一个text_node的话,会找到text_node的父元素,这个参数一定会得到一个dom元素的,他和getEventTarget(bookKeeping.nativeEvent)是一样的。
topLevelTargetID:原始元素的id
bookKeeping.nativeEvent:原始事件对象。
getEventTarget(bookKeeping.nativeEvent):触发事件的那个DOM元素,可能会是text_node。这个返回的是text_node的父元素
-->
理一遍:我们先使用ReactUpdates.batchedUpdates()批处理策略来处理方法 handleTopLevelImpl,方法handleTopLevelImpl 调用了handleTopLevelWithoutPath方法。
handleTopLevelWithoutPath方法的作用到目前为止是确认不会有嵌套存在,而后调用了如下的方法:
ReactEventListener._handleTopLevel(
bookKeeping.topLevelType,
topLevelTarget,
topLevelTargetID,
bookKeeping.nativeEvent,
getEventTarget(bookKeeping.nativeEvent)
);