react 源码解析 18 事件系统
- 2021 年 12 月 28 日
本文字数:3743 字
阅读完需:约 12 分钟
react 源码解析 18 事件系统
视频讲解(高效学习):进入学习
从一个 bug 说起
下面这个 demo_13 在 react17 和 react16 中有什么不同吗?代码也很简单,模拟一个 modal 框,点击显示出现,点击其他地方,相当于点击了 mask,modal 消失,因为 react 事件都是委托到上层,所以需要在 handleClick 阻止冒泡,这样点击显示的时候不会触发 document 上的事件回调,导致 modal 无法显示。但是在 react16 上发现这样做还是不行,需要调用 e.nativeEvent.stopImmediatePropagation()才能实现,而 react17 上没什么影响
究其原因就是 react16 和 17 在委托事件的容器上做出了改变,react16 的事件会冒泡的 document 上,而 17 则会冒泡到 root 容器上,也就是 ReactDom.render 的第二个参数
export default class Demo13 extends React.Component {
state = { show: false };
componentDidMount() {
document.addEventListener("click", () => {
this.setState({ show: false });
});
}
handleClick = (e) => {
e.stopPropagation();//react17中生效
// e.nativeEvent.stopImmediatePropagation(); //react16中生效 stopImmediatePropagation也阻止本级监听函数执行
this.setState({ show: true });
};
render() {
return (
<div>
<button onClick={this.handleClick}>显示</button>
{this.state.show && <div onClick={(e) => e.nativeEvent.stopImmediatePropagation()}>modal</div>}
</div>
);
}
}
大家也可以看下 demo_11、demo_12 在 react16、17 触发顺序有何差异,同时 demo 项目中的 event.html 也模拟了 react16、17 的事件代理机制
事件系统架构图
我们以 SimpleEvent 为例看事件注册、绑定和触发的过程,看视频的调试过程
事件注册
DOMPluginEventSystem.js 会调用 SimpleEventPlugin 插件的 registerEvents 方法注册事件
//DOMPluginEventSystem.js
SimpleEventPlugin.registerEvents();
registerSimpleEvents
function registerSimpleEvents() {
registerSimplePluginEventsAndSetTheirPriorities(discreteEventPairsForSimpleEventPlugin, DiscreteEvent);
//...
}
function registerSimplePluginEventsAndSetTheirPriorities(eventTypes, priority) {
for (var i = 0; i < eventTypes.length; i += 2) {
var topEvent = eventTypes[i];
var event = eventTypes[i + 1];
var capitalizedEvent = event[0].toUpperCase() + event.slice(1);
var reactName = 'on' + capitalizedEvent;
eventPriorities.set(topEvent, priority);
topLevelEventsToReactNames.set(topEvent, reactName);
registerTwoPhaseEvent(reactName, [topEvent]);//注册捕获和冒泡两个阶段的事件
}
}
registerTwoPhaseEvent
function registerTwoPhaseEvent(registrationName, dependencies) {
registerDirectEvent(registrationName, dependencies);
registerDirectEvent(registrationName + 'Capture', dependencies);
}
registerDirectEvent
function registerDirectEvent(registrationName, dependencies) {
//...
for (var i = 0; i < dependencies.length; i++) {
allNativeEvents.add(dependencies[i]);//生成allNativeEvents对象
}
}
事件绑定
listenToAllSupportedEvents
//由函数createRootImpl调用,也就是在创建根节点之后执行
function listenToAllSupportedEvents(rootContainerElement) {
allNativeEvents.forEach(function (domEventName) {
if (!nonDelegatedEvents.has(domEventName)) {
listenToNativeEvent(domEventName, false, rootContainerElement, null);
}
listenToNativeEvent(domEventName, true, rootContainerElement, null);
});
}
}
listenToNativeEvent
function listenToNativeEvent(domEventName, isCapturePhaseListener, rootContainerElement, targetElement) {
//...
if (!listenerSet.has(listenerSetKey)) {
if (isCapturePhaseListener) {
eventSystemFlags |= IS_CAPTURE_PHASE;
}
addTrappedEventListener(target, domEventName, eventSystemFlags, isCapturePhaseListener);
listenerSet.add(listenerSetKey);
}
}
addTrappedEventListener
function addTrappedEventListener(targetContainer, domEventName, eventSystemFlags, isCapturePhaseListener, isDeferredListenerForLegacyFBSupport) {
//创建具有优先级的监听函数
var listener = createEventListenerWrapperWithPriority(targetContainer, domEventName, eventSystemFlags);
//...
targetContainer = targetContainer;
var unsubscribeListener;
if (isCapturePhaseListener) {//节点上添加事件
if (isPassiveListener !== undefined) {
unsubscribeListener = addEventCaptureListenerWithPassiveFlag(targetContainer, domEventName, listener, isPassiveListener);
} else {
unsubscribeListener = addEventCaptureListener(targetContainer, domEventName, listener);
}
} else {
if (isPassiveListener !== undefined) {
unsubscribeListener = addEventBubbleListenerWithPassiveFlag(targetContainer, domEventName, listener, isPassiveListener);
} else {
unsubscribeListener = addEventBubbleListener(targetContainer, domEventName, listener);
}
}
}
createEventListenerWrapperWithPriority
function createEventListenerWrapperWithPriority(targetContainer, domEventName, eventSystemFlags) {
var eventPriority = getEventPriorityForPluginSystem(domEventName);
var listenerWrapper;
switch (eventPriority) {
case DiscreteEvent:
listenerWrapper = dispatchDiscreteEvent;
break;
case UserBlockingEvent:
listenerWrapper = dispatchUserBlockingUpdate;
break;
case ContinuousEvent:
default:
listenerWrapper = dispatchEvent;
break;
}
//绑定dispatchDiscreteEvent
return listenerWrapper.bind(null, domEventName, eventSystemFlags, targetContainer);
}
事件触发
dispatchDiscreteEvent(dispatchEvent)
function dispatchDiscreteEvent(domEventName, eventSystemFlags, container, nativeEvent) {
{
flushDiscreteUpdatesIfNeeded(nativeEvent.timeStamp);
}
discreteUpdates(dispatchEvent, domEventName, eventSystemFlags, container, nativeEvent);
}
unstable_runWithPriority
function unstable_runWithPriority(priorityLevel, eventHandler) {//eventHandler就是dispatchEvent函数
switch (priorityLevel) {
case ImmediatePriority:
case UserBlockingPriority:
case NormalPriority:
case LowPriority:
case IdlePriority:
break;
default:
priorityLevel = NormalPriority;
}
var previousPriorityLevel = currentPriorityLevel;
currentPriorityLevel = priorityLevel;
try {
return eventHandler();
} finally {
currentPriorityLevel = previousPriorityLevel;
}
}
batchedEventUpdates
function batchedEventUpdates(fn, a, b) {
if (isBatchingEventUpdates) {
return fn(a, b);
}
isBatchingEventUpdates = true;
try {
return batchedEventUpdatesImpl(fn, a, b);
//fn参数即:
//function () {
// return dispatchEventsForPlugins(domEventName, eventSystemFlags, nativeEvent, //ancestorInst);
//}
function () {
return dispatchEventsForPlugins(domEventName, eventSystemFlags, nativeEvent, ancestorInst);
}
} finally {
isBatchingEventUpdates = false;
finishEventHandler();
}
}
dispatchEventsForPlugins
function dispatchEventsForPlugins(domEventName, eventSystemFlags, nativeEvent, targetInst, targetContainer) {
var nativeEventTarget = getEventTarget(nativeEvent);
var dispatchQueue = [];
//extractEvent生成SyntheticEvent
extractEvents(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags);
//processDispatchQueue执行形成事件队列
processDispatchQueue(dispatchQueue, eventSystemFlags);
}
zz1998
还未添加个人签名 2021.06.26 加入
还未添加个人简介
评论