React源码解析18(7)------ 实现事件机制(onClick事件)

news2024/11/24 18:47:58

摘要

在上一篇中,我们实现了useState的hook,但由于没有实现事件机制,所以我们只能将setState挂载在window上。
而这一篇主要就是来实现事件系统,从而实现通过点击事件进行setState。

而在React中,虽然我们是将事件绑定在JSX上的某个元素上,但是其实最终的执行者是最外层的容器。
也就是说React利用了冒泡的机制,将所有事件都冒泡到了最外层容器上,从而创建合成事件,在对相应的事件执行。

所以在实现事件机制之前,我们先将准备好的JSX进行修改:

function App() {
  const [name, setName] = useState('kusi','key');
  const [age, setAge] = useState(20)
  const click1 = () => {
    console.log(name)
    setName(name + '1')
  }
  const click2 = () => {
    console.log(age)
    setAge(age + 1)
  }
  return jsx("div", {
    ref: "123",
    onClick: click1,
    children: jsx("span", {
      children: name + age,
      onClick: click2
    })
  });
}

1.实现initEvent方法

刚才我们说了,在React中,事件的执行者是最外层的容器,也就是说我们需要给最外层的容器绑定一个事件,用来初始化。

export const initEvent = (root, eventType) => {
  root.addEventListener(eventType, (e) => {
    dispatchEvent()
  })
}

而我们可以在最开始的时候,调用initEvent。最开始也就是createContainer方法里面:

function createContainer(root) {
  initEvent(root, 'click')
  const hostRootFilber = new FilberNode(HostRoot, {}, '')
  return new FilberRootNode(root, hostRootFilber)
}

这里我们先实现click事件。

2.给所有DOM绑定props

我们思考一下,对于所有的事件,一定是在对应组件的Props里面,而我们要在dom上拿到对应的事件,那么就要将props属性同步给dom。
而真实DOM是在completeWork阶段生成的,所以我们需要实现一个方法,用来给dom绑定props属性:

function addPropsToDOM(element, props) {
  element['__props'] = props
}

在completeWork阶段,调用该方法:

