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

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

摘要

在上一篇中,我们实现了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.tagswitch (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)}
}

相关文章:

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

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

Android app专项测试之耗电量测试

前言 耗电量指标 待机时间成关注目标 提升用户体验 通过不同的测试场景&#xff0c;找出app高耗电的场景并解决 01、需要的环境准备 1、python2.7(必须是2.7&#xff0c;3.X版本是不支持的) 2、golang语言的开发环境 3、Android SDK 此三个的环境搭建这里就不详细说了&am…...

设计模式-面试常问

1.单例模式 保证系统中&#xff0c;一个类&#xff0c;只有一个实例&#xff0c;并且提供对外访问。 优点&#xff1a;只有一个对象&#xff0c;可以节省资源。适合频繁创建销毁对象的场景。 实现&#xff1a;要用到static&#xff0c;静态私有对象。暴露单例的静态方法。 &…...

聊聊在集群环境中本地缓存如何进行同步

前言 之前有发过一篇文章聊聊如何利用redis实现多级缓存同步。有个读者就给我留言说&#xff0c;因为他项目的redis版本不是6.0版本&#xff0c;因此他使用我文章介绍通过MQ来实现本地缓存同步&#xff0c;他的同步流程大概如下图 他原来的业务流程是每天凌晨开启定时器去爬取…...

【C++深入浅出】初识C++上篇(关键字,命名空间,输入输出,缺省参数,函数重载)

目录 一. 前言 二. 什么是C 三. C关键字初探 四. 命名空间 4.1 为什么要引入命名空间 4.2 命名空间的定义 4.3 命名空间使用 五. C的输入输出 六. 缺省参数 6.1 缺省参数的概念 6.2 缺省参数的分类 七. 函数重载 7.1 函数重载的概念 7.2 函数重载的条件 7.3 C支…...

租房合同范本

房屋租赁合同 甲方&#xff08;出租方&#xff09;&#xff1a; 身份证&#xff1a; 联系电话&#xff1a; 乙方&#xff08;承租方&#xff09;&#xff1a; 身份证&#xff1a; 联系电话&#xff1a; …...

轻薄的ESL电子标签有哪些特性?

在智慧物联逐渐走进千万家的当下&#xff0c;技术变革更加日新月异。ESL电子标签作为科技物联的重要组成部分&#xff0c;是推动千行百业数字化转型的重要技术&#xff0c;促进物联网产业的蓬勃发展。在智慧零售、智慧办公、智慧仓储等领域&#xff0c;ESL电子标签在未来是不可…...

AI 实力:利用 Docker 简化机器学习应用程序的部署和可扩展性

利用 Docker 的强大功能&#xff1a;简化部署解决方案、确保可扩展性并简化机器学习模型的 CI/CD 流程。 近年来&#xff0c;机器学习 (ML) 出现了爆炸性增长&#xff0c;导致对健壮、可扩展且高效的部署方法的需求不断增加。由于训练和服务环境之间的差异或扩展的困难等因素&a…...

商用汽车转向系统常见故障解析

摘要&#xff1a; 车辆转向系统是用于改变或保持汽车行驶方向的专门机构。其作用是使汽车在行驶过程中能按照驾驶员的操纵意图而适时地改变其行驶方向&#xff0c;并在受到路面传来的偶然冲击及车辆意外地偏离行驶方向时&#xff0c;能与行驶系统配合共同保持车辆继续稳定行驶…...

Python中的MetaPathFinder

MetaPathFinder 是 Python 导入系统中的一个关键组件&#xff0c;它与 sys.meta_path 列表紧密相关。sys.meta_path 是一个包含 MetaPathFinder 实例的列表&#xff0c;这些实例用于自定义模块的查找和加载逻辑。当使用 import 语句尝试导入一个模块时&#xff0c;Python 会遍历…...

工控机防病毒

2月3日&#xff0c;作为全球最大的半导体制造设备和服务供应商&#xff0c;美国应用材料公司&#xff08;Applied Materials&#xff09;表示&#xff0c;有一家上游供应商遭到勒索软件攻击&#xff0c;由此产生的关联影响预计将给下季度造成2.5亿美元&#xff08;约合人民币17…...

LangChain手记 Question Answer 问答系统

整理并翻译自DeepLearning.AILangChain的官方课程&#xff1a;Question Answer&#xff08;源代码可见&#xff09; 本节介绍使用LangChian构建文档上的问答系统&#xff0c;可以实现给定一个PDF文档&#xff0c;询问关于文档上出现过的某个信息点&#xff0c;LLM可以给出关于该…...

如何优化css中的一些昂贵属性

如何优化css中的一些昂贵属性 就性能而言&#xff0c;某些 CSS 属性比其他属性的成本更高。如果使用不当&#xff0c;它们可能会减慢我们的网页速度并降低对用户的响应速度。在本文中&#xff0c;我们将探讨一些成本最高的 CSS 属性以及如何优化它们。 box-shadow box-shado…...

