搜索

查看: 3119|回复: 11

[JavaScript] React中的合成事件是什么原理

[复制链接]
发表于 2023-5-4 11:45:32 | 显示全部楼层 |阅读模式
Editor 2023-5-4 11:45:32 3119 11 看全部
目录
  • 事件介绍
  • 什么是事件
  • React合成事件基础知识
  • 什么是合成事件
  • 为什么会有合成事件
  • 合成事件机制简述
  • React合成事件实现原理
  • 事件注册
  • React中模拟冒泡和捕获
    事件介绍
    什么是事件
    事件是在编程时系统内发生的动作或者发生的事情,而开发者可以某种方式对事件做出回应,而这里有几个先决条件
  • 事件对象
  • 给事件对象注册事件,当事件被触发后需要做什么
  • 事件触发
    举个例子
    在机场等待检票,听到广播后排队检票
  • 事件对象,也就是检票事件
  • 事件注册/监听,也就是我们在机场等待的时候
  • 事件触发,广播响起开始检票
  • 事件触发之后的回调,我们开始排队...
    // 检票事件
    var event = new Event('check-ticket');
    // 事件注册,并编写回调
    elem.addEventListener('check-ticket', function (e) {
      // 开始排队
      LiningUp()
    });
    // 事件触发,广播开始检票
    elem.dispatchEvent(event);
    React合成事件基础知识
    什么是合成事件
    不是浏览器本身触发的事件,自己创建和触发的事件。
    在React中事件的写法和原生事件写法的区别?
  • React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
  • 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
    // HTML中的写法

      Activate Lasers

    // React中的写法

      Activate Lasers

    为什么会有合成事件
  • 对事件进行归类,可以在事件产生的任务上包含不同的优先级
  • 提供合成事件对象,抹平浏览器的兼容性差异
    合成事件机制简述
    提供了一种“顶层注册,事件收集,统一触发”的事件机制
  • “顶层注册”,其实是在root元素上绑定一个统一的事件处理函数
  • “事件收集”, 事件触发时(实际上是root上的事件处理函数被执行),构造合成事件对象,按照冒泡或捕获的路径去组件中收集真正的事件处理函数
  • “统一触发”,在收集过程之后,对收集的事件逐一执行,并共享同一个合成事件对象
    React合成事件实现原理
    事件注册
    时机:在fiber节点进入render阶段的complete阶段时,名为onClick的props会被识别为事件进行处理
    function setInitialDOMProperties(
      tag: string,
      domElement: Element,
      rootContainerElement: Element | Document,
      nextProps: Object,
      isCustomComponentTag: boolean,
    ): void {
      for (const propKey in nextProps) {
        if (!nextProps.hasOwnProperty(propKey)) {
          ...
        } else if (registrationNameDependencies.hasOwnProperty(propKey)) {
            // 如果propKey属于事件类型,则进行事件绑定
            ensureListeningTo(rootContainerElement, propKey, domElement);
          }
        }
      }
    }
    // registrationNameDependencies为合成事件名和真实事件的映射,格式如下:
    {
      onChange: ['change', 'click', 'focusin', 'focusout', 'input', 'keydown', 'keyup', 'selectionchange']
      onChangeCapture: ['change', 'click', 'focusin', 'focusout', 'input', 'keydown', 'keyup', 'selectionchange']
      onClick: ['click']
    }
    调用ensureListeningTo进行事件绑定
    然后调用DOMPluginEventSystem.js中的addTrappedEventListener进行事件注册,而监听器listener是React通过createEventListenerWrapperWithPriority创建出优先级不同的时间监听包装器
    总的来说,会有三种事件监听包装器:
  • dispatchDiscreteEvent: 处理离散事件
  • dispatchUserBlockingUpdate:处理用户阻塞事件
  • dispatchEvent:处理连续事件
    事件触发-事件监听器做了什么
    负责以不同的优先级权重来触发真正的事件流程
    在createEventListenerWrapperWithPriority中,根据eventPriority事件优先级不同调用事件dispatchDiscreteEvent、dispatchUserBlockingUpdate、dispatchUserBlockingUpdate、dispatchEvent进行事件绑定
    export function createEventListenerWrapperWithPriority(
      targetContainer: EventTarget,
      domEventName: DOMEventName,
      eventSystemFlags: EventSystemFlags,
    ): Function {
      const eventPriority = getEventPriorityForPluginSystem(domEventName);
      let listenerWrapper;
      switch (eventPriority) {
        case DiscreteEvent:
          listenerWrapper = dispatchDiscreteEvent;
          break;
        case UserBlockingEvent:
          listenerWrapper = dispatchUserBlockingUpdate;
          break;
        case ContinuousEvent:
        default:
          listenerWrapper = dispatchEvent;
          break;
      }
      return listenerWrapper.bind(
        null,
        domEventName,
        eventSystemFlags,
        targetContainer,
      );
    }
    在每一个事件绑定函数中,会调用dispatchEventsForPlugins函数
    合成事件,放入dispatchQueue中
    而dispatchQueue由两部分组成
  • 合成事件对象
  • 多个listeners(可以共用一个合成事件对象)
    执行事件执行路径下的事件,从dispatchQueue中取出事件对象events和具体执行路径listeners,然后遍历执行
    function dispatchEventsForPlugins(
      domEventName: DOMEventName,
      eventSystemFlags: EventSystemFlags,
      nativeEvent: AnyNativeEvent,
      targetInst: null | Fiber,
      targetContainer: EventTarget,
    ): void {
      const nativeEventTarget = getEventTarget(nativeEvent);
      const dispatchQueue: DispatchQueue = [];
      // 通过不同事件类型插件进行事件对象合成,将合成事件放入事件队列中:dispatchQueue中
      extractEvents(
        dispatchQueue,
        domEventName,
        targetInst,
        nativeEvent,
        nativeEventTarget,
        eventSystemFlags,
        targetContainer,
      );
      // 执行dispatchQueue中具体事件执行路径下的事件
      processDispatchQueue(dispatchQueue, eventSystemFlags);
    }
    // 通过extractEvents合成的dispatchQueue的格式如下
    [
      {
        // event是合成事件对象
        event,
        // 具体的事件执行路径,currentTarget、listener等信息
        listeners: [
          {
            currentTarget: div.counter,
            instance: FiberNode {tag: 5, key: null, elementType: 'div', type: 'div', stateNode: div.counter, …},
            listener: e => {…}
          },
          {
            currentTarget: div.counter-parent,
            instance: FiberNode {tag: 5, key: null, elementType: 'div', type: 'div', stateNode: div.counter, …},
            listener: e => {…}
          }
        ]
      }
    ]
    React中模拟冒泡和捕获
    原理:收集的事件放在dispatchQueue数组中,而冒泡和捕获的区别在于执行时机和顺序,那么我们只需要对数组按照不同顺序循环执行即可
    function processDispatchQueueItemsInOrder(
      event: ReactSyntheticEvent,
      dispatchListeners: Array,
      inCapturePhase: boolean,
    ): void {
      let previousInstance;

      if (inCapturePhase) {
        // 事件捕获倒序循环
        for (let i = dispatchListeners.length - 1; i >= 0; i--) {
          const {instance, currentTarget, listener} = dispatchListeners;
          if (instance !== previousInstance && event.isPropagationStopped()) {
            return;
          }
          // 执行事件,传入event对象,和currentTarget
          executeDispatch(event, listener, currentTarget);
          previousInstance = instance;
        }
      } else {
        // 事件冒泡正序循环
        for (let i = 0; i
    到此这篇关于React中的合成事件是什么原理的文章就介绍到这了,更多相关React合成事件内容请搜索知鸟论坛以前的文章或继续浏览下面的相关文章希望大家以后多多支持知鸟论坛
  • 回复

    使用道具 举报

    发表于 2023-6-28 23:51:20 | 显示全部楼层
    哈哈SE7 2023-6-28 23:51:20 看全部
    这个帖子不回对不起自己!我想我是一天也不能离开知鸟论坛
    回复

    使用道具 举报

    发表于 2023-6-29 21:34:31 | 显示全部楼层
    伊索谗言 2023-6-29 21:34:31 看全部
    我看不错噢 谢谢楼主!知鸟论坛越来越好!
    回复

    使用道具 举报

    发表于 2023-6-30 02:04:23 | 显示全部楼层
    462710480 2023-6-30 02:04:23 看全部
    楼主太厉害了!楼主,I*老*虎*U!我觉得知鸟论坛真是个好地方!
    回复

    使用道具 举报

    发表于 2023-6-30 03:44:46 | 显示全部楼层
    风吹吹蛋蛋疼风w 2023-6-30 03:44:46 看全部
    既然你诚信诚意的推荐了,那我就勉为其难的看看吧!知鸟论坛不走平凡路。
    回复

    使用道具 举报

    发表于 2023-6-30 10:57:34 | 显示全部楼层
    术数古籍专卖疤 2023-6-30 10:57:34 看全部
    楼主发贴辛苦了,谢谢楼主分享!我觉得知鸟论坛是注册对了!
    回复

    使用道具 举报

    发表于 2023-7-4 04:57:43 | 显示全部楼层
    丁侦球 2023-7-4 04:57:43 看全部
    楼主,我太崇拜你了!我想我是一天也不能离开知鸟论坛
    回复

    使用道具 举报

    发表于 2023-7-5 07:05:54 | 显示全部楼层
    Gordon520 2023-7-5 07:05:54 看全部
    这东西我收了!谢谢楼主!知鸟论坛真好!
    回复

    使用道具 举报

    发表于 2023-7-5 22:49:58 | 显示全部楼层
    向往草原403 2023-7-5 22:49:58 看全部
    楼主,我太崇拜你了!我想我是一天也不能离开知鸟论坛
    回复

    使用道具 举报

    发表于 2023-7-6 01:29:59 | 显示全部楼层
    普通人物怨 2023-7-6 01:29:59 看全部
    楼主,大恩不言谢了!知鸟论坛是最棒的!
    回复

    使用道具 举报

    • 您可能感兴趣
    点击右侧快捷回复 【请勿灌水】
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则 返回列表

    RSS订阅| SiteMap| 小黑屋| 知鸟论坛
    联系邮箱E-mail:zniao@foxmail.com
    快速回复 返回顶部 返回列表