说到 React 的事件,也算是 React 的一个非常有亮点的优化,它在原生事件体系的基础上,单独实现了一套事件机制。想要了解这个机制,首先的了解下什么是事件委托以及事件委托的好处。

事件委托

假设有如下 html,我们想要在每个 li 上绑定 onClick 事件,最直观的做法当然就是给每个 li 分别添加事件,增加事件回调。这种做法当然没错,但是我们有一种更好的做法,那就是在 ul 上添加有个监听事件,由于事件的冒泡机制,事件就会冒泡到ul上,因为ul上有事件监听,所以事件就会触发。

Event 对象提供了一个属性叫 target,可以返回事件的目标节点,我们成为事件源,也就是说,target 就可以表示为触发当前的事件 dom,我们可以根据 dom 进行判断到底是哪个元素触发了事件,根据不同的元素,执行不同的回调方法。

这就是事件委托。

<

事件委托有以下优点。

  1. 减少事件注册,节省内存,能够提升整体性能。
  2. 简化了dom节点更新时,相应事件的更新(用过 jquery 的都知道,动态加入的元素,事件需要重新绑定)。

React 数据结构

React 并不是将 click 事件直接绑定在 dom 上面,而是采用事件冒泡的形式冒泡到 document 上面,这个思路借鉴了事件委托机制。所以,React 中所有的事件最后都是被委托到了 document这个顶级 DOM 上。

这个事件委托是在什么时候完成的呢?我们看如下示例。

<

这就是一个普通的 div,经过 React 的处理后,render 中的内容会变成以下数据结构。

