React18和React16合成事件原理(附图)

💡 React18合成事件的处理原理

  “绝对不是”给当前元素基于addEventListener做的事件绑定,React中的合成事件,都是基于“事件委托”处理的!

  • 在React17及以后版本,都是委托给#root这个容器(捕获和冒泡都做了委托)
  • 在React17以前,都是委托给document容器的(而且只做了冒泡阶段的委托)
  •  对于没有实现事件传播机制的事件,才是单独做的事件绑定(例如,onMouseEnter/onMouseLeave...)

在组件渲染的时候,如果发现JSX元素中有onXxx/OnXxxCapture这样的属性,不会给当前元素直接做事件绑定,只是把绑定的方法赋值给元素的相关属性!

  1. outer.onClick={() => {console.log("outer 冒泡(合成)");}} //这不是DOM0级事件绑定(这样的才是outer.onclick(小写))
  2. outer.onClickCapture={() => {console.log("outer 捕获(合成)");}}
  3. inner.onClick={() => {console.log("inner 冒泡(合成)");}}
  4. inner.onClickCapture={() => {console.log("inner 捕获(合成)");}}

主要对#root这个容器做了事件绑定(捕获和冒泡都做了)

原因:因为组件中所渲染的内容,最后都会插到#root容器中,这样点击页面中任何一个元素,最后都会把#root的点击行为触发!!而在给#root绑定的方法中,把之前给元素设置的onXxx/onXxxCapture属性,在相应的阶段执行!!

💡 React18合成事件代码示例

我们给React添加对应的onClick,onClickCapture事件,在componentDidMount()周期函数(第一次渲染完毕)中添加原生事件绑定如下:猜猜打印的结果是什么

//React合成事件原理
import React from "react";class Demosy extends React.Component {render() {return (<divclassName="outer"style={{width: 200,height: 200,background: "lightgreen",}}onClick={() => {console.log("outer 冒泡(合成)");}}onClickCapture={() => {console.log("outer 捕获(合成)");}}><divclassName="inner"style={{width: 100,height: 100,background: "lightcoral",}}onClick={() => {console.log("inner 冒泡(合成)");}}onClickCapture={() => {console.log("inner 捕获(合成)");}}>888888</div></div>);}componentDidMount() {//组件渲染完document.addEventListener("click",() => {console.log("document捕获");},true);document.addEventListener("click",() => {console.log("document冒泡");},false);document.body.addEventListener("click",() => {console.log("body捕获");},true);document.body.addEventListener("click",() => {console.log("body冒泡");},false);let root = document.querySelector("#root");root.addEventListener("click",() => {console.log("root捕获");},true);root.addEventListener("click",() => {console.log("root冒泡");},false);let outer = document.querySelector(".outer");outer.addEventListener("click",() => {console.log("outer捕获(原生)");},true);outer.addEventListener("click",() => {console.log("outer冒泡(原生)");},false);let inner = document.querySelector(".inner");inner.addEventListener("click",() => {console.log("inner捕获(原生)");},true);inner.addEventListener("click",() => {console.log("inner冒泡(原生)");},false);}
}
export default Demosy;

运行 代码,点击inner触发事件,打印结果如下

看到这结果是不是跟你想象的不一样,想知道是什么原理嘛,接下来我们来自己简单的实现一下React18的合成事件原理,我们建立一个index.html,代码如下

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>合成事件的原理</title><style>* {margin: 0;padding: 0;}html,body {height: 100%;overflow: hidden;}.center {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);}#root {width: 300px;height: 300px;background: lightblue;}#outer {width: 200px;height: 200px;background: lightgreen;}#inner {width: 100px;height: 100px;background: lightcoral;}</style></head><body><div id="root" class="center"><div id="outer" class="center"><div id="inner" class="center"></div></div></div><script>const root = document.querySelector("#root"),outer = document.querySelector("#outer"),inner = document.querySelector("#inner");//经过视图渲染解析,outer/inner上 都有onXxx/onXxxCapture这样的属性/*    <divclassName="outer"onClick={() => {console.log("outer 冒泡");}}onClickCapture={() => {console.log("outer 捕获");}}><divclassName="inner"onClick={() => {console.log("inner 冒泡");}}onClickCapture={() => {console.log("inner 捕获");}}></div></div> */outer.onClick = () => {console.log("outer 冒泡(合成)");};outer.onClickCapture = () => {console.log("outer 捕获(合成)");};inner.onClick = () => {console.log("inner 冒泡(合成)");};inner.onClickCapture = () => {console.log("inner 捕获(合成)");};//给#root做事件绑定(源码)//捕获root.addEventListener("click",(ev) => {let path = ev.composedPath(); //path:[事件源-》...-》window] 所有祖先元素//深拷贝 捕获阶段倒叙循环[...path].reverse().forEach((ele) => {let handle = ele.onClickCapture; //handle是一个函数if (handle) handle();});},true);//冒泡root.addEventListener("click",(ev) => {let path = ev.composedPath(); //path:[事件源-》...-》window] 所有祖先元素// 冒泡不需要倒叙path.forEach((ele) => {let handle = ele.onClick;if (handle) handle(); //handle是一个函数});},false);</script></body>
</html>