基于安防监控EasyCVR视频汇聚融合技术的运输管理系统的分析

一、项目背景 近年来&#xff0c;随着物流行业迅速发展&#xff0c;物流运输费用高、运输过程不透明、货损货差率高、供应链协同能力差等问题不断涌现&#xff0c;严重影响了物流作业效率&#xff0c;市场对于运输管理数字化需求愈发迫切。当前运输行业存在的难题如下&#xf…...

在WordPress站点中展示阅读量等流量分析数据(超详细实现)

这篇文章也可以在我的博客中查看 关于本文 专业的流量统计系统能够相对真实地反应网站的访问情况。 这些数据可以在后台很好地进行分析统计&#xff0c;但有时我们希望在网站前端展示一些数据 最常见的情景就是&#xff1a;展示页面的浏览量 这简单的操作当然也可以通过简单…...

学习 Iterator 迭代器

今天看到一个面试题&#xff0c; 让下面解构赋值成立。 let [a,b] {a:1,b:2} 如果我们直接在浏览器输出这行代码&#xff0c;会直接报错&#xff0c;说是 {a:1,b:2} 不能迭代。 看了es6文档后&#xff0c;具有迭代器的就一下几种类型&#xff0c;没有Object类型&#xff0c;…...

JVM---垃圾回收算法介绍

目录 分代收集理论 三种垃圾回收算法 标记-清除算法&#xff08;最基础的、基本不用&#xff09; 标记-复制算法 标记-整理算法 正式因为jvm有了垃圾回收机制&#xff0c;作为java开发者不会去特备关注内存&#xff0c;不像C和C。 优点&#xff1a;开发门槛低、安全 缺点…...

Ubuntu一直卡死的问题(20.04)

Ubuntu一直卡死的问题&#xff08;18.04&#xff09;_ubuntu频繁死机_Mr.Yi的博客-CSDN博客 我自己的解决方法: 1、首先强制关机重启后&#xff0c;直接打开命令行查看磁盘的使用&#xff1a; df -h发现/dev/loop都沾满了&#xff0c;我们能需要做的就是把他们清理干净 sud…...

自动化测试用例设计实例

在编写用例之间&#xff0c;笔者再次强调几点编写自动化测试用例的原则&#xff1a; 1、一个脚本是一个完整的场景&#xff0c;从用户登陆操作到用户退出系统关闭浏览器。 2、一个脚本脚本只验证一个功能点&#xff0c;不要试图用户登陆系统后把所有的功能都进行验证再退出系统…...

CSS3基础

CSS3在CSS2的基础上增加了很多功能&#xff0c;如圆角、多背景、透明度、阴影等&#xff0c;以帮助开发人员解决一些实际问题。 1、初次使用CSS 与HTML5一样&#xff0c;CSS3也是一种标识语言&#xff0c;可以使用任意文本编辑器编写代码。下面简单介绍CSS3的基本用法。 1.1…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

10-Oracle 23 ai Vector Search 概述和参数

一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI&#xff0c;使用客户端或是内部自己搭建集成大模型的终端&#xff0c;加速与大型语言模型&#xff08;LLM&#xff09;的结合&#xff0c;同时使用检索增强生成&#xff08;Retrieval Augmented Generation &#…...

关于uniapp展示PDF的解决方案

在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项&#xff1a; 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库&#xff1a; npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...

学习一下用鸿蒙​​DevEco Studio HarmonyOS5实现百度地图

在鸿蒙&#xff08;HarmonyOS5&#xff09;中集成百度地图&#xff0c;可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API&#xff0c;可以构建跨设备的定位、导航和地图展示功能。 ​​1. 鸿蒙环境准备​​ ​​开发工具​​&#xff1a;下载安装 ​​De…...

WEB3全栈开发——面试专业技能点P8DevOps / 区块链部署

一、Hardhat / Foundry 进行合约部署 概念介绍 Hardhat 和 Foundry 都是以太坊智能合约开发的工具套件&#xff0c;支持合约的编译、测试和部署。 它们允许开发者在本地或测试网络快速开发智能合约&#xff0c;并部署到链上&#xff08;测试网或主网&#xff09;。 部署过程…...

python学习day39

图像数据与显存 知识点回顾 1.图像数据的格式&#xff1a;灰度和彩色数据 2.模型的定义 3.显存占用的4种地方 a.模型参数梯度参数 b.优化器参数 c.数据批量所占显存 d.神经元输出中间状态 4.batchisize和训练的关系 import torch import torchvision import torch.nn as nn imp…...

LangChain + LangSmith + DeepSeek 入门实战:构建代码生成助手

本文基于 Jupyter Notebook 实践代码&#xff0c;结合 LangChain、LangSmith 和 DeepSeek 大模型&#xff0c;手把手演示如何构建一个代码生成助手&#xff0c;并实现全流程追踪与优化。 一、环境准备与配置 1. 安装依赖 pip install langchain langchain_openai2. 设置环境变…...