美文网首页react & vue & angular
react源码解析18事件系统

react源码解析18事件系统

作者: buchila11 | 来源:发表于2021-12-07 08:13 被阅读0次

    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的事件代理机制

    事件系统架构图

    react源码18.1

    我们以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);
      }
      

    相关文章

      网友评论

        本文标题:react源码解析18事件系统

        本文链接:https://www.haomeiwen.com/subject/aleoxrtx.html