当前位置: 首页 > article >正文

React事件处理机制详解

​🌈个人主页:前端青山
🔥系列专栏:React篇
🔖人终将被年少不可得之物困其一生

依旧青山,本期给大家带来React篇专栏内容:React-

前言

在前端开发中,事件处理是构建交互式用户界面的关键部分。React 作为一个流行的 JavaScript 库,提供了丰富的事件处理机制,使得开发者能够更高效地管理事件。本文将详细介绍 React 中事件处理的基本概念,包括 ES5 和 ES6 语法的事件绑定方法,并深入探讨 React 合成事件的特点及其内部机制。

目录

前言

1 ES5语法绑定事件

1.1 无参数的绑定

1.1.1 方法一

1.1.2 方法二

1.1.2 有参数的绑定

1.2 ES6语法绑定事件

1.2.1 无参数绑定

1.2.1.1 方法一

1.2.1.2 方法二

1.2.2 有参数绑定

1.2.2.1 方法一

1.2.2.2 方法二

1.3 合成事件的特点

1.3.1 事件机制

1.3.2 对合成事件的理解

1.3.3 事件机制的流程

1、事件注册

2、事件存储

3、事件执行

1.3.4 合成事件、原生事件之间的冒泡执行关系

总结

React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:

  • React 事件的命名采用小驼峰式(camelCase),而不是纯小写。

  • 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。

1 ES5语法绑定事件

1.1 无参数的绑定

1.1.1 方法一
  • 定义函数

handleClick(e) { // e - 事件对象
  e.preventDefault();
  // doSomething ...
}
  • constructor 中绑定函数执行上下文

this.handleClick = this.handleClick.bind(this);
  • jsx中调用

<button onClick={this.hanleClick} />
1.1.2 方法二
  • 定义函数

handleClick(e) { // e - 事件对象
  e.preventDefault();
  // doSomething ...
}
  • jsx 中调用

<button onClick={this.hanleClick.bind(this)} />

1.1.2 有参数的绑定

  • 定义函数

handleClick(param1, param2, e) {
  e.preventDefault();
  // do something ...
}

注意此时无论多少个参数, e 一定放在最后

  • jsx 中调用

<button onClick={this.hanleClick.bind(this, 'x', 'xx')} />

src/index.js

import React from 'react'
import ReactDOM  from 'react-dom/client'
​
// 引入时,后缀名可以省略,可以在webpack中配置
// import App from './01-App-parent-child'
// import App from './02-App-parent-child-value'
// import App from './03-App-parent-child-value-default'
// import App from './04-App-parent-child-value-default-type'
// import App from './05-App-props-children'
// import App from './06-App-mutiple-props-children'
// import App from './07-App-mouse-tracker'
// import App from './08-App-render-props'
// import App from './09-App-state-es6'
// import App from './10-App-state-es7'
// import App from './11-App-setState-function'
// import App from './12-App-setState-object'
// import App from './13-App-setState-callback'
// import App from './14-App-LifeCycle'
import App from './15-App-handler-es5'
​
const root = ReactDOM.createRoot(document.getElementById('root'))
​
root.render(<App root={ root }/>)
​

src/15-App-handler-es5.jsx

import React, { Component } from 'react';
​
class App extends Component {
  constructor (props) {
    super(props)
    // 中绑定函数执行上下文
    this.clickHandler1Fn = this.clickHandler1.bind(this)
  }
  clickHandler1 (event) { // 构造函数中改变this指向 不推荐 --- 无法实现传递参数目标
    console.log(this)  // undefined  ==构造函数中改变this指向==>  App实例
    console.log(event) // SyntheticBaseEvent合成事件   js原生 PointerEvent
  }
​
  clickHandler2 (event) { // 推荐 ---- 可以传递参数
    console.log(this) // undefined  ===jsx代码中改变了this指向===> App实例
    console.log(event)
  }
​
  // 无论有多少个参数,记住,事件对象始终为最后一个参数
  clickHandler3 (params1, params2, params3, event) {
    console.log(params1)
    console.log(params2)
    console.log(params3)
    console.log(event)
  }
  render() {
    return (
      <div>
        <h1>es5绑定事件以及传递参数</h1>
        <button onClick={ this.clickHandler1Fn }>点击-绑定事件方法1</button>
        <button onClick={ this.clickHandler2.bind(this) }>点击-绑定事件方法2 - 推荐</button>
        <button onClick={ this.clickHandler3.bind(this, 'a', 'b', 'c') }>点击-传递参数</button>
      </div>
    );
  }
}
​
export default App;

