您好,欢迎访问代理记账网站
  • 价格透明
  • 信息保密
  • 进度掌控
  • 售后无忧

React - 事件机制

一、DOM 事件流

在正式讲解 React 事件之前,有必要了解一下 DOM 事件流,其包含三个流程:事件捕获阶段、处于目标阶段和事件冒泡阶段。
在这里插入图片描述

  1. 事件捕获阶段:事件对象通过目标节点的祖先 Window 传播到目标的父节点。
  2. 处于目标阶段:事件对象到达事件目标节点。如果阻止事件冒泡,那么该事件对象将在此阶段完成后停止传播。
  3. 事件冒泡阶段:事件对象以相反的顺序从目标节点的父项开始传播,从目标节点的父项开始到 Window 结束。
addEventListener()

用于为特定元素绑定一个事件处理函数。addEventListener 有三个参数:

element.addEventListener(event, function, useCapture)
  • event 事件名,如“click”
  • function 事件触发时执行的函数
  • useCapture
    • true 事件在捕获阶段执行
    • fase 默认值,事件在冒泡阶段执行

二、React 合成事件

react的事件是合成事件((Synethic event),不是原生事件

SyntheticEvent

如果DOM上绑定了过多的事件处理函数,整个页面响应以及内存占用可能都会受到影响。React为了避免这类DOM事件滥用,同时屏蔽底层不同浏览器之间的事件系统差异,实现了一个中间层——syntheticEvent。

syntheticEvent 是 react 基于 vitrual dom 的,react 事件处理器接收到一个 syntheticEvent 对象,syntheticEvent 和原生浏览器事件一样拥有同样的接口,也支持事件冒泡机制。可以通过 stopPropgation 和 preventDefault 中断。如果需要访问原生事件对象,可以使用 nativeEvent 属性。

合成事件的目的
  1. 抹平不同浏览器之间的兼容性差异。最主要的动机。
  2. 事件"合成",即事件自定义。事件合成既可以处理兼容性问题,也可以用来自定义事件(例如 React 的 onChange 事件)。
  3. 提供一个抽象跨平台事件机制。类似 VirtualDOM 抽象了跨平台的渲染方式,合成事件(SyntheticEvent)提供一个抽象的跨平台事件机制。
  4. 可以做更多优化。例如利用事件委托机制,几乎所有事件的触发都代理到了 document,而不是 DOM 节点本身,简化了 DOM 事件处理逻辑,减少了内存开销。(React 自身模拟了一套事件冒泡的机制)
  5. 可以干预事件的分发。V16引入 Fiber 架构,React 可以通过干预事件的分发以优化用户的交互体验。

注:「几乎」所有事件都代理到了 document,说明有例外,比如audio、video标签的一些媒体事件(如 onplay、onpause 等),是 document 所不具有,这些事件只能够在这些标签上进行事件进行代理,但依旧用统一的入口分发函数(dispatchEvent)进行绑定。

事件注册

React 的事件注册过程主要做了两件事:document 上注册、存储事件回调。

  1. document 上注册
    在 React 组件挂载阶段,根据组件内的声明的事件类型(onclick、onchange 等),在 document 上注册事件(使用addEventListener),并指定统一的回调函数 dispatchEvent。换句话说,document 上不管注册的是什么事件,都具有统一的回调函数 dispatchEvent。也正是因为这一事件委托机制,具有同样的回调函数 dispatchEvent,所以对于同一种事件类型,不论在 document 上注册了几次,最终也只会保留一个有效实例,这能减少内存开销。

  2. 存储事件回调
    React 为了在触发事件时可以查找到对应的回调去执行,会把组件内的所有事件统一地存放到一个对象中(listenerBank)。而存储方式如上图,首先会根据事件类型分类存储,例如 click 事件相关的统一存储在一个对象中,回调函数的存储采用键值对(key/value)的方式存储在对象中,key 是组件的唯一标识 id,value 对应的就是事件的回调函数。

在这里插入图片描述

事件分发

事件分发也就是事件触发。React 的事件触发只会发生在 DOM 事件流的冒泡阶段,因为在 document 上注册时就默认是在冒泡阶段被触发执行。

其大致流程如下:

  1. 触发事件,开始 DOM 事件流,先后经过三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段
  2. 当事件冒泡到 document 时,触发统一的事件分发函数 ReactEventListener.dispatchEvent
  3. 根据原生事件对象(nativeEvent)找到当前节点(即事件触发节点)对应的 ReactDOMComponent 对象
  4. 事件的合成
    1. 根据当前事件类型生成对应的合成对象
    2. 封装原生事件对象和冒泡机制
    3. 查找当前元素以及它所有父级
    4. 在 listenerBank 中查找事件回调函数并合成到 events 中
  5. 批量执行合成事件(events)内的回调函数
  6. 如果没有阻止冒泡,会将继续进行 DOM 事件流的冒泡(从 document 到 window),否则结束事件触发

在这里插入图片描述

特点

几乎所有的事件代理(delegate)到document,达到性能优化的目的。
对于每种类型的事件,拥有统一的分发函数dispatchEvent
事件对象(event)是合成对象(SyntheticEvent),不是原生的

三、合成事件和原生事件的区别

写法不同

在原生事件中,事件名称使用小写,而 React 中使用驼峰命名。如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串

// 原生事件
<button onclick="buttonClick()"> 

// React事件
<button onClick="buttonClick()"> 

阻止默认行为不同

在 HTML 中,阻止事件的默认行为使用 return false,而 React 中必须调用 preventDefault。

// 原生
<button onclick="console.log('123'); return false"> 
  
// React
 function buttonClick(e) {
    e.preventDefault()
}

机制不同

原生是直接将事件绑定到当前元素,React 组件上声明的事件没有绑定在 React 组件对应的原生 DOM 节点上。

React 中的事件机制则分为两个阶段:事件注册、事件分发。所有的事件都会注册到 document 上,当触发时,会采用事件冒泡的形式冒泡到document上面,然后React将事件封装给正式的函数处理运行和处理。

触发先后

由于 React 的事件委托机制,React 组件对应的原生 DOM 节点上的事件触发时机总是在 React 组件上的事件之前。
所以要注意,原生事件(阻止冒泡)会阻止合成事件的执行,合成事件(阻止冒泡)不会阻止原生事件的执行

参考链接
一文吃透 react 事件机制原理


分享:

低价透明

统一报价,无隐形消费

金牌服务

一对一专属顾问7*24小时金牌服务

信息保密

个人信息安全有保障

售后无忧

服务出问题客服经理全程跟进