React 事件与 DOM 事件有何不同

425 阅读2分钟

首先回顾原生 DOM 下的事件流 一个事件的传播过程要经过以下 3 个阶段:

  • 事件捕获阶段
  • 目标阶段
  • 事件冒泡阶段

DOM 事件流下的性能优化思路:事件委托,通过e.target获取真实发生事件的DOM节点

像这样利用事件的冒泡特性,把多个子元素的同一类型的监听逻辑,合并到父元素上通过一个监听函数来管理的行为,就是事件委托。通过事件委托,我们可以减少内存开销、简化注册步骤,大大提高开发效率。

这正是 React合成事件的灵感源泉。

React 事件系统是如何工作的

React 的事件系统沿袭了事件委托的思想。在 React 中,除了少数特殊的不可冒泡的事件(比如媒体类型的事件)无法被事件系统处理外,绝大部分的事件都不会被绑定在具体的元素上,而是统一被绑定在页面的 document 上。当事件在具体的 DOM 节点上被触发后,最终都会冒泡到 document 上,document 上所绑定的统一事件处理程序会将事件分发到具体的组件实例。

在分发事件之前,React 首先会对事件进行包装,把原生 DOM 事件包装成合成事件。

合成事件是 React 自定义的事件对象,它符合W3C规范,在底层抹平了不同浏览器的差异,在上层面向开发者暴露统一的、稳定的、与 DOM 原生事件相同的事件接口。开发者们由此便不必再关注烦琐的兼容性问题,可以专注于业务逻辑的开发。

虽然合成事件并不是原生 DOM 事件,但它保存了原生 DOM 事件的引用

事件的绑定是在组件的挂载过程中完成

React 17事件的改动

  • 更改事件委托

原来的 react 的事件系统是挂在 document 上的,为了支持混用将会修改到你挂载的 rootNode 中,这样其实会造成一些问题,比如说原来的 e.stopPropagation() 能解除原生 document 事件的冒泡,现在只能阻止到 rootNode,但大部分时候是没有影响的

  • onScroll事件不再冒泡,以防止出现一些混淆。
  • React的onFocus和onBlur事件已在底层切换为原生的focusin和focusout事件。它们更接近React现有行为,有时还会提供额外的信息。
  • 捕获事件(例如,onClickCapture)现在使用的是实际浏览器中的捕获监听器
  • 去除事件池,在现在浏览器上,这个功能不会提升性能,反而会因为重用事件对象,造成困扰。
  • focus 事件底层切换成了 focusin,行为没有变化
  • useEffect 的回调修改为异步调用