1. 什么是事件代理机制
React使用了一种称为“事件代理”(Event Delegation)的机制来处理事件。事件代理是指将事件处理程序绑定到组件的父级元素上,然后在需要处理事件的子元素上触发事件时,事件将被委托给父级元素进行处理。
React的事件代理机制有以下几个特点:
事件委托:React将事件绑定到组件的父级元素上,而不是直接绑定到每个子元素。这样,不论子元素的数量如何,只需要在父级元素上绑定一个事件处理程序,就可以处理所有子元素的事件。
事件冒泡:当子元素上的事件触发时,事件会沿着DOM树从子元素冒泡到父级元素。React利用事件冒泡机制来实现事件代理,将事件从子元素传递到父级元素进行处理。
合成事件:React提供了合成事件(Synthetic Event)来封装底层浏览器的原生事件。合成事件是跨浏览器兼容的,并且提供了一致的接口,使开发者可以方便地处理事件。
组件实例存储:React会在组件实例上存储合成事件,以便在处理事件时能够准确地访问到组件的相关数据和方法。
通过事件代理机制,React实现了高效和灵活的事件处理方式,同时减少了内存消耗。它使得开发者能够更方便地处理事件,并且能够避免一些潜在的性能问题,特别是在处理大量子元素的情况下。需要注意的是,React的事件代理机制并不是与原生的事件代理完全相同。React并不是通过在父级元素上使用事件委托来实现性能优化,而是使用合成事件和组件实例存储来提供一种更高效和便捷的事件处理方式。
看完这些概念肯定会疑惑: react通过什么方式把子元素的事件交给父级元素处理的、react中怎么获取触发元素呢、在react中能使用原生事件吗、父子元素有相同的事件时怎么只触发子元素的事件、react的事件代理机制和原生的事件代理机制有区别吗、什么是react合成事件、react合成事件和原生事件有什么区别?
2. 事件流
想要解决这些疑问,首先要明白触发事件后,事件是怎么传播的。
事件发生时会在元素节点与根节点之间按照特定的顺序传播,路径所经过的所有节点都会收到该事件,这个传播过程即DOM
事件流。
事件传播分为三个阶段:
- 捕获(Capture):事件对象从window对象传递到目标对象的过程。
- 目标(target):目标节点在处理事件的过程。
- 冒泡(Bubble):事件对象从目标对象传递到window对象的过程。
通俗的讲是由外到里捕获再从里到外冒泡的过程。
2.1 注册事件
1. addEventListener(event, function, useCapture)
event
:string,如"click"function
:function,事件处理函数useCapture
:布尔值,默认是false:冒泡事件;为true时捕获事件
2. onClock = function
注册冒泡事件
3. onClickCapture = function
注册捕获事件
区别:
同一元素通过addEventListener可以多次注册同一事件,执行顺序:按照注册顺序从上到下依次执行;
同一元素通过onClock或onClickCapture多次注册同一事件,只有最后注册的事件生效。
2.2 阻止事件传播和默认事件
为什么要阻止事件冒泡?这是因为某DOM节点绑定了某个事件监听器,当该DOM节点触发事件的时候才会执行回调函数,但是如果该节点的某后代节点触发了一个事件,也会由于事件冒泡导致该DOM节点的事件也被触发,在不应该的情况下执行了回调函数。
stopPropagation
阻止事件的传播,所以在捕获、冒泡阶段都可以阻止。
stopImmediatePropagation
同样能阻止事件传播,还能阻止该元素执行其他相同注册事件。
preventDefault
,它的作用不是用于阻止冒泡,而是阻止浏览器默认行为。如a标签跳转,表单提交等。
注意:原生事件阻止冒泡会阻止合成事件的触发,而合成事件的阻止冒泡不影响原生组件。所以两者最好不要混合使用,否则会出现一些奇怪的问题。
2.3 获取触发元素
e.target:触发dom元素
e.currentTarget:绑定事件的dom元素
3. 认识 React 合成事件
合成事件是 React 自定义的事件对象,在底层抹平了不同浏览器的差异,在上层面向开发者暴露统一的、稳定的、与 DOM 原生事件相同的事件接口。
虽然合成事件并不是原生 DOM 事件,但它保存了原生 DOM 事件的引用。当你需要访问原生 DOM 事件对象时,可以通过合成事件对象的 e.nativeEvent 属性获取到它。
注意:有些事件 React 并没有实现,比如 window 的 resize 事件。