1.2 ES6语法绑定事件

1.2.1 无参数绑定

1.2.1.1 方法一
  • 定义函数

handleClick = (e) => {
  e.preventDefault();
  // do something ...
}
  • jsx中调用

<button onClick={this.hanleClick} />

比起 es 5 中的无参数函数的绑定调用, es 6 不需要使用 bind 函数;

1.2.1.2 方法二

jsx中定义箭头函数

<button onClick={ () => {}} />

1.2.2 有参数绑定

1.2.2.1 方法一
  • 定义函数

handleClick = (param1, e) => {
  e.preventDefault();
  // do something ...
}
  • jsx调用

<button onClick={this.hanleClick.bind(this, 'x')} />

有参数时,在绑定时依然要使用 bind; 并且参数一定要传,不然仍然存在 this 指向错误问题;

1.2.2.2 方法二
  • 定义函数

handleClick = (param1, e) => {
  // do something ...
}
  • jsx调用

<button onClick={() => this.handleClick('c')} />
// 如果需要对 event 对象进行处理的话,需要写成下面的格式
<button onClick={(e) => this.handleClick('c', e)} />

src/index.js

import React from 'react'
import ReactDOM  from 'react-dom/client'
​
// 引入时,后缀名可以省略,可以在webpack中配置
// import App from './01-App-parent-child'
// import App from './02-App-parent-child-value'
// import App from './03-App-parent-child-value-default'
// import App from './04-App-parent-child-value-default-type'
// import App from './05-App-props-children'
// import App from './06-App-mutiple-props-children'
// import App from './07-App-mouse-tracker'
// import App from './08-App-render-props'
// import App from './09-App-state-es6'
// import App from './10-App-state-es7'
// import App from './11-App-setState-function'
// import App from './12-App-setState-object'
// import App from './13-App-setState-callback'
// import App from './14-App-LifeCycle'
// import App from './15-App-handler-es5'
import App from './16-App-handler-es6'
​
const root = ReactDOM.createRoot(document.getElementById('root'))
​
root.render(<App root={ root }/>)
​

src/16-App-handler-es6.jsx

import React, { Component } from 'react';
​
class App extends Component {
  clickHandler1 = (event) => { // 类中定义箭头函数
    console.log(this)
    console.log(event)
  }
​
  clickHandler3 = (params, event) => {
    console.log(params)
    console.log(this)
    console.log(event)
  }
​
  clickHandler4 = (params1, params2, event) => {
    console.log(params1)
    console.log(params2)
    console.log(this)
    console.log(event)
  }
  render() {
    return (
      <div>
        <h1>es6绑定事件以及传递参数</h1>
        <button onClick={ this.clickHandler1 }>点击-绑定事件方法1</button>
        <button onClick={ (event) => { // jsx中写箭头函数
          console.log(this)
          console.log(event)
        } }>点击-绑定事件方法2</button>
        <button onClick={ this.clickHandler3.bind(this, '参数') }>传递参数1</button>
        <button onClick={ (event) => this.clickHandler4('aaa', 'bbb', event) }>传递参数2</button>
      </div>
    );
  }
}
​
export default App;

1.3 合成事件的特点

1.3.1 事件机制

  • react自身实现了一套事件机制,包括事件的注册、事件的存储、事件的合成及执行等。

  • react 的所有事件并没有绑定到具体的dom节点上而是绑定在了document 上,然后由统一的事件处理程序来派发执行。

  • 通过这种处理,减少了事件注册的次数,另外react还在事件合成过程中,对不同浏览器的事件进行了封装处理,抹平浏览器之间的事件差异。

1.3.2 对合成事件的理解

(1)对原生事件的封装

react会根据原生事件类型来使用不同的合成事件对象,比如: 聚焦合成事件对象SyntheticFoucsEvent(合成事件对象:SyntheticEventreact合成事件的基类,定义了合成事件的基础公共属性和方法。合成事件对象就是在该基类上创建的)

