前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >react源码解析18事件系统

react源码解析18事件系统

原创
作者头像
zz1998
发布2021-12-07 08:17:59
4510
发布2021-12-07 08:17:59
举报

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的第二个参数

代码语言:javascript
复制
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方法注册事件
代码语言:javascript
复制
//DOMPluginEventSystem.js
SimpleEventPlugin.registerEvents();
  • registerSimpleEvents
代码语言:javascript
复制
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
代码语言:javascript
复制
function registerTwoPhaseEvent(registrationName, dependencies) {
  registerDirectEvent(registrationName, dependencies);
  registerDirectEvent(registrationName + 'Capture', dependencies);
}
  • registerDirectEvent
代码语言:javascript
复制
function registerDirectEvent(registrationName, dependencies) {
 //...

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

事件绑定

  • listenToAllSupportedEvents
代码语言:javascript
复制
//由函数createRootImpl调用,也就是在创建根节点之后执行
function listenToAllSupportedEvents(rootContainerElement) {
    allNativeEvents.forEach(function (domEventName) {
      if (!nonDelegatedEvents.has(domEventName)) {
        listenToNativeEvent(domEventName, false, rootContainerElement, null);
      }

      listenToNativeEvent(domEventName, true, rootContainerElement, null);
    });
  }
}
  • listenToNativeEvent
代码语言:javascript
复制
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
代码语言:javascript
复制
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
代码语言:javascript
复制
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)
代码语言:javascript
复制
function dispatchDiscreteEvent(domEventName, eventSystemFlags, container, nativeEvent) {
  {
    flushDiscreteUpdatesIfNeeded(nativeEvent.timeStamp);
  }

  discreteUpdates(dispatchEvent, domEventName, eventSystemFlags, container, nativeEvent);
}
  • unstable_runWithPriority
代码语言:javascript
复制
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
代码语言:javascript
复制
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
代码语言:javascript
复制
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);
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • react源码解析18事件系统
    • 视频讲解(高效学习):进入学习
      • 往期文章:
        • 从一个bug说起
          • 事件系统架构图
            • 事件注册
              • 事件绑定
                • 事件触发
                相关产品与服务
                容器服务
                腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档