乐趣区

关于react.js:react源码解析18事件系统

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

事件注册

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

    //DOMPluginEventSystem.js
    SimpleEventPlugin.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);
    }
退出移动版