{

React 会根据这个数据结构来构造真实的 dom,如果对之前的内容还有印象,我们在创建 HostComponent,也就是原生节点的时候,会调用 ensureListeningTo(rootContainerElement, propKey) 这个方法。

function 

ensureListeningTo 就是用来完成事件委托的,对于本例来说,rootContainerElement 表示项目挂载的根节点 div,registrationName 就是我们绑定事件的名称 onClick。div 的 nodeType 是ELEMENT_NODE,也就是元素节点,那么会通过该元素的 ownerDocument 属性获得文档的 Document 对象,然后将 onClick 事件挂载到 Document 对象上。

了解了这点后,我们先不看 listenTo 方法,回过头来了解下 React 事件的整理流程。

React 事件流程

react 源码中有一个注释很好的总结了 React 的事件流程(在注释中画流程图,很社会)。

react-domsrceventsReactBrowserEventEmitter.js

/**

从这个图可以看出,Dom 事件发生后,React 通过事件委托机制将大部分事件代理至 Document 层,ReactEventListener 就是负责给元素绑定事件的。ReactEventEmitter 暴露接口给 React 组件层用于添加事件订阅(对外暴露了 listenTo 等方法)。EventPluginHub 负责管理和注册各种插件。在事件分发时,调用插件来生成合成事件。 React 事件系统使用了插件机制来管理不同行为的事件。这些插件会处理自己感兴趣的事件类型,并生成合成事件对象。

比如 SimpleEventPlugin 负责处理一些比较通用的事件类型,如blur、focus、click、submit、touchMove、mouseMove、scroll、drag、load。

EnterLeaveEventPlugin 负责处理 mouseEnter/mouseLeave 和 pointerEnter/pointerLeave 这两类事件,单独处理的原因是这两类事件不支持冒泡。

TapEventPlugin 是为了解决移动端IOS 300ms 点击延迟,该插件增加了一个 onTouchTap 事件,这个事件触发后,会忽略300ms 后的 onClick 事件。

这里还需要了解的是,EventPluginHub 中处理的时间其实是合成事件 (SyntheticEvent),React 为什么要定义合成事件这个概念呢,有三点原因:

  1. 合成事件 (SyntheticEvent) 可以认为是浏览器原生事件跨浏览器的封装,相当于 React 帮我们做了浏览器的兼容性处理。
  2. React 想通过 SyntheticEvent 实现跨平台事件机制。
  3. 原生事件升级、改造,比如 React 的 onChange 事件,它为表单元素定义了统一的值变动事件,例如 blur、change、focus、input 等。

React 事件委托

了解了 React 大概的事件体系后,接着看之前的 ensureListeningTo 方法,该方法总调用了 listenTo 方法,这个方法就是 ReactEventEmitter 中的(对应着上面的图看会清晰很多)。

react-domsrceventsReactBrowserEventEmitter.js

export 

我们看下 registrationNameDependencies 的数据结构。registrationNameDependencies 比较多,我们看些熟悉的。registrationNameDependencies 存放的是 React 事件名与原生事件名映射的 Map。可以很明显的看到,有些事件是合成事件,比如 onBeforeInput,onChange 等。onClick 事件就是原生的 click。这些值都是插件赋予的。

{

对于依赖的原生事件,scroll blur focus cancel close 方法注册捕获阶段的事件监听器。invalid submit reset 事件不做处理。剩下的事件需要判断是否是媒体触发的,比如 video / audio 的 onplaying 事件,onprogress 事件, onratechange 事件等,这些媒体事件也不需要处理。

React 这么做的原因和事件有关,有些事件是不冒泡的,所以不能在冒泡阶段进行事件委托。具体情况请看我的另一篇文章“JavaScript那些不会冒泡的事件”,大家可以先去了解下这些不会冒泡的事件,回头看 React 的处理流程会比较清晰。

上述代码中处理了个特殊的情况,那就是 form 的 submit invalid,reset 事件,这几个事件没有委托给 Document 对象,如下所示。

case 

为什么呢?原因是如果将 submit 事件绑定到 Form 上,该事件的监听方法会触发两次。当提交按钮被点击后,事件开始冒泡,当冒泡到 Form 的上的时候,由于 Form 存在 onSubmit 回调,会执行一次,然后向上冒泡到 Document,还会执行一次。所以 React 对于 Form 事件就不做处理了。

<

React 同样也没有处理媒体事件,和 Form 事件不同的是,媒体事件是没有冒泡过程的,所以只能在捕获阶段进行事件委托。

<

经过测试后发现,媒体事件如果在捕获阶段进行事件委托,该事件的回调方法会被执行两次,和 Form 的表现是一致的,所以媒体事件没有委托。

除了上述的特殊事件,剩下的事件执行 trapBubbledEvent,也就是注册冒泡阶段的事件监听器,click 事件当然也处在这个行列。

trapBubbledEvent 和 trapCapturedEvent 大同小异,本例中触发的 click 事件属于冒泡事件,我们分析 trapBubbledEvent 方法。

react-domsrceventsReactDOMEventListener.js

export 

trapBubbledEvent 调用 trapEventForPluginEventSystem 方法

function 

React 中的事件分为 3 类。分别是 DiscreteEvent(离散事件),UserBlockingEvent(用户阻塞事件),ContinuousEvent(连续事件)。不同类型的事件代表了不同的优先级。

  1. DiscreteEvent:click,blur,focus,submit,tuchStart 等,优先级是 0。
  2. UserBlockingEvent:touchMove,mouseMove,scroll,drag,dragOver 等,这些事件会阻塞用户的交互,优先级是 1。
  3. ContinuousEvent:load,error,loadStart,abort,animationend 等,优先级是 2,这个优先级最高,不会被打断。

根据优先级的不同,监听函数做了不同的包装,我们先不管这里生成的监听函数和最初的监听方法有什么不同。最终我们会调用 addEventBubbleListener 方法。

addEventBubbleListener 就是 element.addEventListener,为目标添加事件监听函数。

export 

到此为止,一个事件 click 事件的监听方法就被添加到 Document 对象上了。小结一下:

  1. React 借鉴事件委托的方式将大部分事件委托给了 Document 对象。
  2. React 中的事件分为 3 类。分别是 DiscreteEvent(离散事件),UserBlockingEvent(用户阻塞事件),ContinuousEvent(连续事件)。不同类型的事件代表了不同的优先级。
  3. 事件委托需要区分捕获和冒泡,有些事件由于没有冒泡过程,只能在捕获阶段进行事件委托。
  4. 没有进行委托的事件是 Form 事件和 Media 事件,原因是这些事件委托后会触发两次回调函数。

建议先阅读我的另一篇文章JavaScript 中那些不会冒泡的事件,这样会更容易理解 React 对于事件的处理过程

如果您觉得有所收获,就麻烦点个赞吧!

jquery 监听td点击事件_React 事件 | 1. React 中的事件委托相关推荐

  1. jquery 监听td点击事件_安卓开发监听点击事件的一种方法

    本人是菜鸟一只,学习安卓纯属兴趣.没有真正上过编程课程,所有知识都是在网上获取的.今天分享的是监听点击事件的一个方法,这个方法的好处是代码较简洁. 如图,点击保存时,把上面的数据入库. 实现如下: 在 ...

  2. jquery 监听td点击事件_VUE @hook浅析(监听子组件的生命周期钩子) - 古兰精

    一.前言 接触hook是从webhook开始接触的,webhook是git的一个扩展服务,可以在仓库接收到push/commit事件并发送http request至一个开发者可以自定义的URL.通过这 ...

  3. jquery监听窗口大小改变事件jquery.resizeend

    方法一: $(function(){// Bind the resize event. When any test element's size changes, update its// corre ...

  4. Jquery监听onChange事件

    Jquery监听onChange事件 $(document).ready(function () {$("#控件ID").change(function () {var organ ...

  5. 综合设计一个OPPE主页--页面的插件引用(animate.css)--d动画的使用--滚轮或鼠标到该位置时,才有动画的切换---所以我们需要用jquery监听鼠标滚轮的滚动事件

    Animate.css | A cross-browser library of CSS animations. 里面有许多css的效果 首先使用 animate.css文件 link rel=&qu ...

  6. maptalks常见操作——图层置顶置底、添加清空图层、添加标注、切换底图、添加缩放工具、事件监听(点击面出弹框)、右键菜单、绘制mark、锁定视角

    maptalks常见操作--图层置顶置底.添加清空图层.添加标注.切换底图.添加缩放工具.事件监听(点击面出弹框).右键菜单.绘制mark.锁定视角 1.图层置顶 置底 layer.show().br ...

  7. 监听当点击微信等app的返回按钮或者浏览器的上一页或后退按钮的事件

    在实际的应用中,我们常常需要实现在移动app和浏览器中点击返回.后退.上一页等按钮实现自己的关闭页面.调整到指定页面或执行一些其它操作的 需求,那在代码中怎样监听当点击微信.支付宝.百度糯米.百度钱包 ...

  8. jQuery 监听修改文本框事件

    在开发中,有时我们需要对 input 的 value 值变化作实时响应.比如文本输入框的实时字数统计功能,要求文本框内容改变时就要触发相关的统计行为.不管这个内容是键盘输入的,还是鼠标粘贴进来的.1, ...

  9. 使用swipecard实现卡片视图左右滑动监听以及点击监听

    使用swipecard实现卡片视图左右滑动监听以及点击监听  前言: 大家好,今天给大家介绍安卓一种特别实用有很酷炫的组件swipecard,当然这并不是安卓爸爸创造的,这是国内的一个我认为是大牛的一 ...

最新文章

  1. 美国在理论计算机科学的研究重视,留学热门专业计算机之研究方向篇
  2. AppleScript 以管理员身份运行程序
  3. (59)Verilog HDL测试激励:时钟激励1
  4. 20211027:《Labuladong的算法小抄》学习记录(一)
  5. 通过表名导出DDL语句(包含建表、索引、注释、主键)
  6. linux QT 结束当前进程_Linux桌面进化史
  7. EAS后台事务的超时时长如何设置?
  8. 奇怪的键盘按a截图了
  9. HBuilder快捷键整理集合
  10. 新闻分析:微软到底需要雅虎什么?
  11. (完结)Unity游戏开发——新发教你做游戏(七):Animator控制角色动画播放
  12. 常用eclips快捷键小结
  13. 你应该看得懂的RecyclerView嵌套
  14. 华为OD机试 - 高矮个子排队
  15. C语言字符串中‘\0‘问题
  16. php手册学习记录(入门指引处理表单)
  17. 【淘宝SEO】搜索诊断助手新增作弊检测
  18. 天猫精灵java开发工程师工资_李双印-天猫精灵2020年校招(对话系统方向)
  19. 你认为喜剧演员何欢什么时候会走红?
  20. Zotero | 快速入门

热门文章

  1. [附源码]JAVA+ssm明天摄影工作室(程序+Lw)
  2. 魔兽怀旧服联盟服务器不稳定,魔兽世界怀旧服上次被联盟攻击至少三个月前,“单边服”何去何从...
  3. 手把手教你安装 illustrator(Ai)2022 软件
  4. 和平精英android怎么写符号,和平精英名字符号如何输入 名字符号输入方法
  5. Java操作Cookie之添加Cookie
  6. python c++情侣网名是什么意思_c++中嵌入python 看什么意思
  7. 做IT喝刺五加,健康!
  8. 小黑leetcode之旅:86. 分隔链表
  9. smb.conf 详解
  10. lisp 删除编组中图元_2013-09-05 明经 AutoLISP 编程(102918348) 群聊记录:删除列表中第N个元素是用哪个函数?vl-remove 是把列表里相同都会删掉...