单位做核酸简讯,上海seo有哪些公司,电子版个人简历模板,南昌企业做网站设计事件分发 之前讲述了事件如何绑定在document上#xff0c;那么具体事件触发的时候是如何分发到具体的监听者呢#xff1f;我们接着上次注册的事件代理看。当我点击update counter按钮时#xff0c;触发注册的click事件代理。 function dispatchInteractiveEvent(topLevelTyp…事件分发 之前讲述了事件如何绑定在document上那么具体事件触发的时候是如何分发到具体的监听者呢我们接着上次注册的事件代理看。当我点击update counter按钮时触发注册的click事件代理。 function dispatchInteractiveEvent(topLevelType, nativeEvent) {interactiveUpdates(dispatchEvent, topLevelType, nativeEvent);
}
function interactiveUpdates(fn, a, b) {return _interactiveUpdatesImpl(fn, a, b);
}
var _interactiveUpdatesImpl function (fn, a, b) {return fn(a, b);
}; topLevelType为clicknativeEvent为真实dom事件对象。看似很多其实就做了一件事: 执行dispatchEvent(topLevelType, nativeEvent)。其实不然_interactiveUpdatesImpl在后面被重新赋值为interactiveUpdates$1完成了一次自我蜕变。 function setBatchingImplementation(batchedUpdatesImpl, interactiveUpdatesImpl, flushInteractiveUpdatesImpl) {_batchedUpdatesImpl batchedUpdatesImpl;_interactiveUpdatesImpl interactiveUpdatesImpl;_flushInteractiveUpdatesImpl flushInteractiveUpdatesImpl;
}function interactiveUpdates$1(fn, a, b) {if (!isBatchingUpdates !isRendering lowestPriorityPendingInteractiveExpirationTime ! NoWork) {performWork(lowestPriorityPendingInteractiveExpirationTime, false);lowestPriorityPendingInteractiveExpirationTime NoWork;}var previousIsBatchingUpdates isBatchingUpdates;isBatchingUpdates true;try {return scheduler.unstable_runWithPriority(scheduler.unstable_UserBlockingPriority, function () {return fn(a, b);});} finally {isBatchingUpdates previousIsBatchingUpdates;if (!isBatchingUpdates !isRendering) {performSyncWork();}}
}setBatchingImplementation(batchedUpdates$1, interactiveUpdates$1, flushInteractiveUpdates$1); 如果有任何等待的交互更新条件满足的情况下会先同步更新然后设置isBatchingUpdates进行scheduler调度。最后同步更新。scheduler的各类优先级如下: unstable_ImmediatePriority: 1
unstable_UserBlockingPriority: 2
unstable_NormalPriority: 3
unstable_LowPriority: 4
unstable_IdlePriority: 5 进入scheduler调度根据优先级计算时间开始执行传入的回调函数。然后调用dispatchEvent最后更新immediate work。flushImmediateWork里的调用关系很复杂最终会调用requestAnimationFrame进行更新这里不进行过多讨论。 function unstable_runWithPriority(priorityLevel, eventHandler) {switch (priorityLevel) {case ImmediatePriority:case UserBlockingPriority:case NormalPriority:case LowPriority:case IdlePriority:break;default:priorityLevel NormalPriority;}var previousPriorityLevel currentPriorityLevel;var previousEventStartTime currentEventStartTime;currentPriorityLevel priorityLevel;currentEventStartTime exports.unstable_now();try {return eventHandler();} finally {currentPriorityLevel previousPriorityLevel;currentEventStartTime previousEventStartTime;flushImmediateWork();}
} 下面看看dispatchEvent的具体执行过程。 function dispatchEvent(topLevelType, nativeEvent) {if (!_enabled) {return;}// 获取事件触发的原始节点var nativeEventTarget getEventTarget(nativeEvent);// 获取原始节点最近的fiber对象通过缓存在dom上的internalInstanceKey属性来寻找如果没找到会往父节点继续寻找。var targetInst getClosestInstanceFromNode(nativeEventTarget);if (targetInst ! null typeof targetInst.tag number !isFiberMounted(targetInst)) {targetInst null;}// 创建对象包含事件名称原始事件目标fiber对象和ancestor(空数组)如果缓存池有则直接取出并根据参数初始化属性。var bookKeeping getTopLevelCallbackBookKeeping(topLevelType, nativeEvent, targetInst);try {// 批处理事件batchedUpdates(handleTopLevel, bookKeeping);} finally {// 释放bookKeeping对象内存并放入对象池缓存releaseTopLevelCallbackBookKeeping(bookKeeping);}
} 接着看batchedUpdates其实就是设置isBatching变量然后调用handleTopLevel(bookkeeping)。 function batchedUpdates(fn, bookkeeping) {if (isBatching) {return fn(bookkeeping);}isBatching true;try {// _batchedUpdatesImpl其实指向batchedUpdates$1函数具体细节这里不再赘述return _batchedUpdatesImpl(fn, bookkeeping);} finally {isBatching false;var controlledComponentsHavePendingUpdates needsStateRestore();if (controlledComponentsHavePendingUpdates) {_flushInteractiveUpdatesImpl();restoreStateIfNeeded();}}
} 所以将原始节点对应最近的fiber缓存在bookKeeping.ancestors中。 function handleTopLevel(bookKeeping) {var targetInst bookKeeping.targetInst;var ancestor targetInst;do {if (!ancestor) {bookKeeping.ancestors.push(ancestor);break;}var root findRootContainerNode(ancestor);if (!root) {break;}bookKeeping.ancestors.push(ancestor);ancestor getClosestInstanceFromNode(root);} while (ancestor);for (var i 0; i bookKeeping.ancestors.length; i) {targetInst bookKeeping.ancestors[i];runExtractedEventsInBatch(bookKeeping.topLevelType, targetInst, bookKeeping.nativeEvent, getEventTarget(bookKeeping.nativeEvent));}
} runExtractedEventsInBatch中调用了两个方法: extractEvents和runEventsInBatch。前者构造合成事件后者批处理合成事件。 function runExtractedEventsInBatch(topLevelType, targetInst, nativeEvent, nativeEventTarget) {var events extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget);runEventsInBatch(events);
} 事件合成 function extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget) {var events null;for (var i 0; i plugins.length; i) {var possiblePlugin plugins[i];if (possiblePlugin) {var extractedEvents possiblePlugin.extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget);if (extractedEvents) {events accumulateInto(events, extractedEvents);}}}return events;
} plugins是所有合成事件集合的数组EventPluginHub初始化的时候完成注入。遍历所有plugins调用其extractEvents方法返回构造的合成事件。accumulateInto函数则把合成事件放入events。本例click事件合适的plugin是SimpleEventPlugin其他plugin得到的extractedEvents都不满足if (extractedEvents)条件。 EventPluginHubInjection.injectEventPluginsByName({SimpleEventPlugin: SimpleEventPlugin,EnterLeaveEventPlugin: EnterLeaveEventPlugin,ChangeEventPlugin: ChangeEventPlugin,SelectEventPlugin: SelectEventPlugin,BeforeInputEventPlugin: BeforeInputEventPlugin,
}); 接下来看看构造合成事件的具体过程这里针对SimpleEventPlugin其他plugin就不一一分析了来看下其extractEvents: extractEvents: function(topLevelType, targetInst, nativeEvent, nativeEventTarget) {var dispatchConfig topLevelEventsToDispatchConfig[topLevelType];if (!dispatchConfig) {return null;}var EventConstructor void 0;switch (topLevelType) {...case TOP_CLICK:...EventConstructor SyntheticMouseEvent;break;... }var event EventConstructor.getPooled(dispatchConfig, targetInst, nativeEvent, nativeEventTarget);accumulateTwoPhaseDispatches(event);return event;} topLevelEventsToDispatchConfig是一个map对象存储着各类事件对应的配置信息。这里获取到click的配置信息然后根据topLevelType选择对应的合成构造函数这里为SyntheticMouseEvent。接着从SyntheticMouseEvent合成事件对象池中获取合成事件。调用EventConstructor.getPooled最终调用的是getPooledEvent。 注意: SyntheticEvent.extend方法中明确写有addEventPoolingTo(Class)所以SyntheticMouseEvent有eventPool、getPooled和release属性。后面会详细介绍SyntheticEvent.extend function addEventPoolingTo(EventConstructor) {EventConstructor.eventPool [];EventConstructor.getPooled getPooledEvent;EventConstructor.release releasePooledEvent;
} 首次触发事件对象池为空所以这里需要新创建。如果不为空则取出一个并初始化。 function getPooledEvent(dispatchConfig, targetInst, nativeEvent, nativeInst) {var EventConstructor this;if (EventConstructor.eventPool.length) {var instance EventConstructor.eventPool.pop();EventConstructor.call(instance, dispatchConfig, targetInst, nativeEvent, nativeInst);return instance;}return new EventConstructor(dispatchConfig, targetInst, nativeEvent, nativeInst);
} 合成事件的属性是由React主动生成的一些属性和原生事件的属性名完全一致使其完全符合W3C标准因此在事件层面上具有跨浏览器兼容性。如果要访问原生对象通过nativeEvent属性即可获取。这里SyntheticMouseEvent由SyntheticUIEvent扩展而来而SyntheticUIEvent由SyntheticEvent扩展而来。 var SyntheticMouseEvent SyntheticUIEvent.extend({...
});var SyntheticUIEvent SyntheticEvent.extend({...
});SyntheticEvent.extend function (Interface) {var Super this;// 原型继承var E function () {};E.prototype Super.prototype;var prototype new E();// 构造继承function Class() {return Super.apply(this, arguments);}_assign(prototype, Class.prototype);Class.prototype prototype;Class.prototype.constructor Class;Class.Interface _assign({}, Super.Interface, Interface);Class.extend Super.extend;addEventPoolingTo(Class);return Class;
};当被new创建时会调用父类SyntheticEvent进行构造。主要是将原生事件上的属性挂载到合成事件上还配置了一些额外属性。 function SyntheticEvent(dispatchConfig, targetInst, nativeEvent, nativeEventTarget) {this.dispatchConfig dispatchConfig;this._targetInst targetInst;this.nativeEvent nativeEvent;...
} 合成事件构造完成后调用accumulateTwoPhaseDispatches。 function accumulateTwoPhaseDispatches(events) {forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
}// 循环处理所有的合成事件
function forEachAccumulated(arr, cb, scope) {if (Array.isArray(arr)) {arr.forEach(cb, scope);} else if (arr) {cb.call(scope, arr);}
}// 检测事件是否具有捕获阶段和冒泡阶段
function accumulateTwoPhaseDispatchesSingle(event) {if (event event.dispatchConfig.phasedRegistrationNames) {traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event);}
}function traverseTwoPhase(inst, fn, arg) {var path [];// 循环遍历当前元素及父元素缓存至pathwhile (inst) {path.push(inst);inst getParent(inst);}var i void 0;// 捕获阶段for (i path.length; i-- 0;) {fn(path[i], captured, arg);}// 冒泡阶段for (i 0; i path.length; i) {fn(path[i], bubbled, arg);}
}function accumulateDirectionalDispatches(inst, phase, event) {// 获取当前阶段对应的事件处理函数var listener listenerAtPhase(inst, event, phase);// 将相关listener和目标fiber挂载到event对应的属性上if (listener) {event._dispatchListeners accumulateInto(event._dispatchListeners, listener);event._dispatchInstances accumulateInto(event._dispatchInstances, inst);}
} 事件执行批处理合成事件 首先将events合并到事件队列之前没有处理完毕的队列也一同合并。如果新的事件队列为空则退出。反之开始循环处理事件队列中每一个event。forEachAccumulated前面有提到过这里不再赘述。 function runEventsInBatch(events) {if (events ! null) {eventQueue accumulateInto(eventQueue, events);}var processingEventQueue eventQueue;eventQueue null;if (!processingEventQueue) {return;}forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel);rethrowCaughtError();
} 接下来看看事件处理executeDispatchesAndRelease方法将事件执行和事件清理分开。 var executeDispatchesAndReleaseTopLevel function (e) {return executeDispatchesAndRelease(e);
};var executeDispatchesAndRelease function (event) {if (event) {// 执行事件executeDispatchesInOrder(event);if (!event.isPersistent()) {// 事件清理将合成事件放入对象池event.constructor.release(event);}}
}; 提取事件的处理函数和对应的fiber调用executeDispatch。 function executeDispatchesInOrder(event) {var dispatchListeners event._dispatchListeners;var dispatchInstances event._dispatchInstances;if (Array.isArray(dispatchListeners)) {for (var i 0; i dispatchListeners.length; i) {if (event.isPropagationStopped()) {break;}executeDispatch(event, dispatchListeners[i], dispatchInstances[i]);}} else if (dispatchListeners) {executeDispatch(event, dispatchListeners, dispatchInstances);}event._dispatchListeners null;event._dispatchInstances null;
} 获取真实dom挂载到event对象上然后开始执行事件。 function executeDispatch(event, listener, inst) {var type event.type || unknown-event;// 获取真实domevent.currentTarget getNodeFromInstance(inst);invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);event.currentTarget null;
} invokeGuardedCallbackAndCatchFirstError下面调用的方法很多最终会来到invokeGuardedCallbackImpl关键就在func.apply(context, funcArgs);这里的func就是listener本例中是handleClick而funcArgs就是合成事件对象。至此事件执行完毕。 var invokeGuardedCallbackImpl function (name, func, context, a, b, c, d, e, f) {var funcArgs Array.prototype.slice.call(arguments, 3);try {func.apply(context, funcArgs);} catch (error) {this.onError(error);}
}; 事件清理 事件执行完之后剩下就是一些清理操作。event.constructor.release(event)相当于releasePooledEvent(event)。由于click对应的是SyntheticMouseEvent所以会放入SyntheticMouseEvent.eventPool中。EVENT_POOL_SIZE固定为10。 function releasePooledEvent(event) {var EventConstructor this;event.destructor();if (EventConstructor.eventPool.length EVENT_POOL_SIZE) {EventConstructor.eventPool.push(event);}
} 这里做了两件事第一手动释放event属性上的内存将属性置为null第二将event放入对象池。至此清理工作完毕。 destructor: function () {...this.dispatchConfig null;this._targetInst null;this.nativeEvent null;this.isDefaultPrevented functionThatReturnsFalse;this.isPropagationStopped functionThatReturnsFalse;this._dispatchListeners null;this._dispatchInstances null;...
} event清理完后还会清理bookKeeping同样也会放入对象池进行缓存。同样CALLBACK_BOOKKEEPING_POOL_SIZE也固定为10。 // callbackBookkeepingPool是react-dom中的全局变量
function releaseTopLevelCallbackBookKeeping(instance) {instance.topLevelType null;instance.nativeEvent null;instance.targetInst null;instance.ancestors.length 0;if (callbackBookkeepingPool.length CALLBACK_BOOKKEEPING_POOL_SIZE) {callbackBookkeepingPool.push(instance);}
} 总结 最后执行performSyncWork。如果执行的事件内调用了this.setState会进行reconciliation和commit。由于事件流的执行是批处理过程同步调用this.setState不会立马更新需等待所有事件执行完成即scheduler调度完后才开始performSyncWork最终才能拿到新的state。如果是setTimeout或者是在dom上另外addEventListener的回调函数中调用this.setState则会立马更新。因为执行回调函数的时候不经过React事件流。 更好的阅读体验在我的github欢迎?提issue。 转载于:https://www.cnblogs.com/raion/p/10598473.html