运行代码结果图如下

我们可以通过ev.composedPath()拿到我们需要的对应路径,如上图路径是(事件源->....->window),我们通过事件传播机制知道,拿到我们需要的路径在捕获阶段把它倒叙循环,拿到它onClickCapture的属性(是一个函数),如果存在就执行它,冒泡阶段则不需要倒叙,拿到它onClick的属性(是一个函数),如果存在同样就执行它,此时得到的结果如上图所示

我们来画图深入了解一下

大概了解了一下如何原生实现React18原理,回过头我们在看看上面的题目,再通过画图来过一遍

对比打印结果,是不是对我们React18的合成事件原理秒懂

💡 React18合成事件代码示例

在React16版本中,合成事件的处理机制,不再是把事件委托给#root元素了,而是委托给document元素,并且只做了冒泡阶段的委托,在委托的方法中,把onXxx/onXxxCapture合成事件属性进行执行!

React16中,关于合成事件对象的处理,React内部是基于“事件对象池”,做了一个缓存机制!! React17及有以后,是去掉了这套事件对象池和缓存机制的!

当每一次事件触发的时候,如果传播到了委托的元素上(documnet),在委托的方法中,我们首先会对内置事件对象做统一处理,生成合成事件对象!!

在React16版本中为了防止每一次都是重新创建出新的合成事件对象,它设置了一个事件对象池(缓存池)

  •  
    • 等待本次操作结束,把合成事件对象中的成员信息都清空掉,再放入到事件对象池中
    • 本次事件触发,获取到事件操作的相关信息后,我们从事件对象池中获取存储的合成事件对象,把信息赋值给相关的成员
    • (特殊使用ev.persist() 就能把合成事件对象中的信息保存下来)

在React18中 并没有事件对象池机制,所以也不存在,创建的事件对象信息清空问题

补充知识点:

ev.stopPropagation(); //合成事件中的“阻止事件传播”:阻止原生的事件转播&&阻止合成事件中的事件传播

ev.nativeEvent.stopPropagation(); //原生事件对象中的“阻止事件传播”:只能阻止原生的事件转播

ev.nativeEvent.stopImmediatePropagation(); //原生事件对象中的“阻止事件传播”:只能阻止原生的事件转播 也能阻止同级#root的冒泡(其它绑定方法执行)

React16合成事件原理图

我们来简单的画个流程图深刻了解一下React16的合成事件原理

代码执行结果如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/5750.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【java】java中注解的简介,如何自定义注解,有哪些类型,有什么作用

java注解 注解的定义 Java 注解用于为 Java 代码提供元数据。作为元数据&#xff0c;注解不直接影响你的代码执行&#xff0c;但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。 首先要明确一点的是&#xff0c;注解并没有实际的作用&…

八股文(消息队列)

文章目录 1. RabbitMQ特点2. 如何保证消息的可靠性3. RabbitMQ消息的顺序性4. 实现RabbitMQ的高可用性5. 如何解决消息队列的延时以及过期失效问题&#xff1f;6. RabbitMQ死信队列7. RabbitMQ延迟队列8.RabbitMQ的工作模式9. RabbitMQ消息如何传输10. 核心概念10.1 生产者和消…

SpringBoot整合Spring Security实现权限控制

文章目录 Spring Security介绍Spring Security案例1、快速搭建一个springboot工程2、导入SpringSecurity整合springboot工程3、认证3.1、登录流程校验3.2、入门案例的原理3.3、实现思路3.4、实现认证流程&#xff08;自定义&#xff09;3.5、正式实现3.5.1 实现数据库的校验3.5…

python node Ubuntu 安装软件、删除软件 、更新软件 中的 软件源概念

在Node 用npm 安装软件 在Python 用 pip 安装软件 在Ubuntu 用 apt 、apt-get 、snap 安装软件 因为这三款软件 都是国外的&#xff0c; 软件包&#xff08;模块&#xff09;都放在国外的&#xff0c; 安装 、更新 特别慢 Node中配置 下载源 在 node 中 要配置 下载的的地址…

【C语言初阶】指针的运算or数组与指针的关系你了解吗?

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《快速入门C语言》《C语言初阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 &#x1f4cb; 前言&#x1f4ac; 指针运算&#x1f4ad; 指针-整数&#x1f4ad; 指针-指针&#x1f4ad; 指针…

关于K8s的Pod的详解(一)

关于K8s的Pod的详解&#xff08;一&#xff09; Pod和API server的通信加快Pod启动更改Pod的资源Pod 的持久卷的单个访问模式Pod 拓扑分布约束Pod 拓扑分布中的最小域数 Pod 作为k8s创建&#xff0c;调度&#xff0c;管理的基本单位。由上级的Controller对Node上安装的Kubelet发…

电脑安装双系统ubuntu18.04+windows后开机直接进入Windows解决方法

电脑型号&#xff1a;联想拯救者Y9000K2021H 系统&#xff1a;Windows11Ubuntu18.04双系统 问题&#xff1a;笔记本安装双系统后&#xff0c;Windows系统下处理word或者看论文&#xff1b;Ubuntu18.04系统安装ros进行机械臂控制等的研究。但最近开机后发现没有系统选项了&#…