export const completeWork = (filberNode) => {
  const tag = filberNode.tag
  switch (tag) {
    case HostComponent: {
      if(filberNode.stateNode !== null){
        //更新
        addPropsToDOM(filberNode.stateNode, filberNode.pendingProps)
      }else{
        completeHostComponent(filberNode)
      }
      break;
    }
function completeHostComponent(filberNode) {
  const type = filberNode.type;
  const element = document.createElement(type);
  addPropsToDOM(element, filberNode.pendingProps)
  filberNode.stateNode = element;
  const parent = filberNode.return;
  if(parent && parent.stateNode && parent.tag === HostComponent) {
    parent.stateNode.appendChild(element)
  }
  completeWork(filberNode.child)
}

此时可以打印看一下stateNode中的element,是否已经有__props属性了:

在这里插入图片描述

3.收集所有事件

现在所有的DOM已经有了对应的事件,现在我们需要将所有的事件收集起来:
收集的过程就是,当前点击的元素,到最外层容器录过的所有事件。
所以我们需要三个参数:当前点击的元素,容器,事件类型。

由于在React中,事件分为两种,比如onClick和onClickCapture。所以我们用两个集合来收集这两种事件。

function collectEvent(event, root, eventType) {
  const bubble = [];
  const capture = [];

  while(event !== root){
    const eventProps = event['__props'];
    if(eventType === 'click'){
      const click = eventProps['onClick'];
      const clickCapture = eventProps['onClickCapture'];
      if(click){
        bubble.push(click);
      }
      if(clickCapture){
        capture.unshift(clickCapture)
      }
    }
    event = event.parentNode;
  }
  return {bubble, capture}
}

然后我们在dispatchEvent中进行调用:

function dispatchEvent(root, eventType, e) {
  const {bubble, capture} = collectEvent(e.target, root, eventType)
  console.log(bubble, capture);
}

我们看一下打印结果:
在这里插入图片描述
可以看到在bubble中,已经将方法保存下来了。

4.创建合成事件对象

我们现在已经收集了这么多方法,按理说也该去执行了。但是有一个问题, 我们创建了bubble和capture。只是用来模仿浏览器的冒泡和捕获,也就是并非是真正的冒泡捕获。

最终执行所有事件的还是root,所以我们要创建一个新的event,用来代替浏览器的event。

在这个方法中,我们用一个标志位__stopPropgation来决定是否冒泡。如果在外面调用“e.stopPropgation”,我们将这个标志位置位true。

function createSyntheticEvent(e) {
  const syntheticEvent = e;
  syntheticEvent.__stopPropgation = false;
  const originStopPropgation = e.stopPropagation;

  syntheticEvent.stopPropagation = () => {
    syntheticEvent.__stopPropgation = true;
    if( originStopPropgation ) {
      originStopPropgation()
    }
  }
  return syntheticEvent;
}
}

在dispatchEvent中进行调用:

function dispatchEvent(root, eventType, e) {
  const {bubble, capture} = collectEvent(e.target, root, eventType)
  console.log(bubble, capture);
  const se = createSyntheticEvent(e)
}

4.事件调用

OK,现在我们要进行最后一步,对事件进行调用了。我们只需要对bubble和capture中的事件进行遍历调用即可,现在我们实现一个方法:

function triggerEvent(paths, se) {
  for(let i=0; i< paths.length; i++) {
    paths[i].call(null, se);
    if(se.__stopPropgation) {
      break;
    }
  }
}

然后再dispatchEvent中执行:

function dispatchEvent(root, eventType, e) {
  const {bubble, capture} = collectEvent(e.target, root, eventType)
  const se = createSyntheticEvent(e);
  triggerEvent(capture,se);
  if(!se.__stopPropgation) {
    triggerEvent(bubble,se)
  }
}

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

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

相关文章

ssm基于ssm的人才招聘网站源码和论文

ssm基于ssm的人才招聘网站源码和论文020 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 选题依据&#xff08;研究的背景、目的和意义等&#xff09; 在Internet飞速发展的今天&#xff0c;互联网成为人们快…

如何保证数据传输的安全?

要确保数据传输的安全&#xff0c;您可以采取以下措施&#xff1a; 使用加密协议&#xff1a;使用安全的传输协议&#xff0c;如HTTPS(HTTP over SSL/TLS)或其他安全协议&#xff0c;以保护数据在传输过程中的安全性。加密协议可以有效防止数据被窃听或篡改。 强化身份验证&…

【从零学习python 】28. Python中的局部变量和全局变量

文章目录 局部变量全局变量全局变量和局部变量名字相同问题修改全局变量查看所有的全局变量和局部变量函数返回值一、多个return?二、一个函数返回多个数据的方式 对返回的数据直接拆包进阶案例 局部变量 局部变量&#xff0c;就是在函数内部定义的变量其作用范围是这个函数内…

Java-java中的类,main函数和文件名的关系

java中的类&#xff0c;main函数和文件名的关系 类与文件名的关系 一个java文件中可以定义多个类&#xff0c;但是最多只有一个类被public修饰&#xff0c;并且这个类的类名与文件名必须相同&#xff0c;若这个文件中没有public的类&#xff0c;则文件名随便是一个类的名字即可…

JavaWeb-Filter过滤器

目录 Filter过滤器 1. Filter的生命周期 2.Filter的配置 3.拦截路径 4.拦截具体的使用 5.拦截方式配置&#xff08;资源被访问方式&#xff09; 6.FilterChain拦截链 Filter过滤器 filter是过滤器&#xff0c;相比于Servlet的发送请求&#xff0c;filter是用于拦截请求。…

电脑打开对话框中没有桌面这个选项解决办法

问题描述&#xff1a; 左侧栏中的桌面图标不显示 解决方法&#xff1a; 左侧的空白处右键-显示所有的文件夹 这时所有的文件夹都显示了&#xff01;

我给 PostgreSQL 官方 JDBC 驱动修复了一个高并发性能问题

这是我在 2022 年给 PostgreSQL 官方 JDBC 驱动 修复的一个高并发性能问题。 该问题影响的版本范围是 pgjdbc&#xff1a; 42.3.242.3.3 Issue: Concurrent performance issue in 42.3.2 caused by #2291 https://github.com/pgjdbc/pgjdbc/issues/2450 PR: Use non-synchro…

[Mongodb 5.0]聚合操作

本文对应Aggregation Operations — MongoDB Manual 正文 此章节主要介绍了Aggregation Pipeline&#xff0c;其实就是将若干个聚合操作放在管道中进行执行&#xff0c;每一个聚合操作的结果作为下一个聚合操作的输入&#xff0c;每个聚合指令被称为一个stage。 在正式开始学…

Vue使用jspdf和html2canvas组件库结合导出PDF文件

效果图&#xff1a; 1、安装依赖&#xff1a; npm install html2canvas --save npm install jspdf --save 或 yarn add html2canvas --save yarn add jspdf --save 2、封装全局调用方法&#xff1a;this.$exportPDF(#id,文件名) 新建js文件&#xff1a;/utils/html2Pdf.js&am…

Nginx 服务优化与防盗链

Nginx隐藏版本号、服务名 1. 改配置文件 HTTP模块中添加命令&#xff1a;server_tokens off&#xff1b; 关闭防火墙、安全机制 将配置文件备份一份&#xff0c;再进行配置 2. 改源码包 /opt/nginx-1.22.0/src/core/nginx.h #define NGINX_VERSION "1.1.1" #修改…

SSH公网远程直连Docker容器

文章目录 1. 下载docker镜像2. 安装ssh服务3. 本地局域网测试4. 安装cpolar5. 配置公网访问地址6. SSH公网远程连接测试7.固定连接公网地址8. SSH固定地址连接测试 在某些特殊需求下,我们想ssh直接远程连接docker 容器,下面我们介绍结合cpolar工具实现ssh远程直接连接docker容器…

【BASH】回顾与知识点梳理(二十七)

【BASH】回顾与知识点梳理 二十七 二十七. 磁盘配额(Quota)27.1 磁盘配额 (Quota) 的应用与实作什么是 QuotaQuota 的一般用途Quota 的使用限制Quota 的规范设定项目 27.2 一个 XFS 文件系统的 Quota 实作范例实作 Quota 流程&#xff1a;设定账号实作 Quota 流程-1&#xff1a…

使用 PostgreSQL 创建全文搜索引擎2:Postgres 与 Elasticsearch

使用 PostgreSQL 作为全文搜索引擎很诱人&#xff0c;因为它需要的基础设施较少。但它的搜索相关功能集是否足以与基于 Lucene 的替代方案竞争&#xff1f; 在第 1 部分中&#xff0c;我们深入研究了 PostgreSQL 全文搜索的功能&#xff0c;并探讨了如何实现相关性提升(releva…

如何选择最佳的文件传输协议?(FTP、TFTP、Raysync)

在数字化时代&#xff0c;通过互联网传输文件是一项常见的任务。因此&#xff0c;选择适合您企业需求的文件传输协议非常重要。 文件传输协议是发送方和接收方之间的一套规则和信息。它的作用就像网络两端都能理解的一种语言&#xff0c;使得数据可以正确输出并带有正确的文件…

SRM订单管理:优化供应商关系

一、概述SRM订单管理的概念&#xff1a; SRM订单管理是指在供应商关系管理过程中&#xff0c;有效管理和控制订单的创建、处理和交付。它涉及与供应商之间的沟通、合作和协调&#xff0c;旨在实现订单的准确性、可靠性和及时性。 二、SRM订单管理的流程&#xff1a; 1. 订单创…

问道管理:缩量小幅上涨说明什么?

股市里面&#xff0c;股票价格上涨或跌落都是常见现象。可是关于那些在商场上寻求收益的出资者来说&#xff0c;他们需要对每一个股市中的价格动摇有深化的了解&#xff0c;以便做出更正确的出资决策。最近&#xff0c;出资者们发现商场缩量小幅上涨的现象时有发生&#xff0c;…

SpringBoot复习:(51)默认情况下DataSource是怎么创建出来的,是什么类型的?

DataSource是通过DataSourceAutoConfiguration创建的&#xff0c;这个类代码如下&#xff1a; 可以看到DataSourceAutoConfiguration有个静态内部类PooledDataSourceConfiguration,在这个类上有个Import注解&#xff0c;导入了DataSourceConfiguration.Hikari这个类&#xff0…

8.15号经典模型复习笔记

文章目录 Deep Residual Learning for Image Recognition(CVPR2016)方法 Densely Connected Convolutional Networks&#xff08;CVPR2017&#xff09;方法 EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks&#xff08;ICML2019&#xff09;方法 Re…

科创板四周年,6家半导体厂商被评为最具价值上市公司

7月21日&#xff0c;由聚焦科创板的权威媒体《科创板日报》及财联社联合发起的“科创板开市四周年评选”榜单正式发布&#xff0c;其包括“2023最具价值科创板上市公司”、“2023最具创新力科创板上市公司”等子榜单。 其中&#xff0c;“2023最具价值科创板上市公司”子榜单评…

九州未来参与编制的开源领域3项团体标准获批发布

日前&#xff0c;中电标2023年第21号团体标准公告正式发布&#xff0c;其中由九州未来参与编制的3项开源领域团体标准正式获批发布&#xff0c;于2023年8月1日正式实施。 具体内容如下&#xff1a; 《T/CESA 1269-2023 信息技术 开源 术语与综述》&#xff0c;本文件界定了信息…