​​《从事件冒泡到处理:前端事件系统的“隐形逻辑”》

“那天在document见到你的第一眼,我就下定决心要陪你到天荒地老”       

---React

我将从事件从出现到被处理的各个过程来介绍事件机制:

这张图片给我们展示了react事件的各个阶段,我们可以看到有DOM,合成事件层,还有事件处理函数。


我觉得我们如果想要了解事件机制首先要知道的就是事件从注册到执行结束的全过程: 

事件注册与触发流程

React 事件的完整生命周期:

  1. 初始化阶段
    React 在根容器上注册所有支持的事件监听(如 onClick 对应 click 事件)。
  2. 触发原生事件
    用户操作触发原生 DOM 事件(如点击按钮)。
  3. 冒泡到根容器
    事件冒泡到根容器,React 根据 event.target 找到实际触发事件的组件。
  4. 构造合成事件
    创建 SyntheticEvent 对象,并触发组件树中对应的事件回调。
  5. 回调执行
    执行组件中定义的 onClick 等处理函数。
  6. 事件池回收
    事件处理完成后,合成事件对象被释放回事件池。

(在React17以后,移除事件池,合成事件对象不再复用,可直接异步访问事件属性)

事件绑定

React的事件绑定与原生的事件绑定不同,React并不是将click事件绑定到了div的真实DOM上,而是在document处监听了所有的事件,当事件发生并且冒泡到document处的时候,React将事件内容封装并交由真正的处理函数运行。这样的方式不仅仅减少了内存的消耗,还能在组件挂在销毁时统一订阅和移除事件。

并且冒泡到document上的事件也不是原生的浏览器事件;而是react自己合成的合成事件。

所以,如果不想要是事件冒泡的话应该调用event.preventDefault()方法,而不是调用event.stopProppagation()方法。

合成事件

React 将所有原生事件封装为 SyntheticEvent 对象,提供跨浏览器一致性

  • 统一接口:无论浏览器差异(如 IE 与 Chrome),事件对象的行为一致。
  • 事件池(Event Pooling)​
    事件对象会被重用,事件回调执行后,事件属性会被清空。若需异步访问事件属性,需调用 e.persist() 保留事件。