网络传输媒体

物理层下面的传输媒体分为两种&#xff1a;导向型传输媒体和非导向型传输媒体。 一、导向型传输媒体 同轴电缆&#xff1a; 图示&#xff1a; 分类&#xff1a; 基带同轴电缆&#xff1a;用于数字传输&#xff0c;在早期局域网中广泛使用宽带同轴电缆&#xff1a;用于模拟传输…

数据结构之BinaryTree(二叉树)的实现

BinaryTree要实现的方法 总结 remove不在BinNode里&#xff0c;而是BinTree里 递归的两种写法 从上往下&#xff1a;同一对象的递归&#xff08;参数多一个&#xff0c;判空用一句话&#xff09;&#xff0c;子对象的递归&#xff08;参数void&#xff0c;判空用两句话&#…

python数据分析05—Pandas数据处理

目录 1.缺失数据处理 1.1 DataFrame自身产生的缺失数据 1.2 缺失数据判断和统计 ​1.3 缺失数据清理 2. 多源数据操作 2.1 合并函数&#xff1a;merge() 2.2 连接函数&#xff1a;join() 2.3 指定方向合并&#xff1a;concat() 3. 数据分组和聚合运算 3.1 groupby()方…

(34)继电器开关

文章目录 前言 34.1 装有IOMCU的自动驾驶仪上的继电器引脚 34.2 通过任务规划器定义继电器引脚 34.3 飞行员控制继电器 34.4 任务控制继电器 34.5 任务规划器控制继电器 前言 "继电器"是自动驾驶仪上的一个数字输出引脚&#xff0c;可在 0V 和 3.3V 或 5V 之间…

《向量数据库指南》:使用公共的Pinecone数据集

目录 数据集包含向量和元数据 列出公共数据集 加载数据集 迭代数据集 分批迭代文档并插入到索引中。 将数据集插入为数据帧。 接下来怎么做 本文档介绍如何使用现有的Pinecone数据集。 要了解创建和列出数据集的方法,请参阅创建数据集。 数据集包含向量和元数据 P…

WPF 搜索框控件样式

WPF 搜索框控件样式 完全通过Xaml代码实现&#xff0c;使用了UserControl进行封装。功能包括聚焦时控件展开&#xff0c;输入为空时的文字提示&#xff0c;以及待选提示项列表等效果。实现效果如下图&#xff1a; xaml代码 <UserControl x:Class"SearchBar.SearchBo…

栈OJ(C++)

文章目录 1.最小栈2.栈的压入、弹出序列3.逆波兰表达式&#xff08;后缀表达式&#xff09;求值3.1后缀表达式求值3.2中缀表达式转后缀表达式3.3带有括号的中缀表达式转后缀表达式 1.最小栈 class MinStack { public:MinStack(){}void push(int val){_st.push(val);//empty放在…

【kafka调试】用命令行查看kafka是否发出了命令

server 10.10.90.210:9092 topic stream_manager_center_capture_file 摄像头id&#xff1a; 17283ed2a1ac685f9fd5ef9f0de04792 cd /usr/loca/kafka bin/kafka-console-consumer.sh --bootstrap-server 10.10.90.210:9092 --topic stream_manager_center_capture_file 然后添…

<C语言> 数据在内存中的存储

1.数据类型介绍 C语言中的基本内置类型如下&#xff1a; char //字符数据类型 short //短整型 int //整型 long //长整型 long long //更长的整型 float //单精度浮点数 double //双精度浮点数类型的意义&#xff1a; 1.使用这个类…

设计模式-抽象工厂模式

在经济学领域中&#xff0c;其主要研究对象(商品)之间根据消费依存关系可分为互补商品或替代商品&#xff0c;其中&#xff0c;互补商品如汽车与汽油、自行车与自行车胎、大饼和香肠、开水和泡面等。在面向对象的代码世界中&#xff0c;不同对象之间也存在这种类似相互依赖的关…

使用 ChatGPT 碰到的坑

最近在使用 ChatGPT 的时候碰到一个小坑&#xff0c;因为某些特殊情况我需要使用 syslog 向 logbeat 中发送日志。 由于这是一个比较古老的协议&#xff0c;确实也没接触过&#xff0c;所以就想着让 ChatGPT 帮我生成个例子。 原本我已经在 Go 中将这个流程跑通&#xff0c;所…

PyTorch从零开始实现Transformer

文章目录 自注意力Transformer块编码器解码器块解码器整个Transformer参考来源全部代码&#xff08;可直接运行&#xff09; 自注意力 计算公式 代码实现 class SelfAttention(nn.Module):def __init__(self, embed_size, heads):super(SelfAttention, self).__init__()self.e…

ElasticSearch学习--RestClient及案例

目录 RestClient查询文档 快速入门 总结 全文检索&#xff08;match&#xff09;查询 精确查询 复合查询 查询总结 排序&#xff0c;分页 高亮 RestClient查询文档 快速入门 总结 全文检索&#xff08;match&#xff09;查询 多种查询的差异都在做类型和条件上&#x…