写点什么

react 源码解析 18 事件系统

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

事件注册

  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);   }
复制代码


用户头像

zz1998

关注

还未添加个人签名 2021.06.26 加入

还未添加个人简介

评论

发布
暂无评论
react源码解析18事件系统