(2)不同浏览器事件兼容的处理

在对事件进行合成时,react针对不同的浏览器,也进行了事件的兼容处理

1.3.3 事件机制的流程

1、事件注册

在组件挂载阶段,根据组件内声明的事件类型-onclickonchange 等,给 document 上添加事件 -addEventListener,并指定统一的事件处理程序 dispatchEvent

2、事件存储

完成事件注册后,将react dom ,事件类型,事件处理函数fn放入数组存储,组件挂载完成后,经过遍历把事件处理函数存储到 listenerBank(一个对象)中,缓存起来,为了在触发事件的时候可以查找到对应的事件处理方法去执行。

开始事件的存储,在react 里所有事件的触发都是通过 dispatchEvent方法统一进行派发的,而不是在注册的时候直接注册声明的回调,来看下如何存储的 。 react 把所有的事件和事件类型以及react 组件进行关联,把这个关系保存在了一个map里,也就是一个对象里(键值对),然后在事件触发的时候去根据当前的 组件id和 事件类型查找到对应的 事件fn

3、事件执行

1、进入统一的事件分发函数(dispatchEvent) 2、结合原生事件找到当前节点对应的ReactDOMComponent对象 3、开始 事件的合成

  • 根据当前事件类型生成指定的合成对象

  • 封装原生事件和冒泡机制

  • listenerBank事件池中查找事件回调并合成到 event(合成事件结束)

4.处理合成事件内的回调事件(事件触发完成 end)

1.3.4 合成事件、原生事件之间的冒泡执行关系

结论:

  • 原生事件阻止冒泡肯定会阻止合成事件的触发。

  • 合成事件的阻止冒泡不会影响原生事件。

原因:

  • 浏览器事件的执行需要经过三个阶段,捕获阶段-目标元素阶段-冒泡阶段

节点上的原生事件的执行是在目标阶段,然而合成事件的执行是在冒泡阶段,所以原生事件会先合成事件执行,然后再往父节点冒泡,所以原生事件阻止冒泡会阻止合成事件的触发,而合成事件的阻止冒泡不会影响原生事件

总结

本文详细介绍了 React 中事件处理的两种主要语法:ES5 和 ES6。通过对比这两种语法的事件绑定方法,我们了解到 ES6 的箭头函数在事件处理中的优势,特别是在处理 this 指向问题时更加简洁和直观。此外,文章还深入探讨了 React 合成事件的特点,包括事件机制的流程、事件注册、事件存储和事件执行等环节。通过这些内容,开发者可以更好地理解 React 事件处理的内部机制,从而在实际开发中更加灵活地运用这些知识。


http://www.kler.cn/a/405384.html

相关文章:

  • Vue实训---1-创建Vue3项目
  • 时序论文23|ICML24谷歌开源零样本时序大模型TimesFM
  • 【AIGC】如何使用高价值提示词Prompt提升ChatGPT响应质量
  • 测试工程师如何在面试中脱颖而出
  • 卷积神经网络各层介绍
  • 前端测试工具详解
  • C#-利用反射自动绑定请求标志类和具体执行命令类
  • Parker派克防爆电机在实际应用中的安全性能如何保证?
  • k8s常见问题及解决办法
  • 【python基础巩固】引用、浅深拷贝你分清了吗?
  • 【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
  • 私有化部署视频平台EasyCVR宇视设备视频平台如何构建视频联网平台及升级视频转码业务?
  • 前端:HTML (学习笔记)【2】
  • 提升性能测试效率与准确性:深入解析JMeter中的各类定时器
  • jdk各个版本介绍
  • java学习-集合
  • 数据新时代:如何选择现代数据治理平台(上)
  • 数据结构及算法--排序篇
  • 如何能让安全责任更清晰——构建清晰安全责任体系策略与实践
  • 数字IC后端实现时钟树综合系列教程 | Clock Tree,Clock Skew Group之间的区别和联系
  • 【工程记录】vscode+ssh远程配置python环境方法
  • 嵌入式:STM32的启动(Startup)文件解析
  • 生成式AI在教育技术中的应用:变革与创新
  • 开源生态发展合作倡议
  • 蓝桥杯-顺子日期
  • wireshark使用lua解析自定义协议