视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
浅谈React Event实现原理
2020-11-27 22:07:25 责编:小采
文档

runEventQueuelnBatch(events)做了两件事

  1. 把 dispatchListener里面的事件排队push进 eventQueue
  2. 执行 EventPluginHub.processEventQueue(false);

执行的细节如下:

EventPluginHub.js

 // 循环 eventQueue调用
 var executeDispatchesAndReleaseTopLevel = function (e) {
 return executeDispatchesAndRelease(e, false);
 };
 /* 从event._dispatchListener 取出 dispatchlistener,然后dispatch事件,
 * 循环_dispatchListeners,调用executeDispatch
 */
 var executeDispatchesAndRelease = function (event, simulated) {
 if (event) {
 // 在这里dispatch事件
 EventPluginUtils.executeDispatchesInOrder(event, simulated);
 // 释放事件
 if (!event.isPersistent()) {
 event.constructor.release(event);
 }
 }
 };

 enqueueEvents: function (events) {
 if (events) {
 eventQueue = accumulateInto(eventQueue, events);
 }
 },

 /**
 * Dispatches all synthetic events on the event queue.
 *
 * @internal
 */
 processEventQueue: function (simulated) {
 // Set `eventQueue` to null before processing it so that we can tell if more
 // events get enqueued while processing.
 var processingEventQueue = eventQueue;
 eventQueue = null;
 if (simulated) {
 forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseSimulated);
 } else {
 forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel);
 }
 // This would be a good time to rethrow if any of the event fexers threw.
 ReactErrorUtils.rethrowCaughtError();
 },
/**
 * Standard/simple iteration through an event's collected dispatches.
 */
function executeDispatchesInOrder(event, simulated) {
 var dispatchListeners = event._dispatchListeners;
 var dispatchInstances = event._dispatchInstances;

 if (Array.isArray(dispatchListeners)) {
 for (var i = 0; i < dispatchListeners.length; i++) {
 // 由这里可以看出,合成事件的stopPropagation只能阻止react合成事件的冒泡,
 // 因为event._dispatchListeners 只记录了由jsx绑定的绑定的事件,对于原生绑定的是没有记录的
 if (event.isPropagationStopped()) {
 break;
 }
 // Listeners and Instances are two parallel arrays that are always in sync.
 executeDispatch(event, simulated, dispatchListeners[i], dispatchInstances[i]);
 }
 } else if (dispatchListeners) {
 executeDispatch(event, simulated, dispatchListeners, dispatchInstances);
 }
 event._dispatchListeners = null;
 event._dispatchInstances = null;
}
function executeDispatch(event, simulated, listener, inst) {
 var type = event.type || 'unknown-event';
 // 注意这里将事件对应的dom元素绑定到了currentTarget上
 event.currentTarget = EventPluginUtils.getNodeFromInstance(inst);
 if (simulated) {
 ReactErrorUtils.invokeGuardedCallbackWithCatch(type, listener, event);
 } else {
 // 一般都是非模拟的情况,执行invokeGuardedCallback
 ReactErrorUtils.invokeGuardedCallback(type, listener, event);
 }
 event.currentTarget = null;
}

由上面的函数可知,dispatch 合成事件分为两个步骤:

  1. 通过_dispatchListeners里得到所有绑定的回调函数,在通过_dispatchInstances的绑定回调函数的虚拟dom元素
  2. 循环执行_dispatchListeners里所有的回调函数,这里有一个特殊情况,也是react阻止冒泡的原理

其实在 EventPluginHub.js 里主要做了两件事情.

1.从event._dispatchListener 取出 dispatchlistener,然后dispatch事件,
循环_dispatchListeners,调用executeDispatch,然后走到ReactErrorUtils.invokeGuardedCallback;
2.释放 event

上面这个函数最重要的功能就是将事件对应的dom元素绑定到了currentTarget上,

这样我们通过e.currentTarget就可以找到绑定事件的原生dom元素。

下面就是整个执行过程的尾声了:

ReactErrorUtils.js

var fakeNode = document.createElement('react');
ReactErrorUtils.invokeGuardedCallback = function(name, func, a, b) {
 var boundFunc = func.bind(null, a, b);
 var evtType = `react-${name}`;
 fakeNode.addEventListener(evtType, boundFunc, false);
 var evt = document.createEvent('Event');
 evt.initEvent(evtType, false, false);
 fakeNode.dispatchEvent(evt);
 fakeNode.removeEventListener(evtType, boundFunc, false);
 };

由invokeGuardedCallback可知,最后react调用了faked元素的dispatchEvent方法来触发事件,并且触发完毕之后立即移除监听事件。

总的来说,整个click事件被分发的过程就是:

1、用EventPluginHub生成合成事件,这里注意同一事件类型只会生成一个合成事件,里面的_dispatchListeners里储存了同一事件类型的所有回调函数

2、按顺序去执行它

下载本文
显示全文
专题