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

news2024/11/24 19:34:30

💡 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 (
      <div
        className="outer"
        style={{
          width: 200,
          height: 200,
          background: "lightgreen",
        }}
        onClick={() => {
          console.log("outer 冒泡(合成)");
        }}
        onClickCapture={() => {
          console.log("outer 捕获(合成)");
        }}
      >
        <div
          className="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这样的属性
      /*    <div
        className="outer"
        onClick={() => {console.log("outer 冒泡");}}
        onClickCapture={() => {console.log("outer 捕获");}}
      >
        <div
          className="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.coloradmin.cn/o/781185.html

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

相关文章

动态规划入门第1课

1、从计数到选择 ---- 递推与DP&#xff08;动态规划&#xff09; 2、从递归到记忆 ---- 子问题与去重复运算 3、动态规划的要点 第1题 网格路1(grid1) 小x住在左下角(0,0)处&#xff0c;小y在右上角(n,n)处。小x需要通过一段网格路才能到小y家。每次&#xff0c;小x可以选…

视频基础知识

1.视频比特率 视频的比特率是指传输过程中单位时间传输的数据量。可以理解为视频的编码采样率。单位是kbps&#xff0c;即每秒千比特。视频比特率是决定视频清晰度的一个重要指标。比特率越高&#xff0c;视频越清晰&#xff0c;但数据量也会越大。比如一部100分钟的电影&#…

5.4 Bootstrap 下拉菜单(Dropdown)插件

文章目录 Bootstrap 下拉菜单&#xff08;Dropdown&#xff09;插件用法在导航栏内在标签页内 选项方法 Bootstrap 下拉菜单&#xff08;Dropdown&#xff09;插件 Bootstrap 下拉菜单 这一章讲解了下拉菜单&#xff0c;但是没有涉及到交互部分&#xff0c;本章将具体讲解下拉菜…

(链表) 剑指 Offer 58 - I. 翻转单词顺序 ——【Leetcode每日一题】

❓剑指 Offer 06. 从尾到头打印链表 难度&#xff1a;简单 输入一个链表的头节点&#xff0c;从尾到头反过来返回每个节点的值&#xff08;用数组返回&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head [1,3,2] 输出&#xff1a;[2,3,1] 限制&#xff1a; 0 < 链…

自己实现数据结构,有更好的可操作性

https://leetcode.cn/problems/asteroid-collision/ 就是说&#xff0c;非得用人家写的栈不可么 stl 的 stack&#xff0c;没办法随机访问&#xff0c;用 vector 或者再不济数组&#xff0c;就好了&#xff0c;非要用 stl 的&#xff0c;就导致还得有另一个 vector 维护向左走…

多目标灰狼算法(MOGWO)的Matlab代码详细注释及难点解释(佳点集改进初始种群的MOGWO)

目录 一、外部种群Archive机制 二、领导者选择机制 三、多目标灰狼算法运行步骤 四、MOGWO的Matlab部分代码详细注释 五、MOGWO算法难点解释 5.1 网格与膨胀因子 5.2 轮盘赌方法选择每个超立方体概率 为了将灰狼算法应用于多目标优化问题,在灰狼算法中引入外部种群Archi…

Spring MVC -- 返回数据(静态页面+非静态页面+JSON对象+请求转发与请求重定向)

目录 1. 返回静态页面 2. 返回非静态页面 2.1 ResponseBody 返回页面内容 2.2 RestController ResponseBody Controller 2.3 示例:实现简单计算的功能 3. 返回JSON对象 3.1 实现登录功能&#xff0c;返回 JSON 对象 4. 请求转发(forward)或请求重定向(redirect) 4.1 请…

Day 63 : 集成学习之 AdaBoosting (1. 带权数据集)

63.1 AdaBoosting基本算法&#xff1a;先从初始训练集训练一个弱学习器&#xff0c;在根据弱学习器的表现对训练样本进行权重调整&#xff0c;经过若干轮之后&#xff0c;将得到一组分类器&#xff0c;将数据输入这组分类器后会得到一个综合且准确的的分类结果。“三个臭皮匠&a…

评论管理功能

后端 bp.get("/comment/list") def comment_list():comments CommentModel.query.order_by(CommentModel.create_time.desc()).all()comment_list []for comment in comments:comment_dict comment.to_dict()comment_list.append(comment_dict)return restful.ok…

Python模块requests基本用法

简介 Python 的 requests 模块是一个流行的第三方库&#xff0c;用于发送HTTP请求。它提供了一组简洁且易于使用的API&#xff0c;使得在Python中进行网络通信变得更加简单和灵活。 目录 1. 基本概念 1.1. HTTP 协议 1.2. GET 请求 1.3. POST 请求 1.4. get 和 post 的区别…

uniapp 小程序 评分组件

效果图&#xff1a; 1、组件&#xff1a;starsRating.vue <template><view class"stars"><image click"btnStars1" class"starsicon" :src"starsObject[0]" mode"widthFix"></image><image c…

Stream API将对象中的某一字段取出转换为list或数组

List<DevicePartMaintain> devicePartMaintainList devicePartMaintainMapper.selectDevicePartMaintainByMitId(mitId);所有id转换为List 要使用Stream流获取devicePartMaintainList中所有的id&#xff0c;您可以使用stream()方法将列表转换为流&#xff0c;然后使用…

从C语言到C++_28(红黑树RedBlackTree)概念+插入接口实现

目录 1. 红黑树的引入和简介 2. 红黑树的性质和定义 3. 红黑树的插入 3.1 调整情况一 3.2 调整情况二 3.2.1 调整情况二中的单旋变色 3.2.2 调整情况二中的双旋变色 3.3 调整情况三 3.4 红黑树插入完整代码 4. 红黑树的验证和完整代码 4.1 验证是不是搜索树&#xf…

Pytorch个人学习记录总结 07

目录 神经网络-非线性激活 神经网络-线形层及其他层介绍 神经网络-非线性激活 官方文档地址&#xff1a;torch.nn — PyTorch 2.0 documentation 常用的&#xff1a;Sigmoid、ReLU、LeakyReLU等。 作用&#xff1a;为模型引入非线性特征&#xff0c;这样才能在训练过程中…

【0基础学习python】顺序结构+条件语句+循环结构(文章后面有人生重开模拟器的相关逻辑和简单实现)

1.顺序语句 默认情况下&#xff0c;python的代码执行顺序是按照从上到下的顺序&#xff0c;依次执行的。 print(1) print(2) print(3)执行的结果一定为 1 2 3 &#xff0c;而不会出现 3 2 1 或者 1 3 2等&#xff0c;这种按照顺序执行的代码&#xff0c;我们称为顺序语句。 …

C++第六讲

思维导图 顺序栈定义成模板类 /* ---------------------------------author&#xff1a;YoungZorncreated on 2023/7/22 16:23.--------------------------------- */ #include<iostream>using namespace std;template<typename T> class my_stack { private:T *p…

Unity进阶--声音管理器学习笔记

文章目录 声音管理器 using System.Collections; using System.Collections.Generic; using UnityEngine;public class AudioManager : MyrSingletonBase<AudioManager> {//环境音private AudioSource enPlayer;//音效private AudioSource sePlayer;//音乐private Audio…

IDEA使用AWS CodeWhisperer

IDEA使用AWS CodeWhisperer 首先在IDEA的插件市场中下载AWS Toolkit&#xff1a; 这里我使用的IDEA是2023.1&#xff0c;就是在ToolWindows里把AWS Toolkit面板调出来&#xff1a; 连接&#xff1a; 打开的网页中粘贴上面提过的代码。进入注册流程。 注册完成后返回IDEA&am…

自动驾驶感知系统-毫米波雷达

毫米波雷达就是电磁波&#xff0c;雷达通过发射无线电信号并接收反射信号来测定车辆与物体间的距离&#xff0c;其频率通常介于10~300GHz之间。与厘米波导引头相比&#xff0c;毫米波导引头体积小&#xff0c;质量轻&#xff0c;空间分辨率高&#xff1b;与红外、激光、电视等光…

Vue--插槽

一、插槽-默认插槽 1.作用 让组件内部的一些 结构 支持 自定义 2.需求 将需要多次显示的对话框,封装成一个组件 3.问题 组件的内容部分&#xff0c;不希望写死&#xff0c;希望能使用的时候自定义。怎么办 4.插槽的基本语法 组件内需要定制的结构部分&#xff0c;改用**…