react源码解析18事件零碎

视频解说(高效学习):进入学习

往期文章:

1.开篇介绍和面试题

2.react的设计理念

3.react源码架构

4.源码目录构造和调试

5.jsx&外围api

6.legacy和concurrent模式入口函数

7.Fiber架构

8.render阶段

9.diff算法

10.commit阶段

11.生命周期

12.状态更新流程

13.hooks源码

14.手写hooks

15.scheduler&Lane

16.concurrent模式

17.context

18事件零碎

19.手写迷你版react

20.总结&第一章的面试题解答

从一个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为例看事件注册、绑定和触发的过程,看视频的调试过程

事件注册

  1. DOMPluginEventSystem.js会调用SimpleEventPlugin插件的registerEvents办法注册事件

    //DOMPluginEventSystem.jsSimpleEventPlugin.registerEvents();
  1. 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]);//注册捕捉和冒泡两个阶段的事件  }}
  1. registerTwoPhaseEvent

    function registerTwoPhaseEvent(registrationName, dependencies) {  registerDirectEvent(registrationName, dependencies);  registerDirectEvent(registrationName + 'Capture', dependencies);}
  1. registerDirectEvent

    function registerDirectEvent(registrationName, dependencies) { //...  for (var i = 0; i < dependencies.length; i++) {    allNativeEvents.add(dependencies[i]);//生成allNativeEvents对象  }}

事件绑定

  1. listenToAllSupportedEvents

    //由函数createRootImpl调用,也就是在创立根节点之后执行function listenToAllSupportedEvents(rootContainerElement) {    allNativeEvents.forEach(function (domEventName) {      if (!nonDelegatedEvents.has(domEventName)) {        listenToNativeEvent(domEventName, false, rootContainerElement, null);      }      listenToNativeEvent(domEventName, true, rootContainerElement, null);    });  }}
  1. 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);  }}
  1. 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);    }  }}
  1. 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);}

事件触发

  1. dispatchDiscreteEvent(dispatchEvent)

    function dispatchDiscreteEvent(domEventName, eventSystemFlags, container, nativeEvent) {  {    flushDiscreteUpdatesIfNeeded(nativeEvent.timeStamp);  }  discreteUpdates(dispatchEvent, domEventName, eventSystemFlags, container, nativeEvent);}
  1. 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;  }}
  1. 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();  }}
  1. 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);}