handleClick(e) {
  e.persist(); // 保留事件对象
  setTimeout(() => console.log(e.target), 100);
}

    React事件和普通Html事件的区别

    区别:

    • 对于事件名称命名方式,原生事件为全小写,react 事件采用小驼峰;
    • 对于事件函数处理语法,原生事件为字符串,react 事件为函数;
    • react 事件不能采用 return false 的方式来阻止浏览器的默认行为,而必须要地明确地调用preventDefault()来阻止默认行为。

    合成事件是 react 模拟原生 DOM 事件所有能力的一个事件对象,其优点如下:

    • 兼容所有浏览器,更好的跨平台;
    • 将事件统一存放在一个数组,避免频繁的新增与删除(垃圾回收)。
    • 方便 react 统一管理和事务机制。

    事件的执行顺序为原生事件先执行,合成事件后执行,合成事件会冒泡绑定到 document 上,所以尽量避免原生事件与合成事件混用,如果原生事件阻止冒泡,可能会导致合成事件不执行,因为需要冒泡到document 上合成事件才会执行。

    事件代理

    事件代理(Event Delegation)是一种通过利用事件冒泡机制,将子元素的事件处理委托给父元素统一管理的技术。它在 ​JavaScript 原生开发和 ​React 等框架中广泛应用,主要用于优化性能简化动态元素的事件绑定

    在React底层,主要对合成事件做了两件事:

    • 事件委派: React会把所有的事件绑定到结构的最外层,使用统一的事件监听器,这个事件监听器上维持了一个映射来保存所有组件内部事件监听和处理函数。
    • 自动绑定: React组件中,每个方法的上下文都会指向该组件的实例,即自动绑定this为当前组件。

    一、原生 JavaScript 中的事件代理

    1. 核心原理
    • 事件冒泡:子元素触发的事件会逐级向上传递到父元素。
    • 统一监听:在父元素上绑定事件监听器,通过 event.target 判断实际触发事件的子元素。
    2. 代码示例
    <ul id="parent">
      <li data-id="1">Item 1</li>
      <li data-id="2">Item 2</li>
      <li data-id="3">Item 3</li>
    </ul>
    
    <script>
      document.getElementById('parent').addEventListener('click', function(e) {
        if (e.target.tagName === 'LI') { // 判断触发元素
          const id = e.target.dataset.id;
          console.log('Clicked item:', id);
        }
      });
    </script>
    3. 优势
    • 性能优化:减少事件监听器数量(尤其适用于列表、表格等大量子元素场景)。
    • 动态元素支持:新增子元素无需重新绑定事件(如 AJAX 加载的数据)。
    4. 缺点
    • 事件类型限制:仅适用于冒泡阶段的事件(如 click,不适用于 focus 等无冒泡的事件)。
    • 逻辑复杂度:需要手动判断 event.target,层级较深时可能需递归查找。

    二、React 中的事件代理

    React 通过合成事件(SyntheticEvent)​自动实现了事件代理,开发者无需手动处理。

    1. 实现原理
    • 统一绑定到根节点:所有事件监听器默认绑定在根容器(如 #root)。
    • 自动委托:React 内部通过事件插件机制,根据事件类型动态管理监听。
    2. 代码表现
    function List() {
      const handleClick = (e) => {
        // 直接通过 e.target 获取触发元素(React 已封装跨浏览器兼容)
        console.log('Clicked element:', e.target);
      };
    
      return (
        <ul onClick={handleClick}>
          <li>Item 1</li>
          <li>Item 2</li>
          <li>Item 3</li>
        </ul>
      );
    }
    3. 优势
    • 自动管理:无需手动判断 event.target,组件销毁时自动解绑事件。
    • 性能优化:避免为每个子元素单独绑定监听器,减少内存占用。
    • 跨浏览器一致性:合成事件屏蔽了浏览器差异(如 event.preventDefault() 的行为)。
    4. 注意事项
    • 阻止冒泡:需显式调用 e.stopPropagation(),否则事件会冒泡到 React 根节点。
    • 原生事件冲突:React 事件与原生 addEventListener 混用时,执行顺序可能不符合预期(见下文示例)。

    三、事件代理的常见问题与解决方案

    1. 事件目标判断错误
    • 问题:当子元素内部有嵌套元素时(如 <li><span>Text</span></li>),e.target 可能指向 span 而非 li
    • 解决:递归查找匹配的父元素:
      function findClosestParent(el, selector) {
        while (el && el !== document) {
          if (el.matches(selector)) return el;
          el = el.parentNode;
        }
        return null;
      }
      
      // React 示例
      const handleClick = (e) => {
        const li = findClosestParent(e.target, 'li');
        if (li) console.log('Clicked li:', li.dataset.id);
      };
    2. React 与原生事件混用
    • 执行顺序:原生事件监听器先于 React 合成事件执行。
    • 示例
      useEffect(() => {
        document.addEventListener('click', () => {
          console.log('原生事件');
        });
      }, []);
      
      const handleClick = (e) => {
        e.stopPropagation(); // 无法阻止原生事件触发
        console.log('React 事件');
      };
      输出顺序原生事件 → React 事件
      解决:在原生事件中调用 e.stopImmediatePropagation()

    四、适用场景

    场景原生 JSReact说明
    动态列表/表格✔️✔️新增元素无需重新绑定事件
    高频事件(如 mousemove✔️✔️减少监听器数量以优化性能
    全局事件(如 scroll✔️React 建议使用 useEffect 绑定
    自定义组件通信✔️通过父组件统一管理子组件事件

    end....

    “我们这一路走来一直一帆风顺,可惜功亏一篑” 

    ——《花束般的恋爱》 


    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/984204.html

    如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

    相关文章

    tiktok web登录 分析

    声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 逆向分析 部分代码 response reques…

    【音视频】ffplay常用命令

    一、 ffplay常用命令 -x width&#xff1a;强制显示宽度-y height&#xff1a;强制显示高度 强制以 640*360的宽高显示 ffplay 2.mp4 -x 640 -y 360 效果如下 -fs 全屏显示 ffplay -fs 2.mp4效果如下&#xff1a; -an 禁用音频&#xff08;不播放声音&#xff09;-vn 禁…

    手机屏幕摔不显示了,如何用其他屏幕临时显示,用来导出资料或者清理手机

    首先准备一个拓展坞 然后 插入一个外接的U盘 插入鼠标 插入有数字小键盘区的键盘 然后准备一根高清线&#xff0c;一端链接电脑显示器,一端插入拓展坞 把拓展坞的连接线&#xff0c;插入手机充电口&#xff08;可能会需要转接头&#xff09; 然后确保手机开机 按下键盘…

    基于SpringBoot的“文物管理系统”的设计与实现(源码+数据库+文档+PPT)

    基于SpringBoot的“文物管理系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统总体功能模块图 E-R实体图 系统首页界面 系统…

    微信小程序投票系统的构建与实现

    在数字化时代&#xff0c;微信小程序已经成为人们日常生活的重要组成部分。无论是企业宣传、活动组织还是社交互动&#xff0c;小程序都展现出了其强大的功能和便捷的用户体验。其中&#xff0c;微信小程序的投票系统尤为突出&#xff0c;它不仅能简化投票流程&#xff0c;还能…

    【Java篇】数据类型与变量:窥见程序的天地万象

    文章目录 Java 数据类型与变量基础&#xff1a;从零开始&#xff0c;轻松掌握前言一、字面常量1.1 什么是字面常量&#xff1f;1.2 字面常量的分类1.2.1 字符串常量1.2.2 整形常量1.2.3 浮点数常量1.2.4 字符常量1.2.5 布尔常量1.2.6 空常量 1.3 字面常量与数据类型 二、Java 中…

    绿盟春招面试题

    《网安面试指南》https://mp.weixin.qq.com/s/RIVYDmxI9g_TgGrpbdDKtA?token1860256701&langzh_CN 5000篇网安资料库https://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247486065&idx2&snb30ade8200e842743339d428f414475e&chksmc0e4732df793fa3bf39…

    操作系统控制台-健康守护我们的系统

    引言基本准备体验功能健康守护系统诊断 收获提升结语 引言 阿里云操作系统控制平台作为新一代云端服务器中枢平台&#xff0c;通过创新交互模式重构主机管理体验。操作系统控制台提供了一系列管理功能&#xff0c;包括运维监控、智能助手、扩展插件管理以及订阅服务等。用户可以…

    Linux系统基于ARM平台的LVGL移植

    软硬件介绍&#xff1a;Ubuntu 20.04 ARM 和&#xff08;Cortex-A53架构&#xff09;开发板 基本原理 LVGL图形库是支持使用Linux系统的Framebuffer帧缓冲设备实现的&#xff0c;如果想要实现在ARM开发板上运行LVGL图形库&#xff0c;那么就需要把LVGL图形库提供的关于帧缓冲设…

    DIY Tomcat:手写一个简易Servlet容器

    在Java Web开发领域&#xff0c;Tomcat堪称经典&#xff0c;它作为Servlet容器&#xff0c;承载着无数Web应用的运行。今天&#xff0c;我将带大家一同探索如何手写一个简易的Tomcat&#xff0c;深入理解其底层原理。 一、背景知识 在开始之前&#xff0c;我们需要对几个关键…

    PDF转JPG(并去除多余的白边)

    首先&#xff0c;手动下载一个软件&#xff08;poppler for Windows&#xff09;&#xff0c;下载地址&#xff1a;https://github.com/oschwartz10612/poppler-windows/releases/tag/v24.08.0-0 否则会出现以下错误&#xff1a; PDFInfoNotInstalledError: Unable to get pag…

    网络安全之端口扫描(一)

    前置介绍 什么是DVWA&#xff1f; DVWA&#xff08;Damn Vulnerable Web Application&#xff09;是一个专门设计用于测试和提高Web应用程序安全技能的开源PHP/MySQL Web应用程序。它是一个具有多个安全漏洞的故意不安全的应用程序&#xff0c;供安全专业人员、渗透测试人员、…

    财务会计域——合并报表系统设计

    摘要 本文主要介绍了合并报表系统的设计&#xff0c;包括其背景、业务流程和系统架构设计。合并报表系统可自动化生成数据&#xff0c;减少人为错误&#xff0c;确保报表合规。其业务流程涵盖数据收集、标准化、合并调整、报表生成、审核及披露等环节。系统架构设计包括数据接…

    游戏引擎学习第147天

    仓库:https://gitee.com/mrxiao_com/2d_game_3 上一集回顾 具体来说&#xff0c;我们通过隐式计算来解决问题&#xff0c;而不是像数字微分分析器那样逐步增加数据。我们已经涵盖了这个部分&#xff0c;并计划继续处理音量问题。不过&#xff0c;实际上我们现在不需要继续处理…

    NoSQL数据库系统Cassandra学习笔记

    详细文档&#xff1a;我用夸克网盘分享了「noSQL.pdf」&#xff0c;点击链接即可保存。打开「夸克APP」在线查看&#xff0c;支持多种文档格式转换。 链接&#xff1a;https://pan.quark.cn/s/dfc3864807b4 参考链接&#xff1a;黑马程序员NoSQL数据库系统Cassandra全套教程&a…

    苹果 M3 Ultra 芯片深度解析:AI 时代的性能革命

    2025 年 3 月 5 日&#xff0c;苹果正式发布了其史上最强 PC 芯片 ——M3 Ultra。这款基于 UltraFusion 封装技术的旗舰级 SoC&#xff0c;不仅延续了苹果芯片在能效比上的传统优势&#xff0c;更通过架构创新与硬件升级&#xff0c;将 AI 计算能力推向了新高度。本文将从性能突…

    Qt从入门到入土(八) -打包Qt程序

    前言 当你写完一个有趣的Qt程序时&#xff0c;想发给朋友或者家人&#xff0c;但是他们的电脑又没有安装Qt&#xff0c;那么如何直接在他们电脑上运行又不需要安装Qt呢&#xff1f;本篇文章会告诉你答案&#xff0c;本文详细的介绍了界面设计和功能实现。读完本文你不仅可以学…

    使用OpenCV和MediaPipe库——实现人体姿态检测

    目录 准备工作如何在Windows系统中安装OpenCV和MediaPipe库&#xff1f; 安装Python 安装OpenCV 安装MediaPipe 验证安装 代码逻辑 整体代码 效果展示 准备工作如何在Windows系统中安装OpenCV和MediaPipe库&#xff1f; 安装Python 可以通过命令行运行python --versio…

    React:Axios

    axios可以在浏览器和node.js两边跑&#xff0c;可以向服务端发起ajax请求&#xff0c;也可以在node.js里运行&#xff0c;向远端服务发送http请求 Axios中文文档 | Axios中文网 <!DOCTYPE html> <html lang"en"> <head><meta charset"UT…

    数据结构第八节:红黑树(初阶)

    【本节要点】 红黑树概念红黑树性质红黑树结点定义红黑树结构红黑树插入操作的分析 一、红黑树的概念与性质 1.1 红黑树的概念 红黑树 &#xff0c;是一种 二叉搜索树 &#xff0c;但 在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是 Red和 Black 。 通过对 任何…