下一个泡泡玛特工程师是谁呢?

收集一些自己用到的prompt

2023-04-05 · 1 min read

机械键盘相关参数

机械键盘相关参数记录

2023-04-05 · 1 min read

Web 前端性能优化

常用的web前端性能优化手段

2023-03-19 · 3 min read

Blender常用快捷键

自己经常用到的一些快捷键记录

2022-12-11 · 1 min read

Docker 常用命令

精简 Docker 常用命令

2022-11-06 · 1 min read

项目管理

项目管理相关知识

2022-11-06 · 1 min read

JS沙箱sandbox的各种实现

我们把Js隔离机制常常称作沙箱

2022-09-18 · 1 min read

Amazing!Solid 比react还react

今天来介绍2个amazing的东东

2022-09-12 · 1 min read

puppeteer应用

使用傀儡师来操作浏览器这个傀儡吧

2022-09-11 · 1 min read

做一个web termianl

前端react, 后端nodejs, 直接可用版web termianl

2022-07-17 · 1 min read

TypeScript里常用的工具类型

经常用到的工具类型,提取抽离出来,供以后复用

2022-06-26 · 1 min read

Vim大法好

想要丢掉鼠标,试试Vim

2022-06-26 · 1 min read

碧血丹心

无论时光如何沾染风霜,也永似红日光

2022-05-10 · 1 min read

chrome V8 引擎中的垃圾回收机制

V8引擎就是nodejs的发动机

2022-03-03 · 1 min read

如何开发一个cli

命令行交互界面是程序员必备的工具,如何开发一个呢?

2022-02-21 · 1 min read

Javascript中的哲学

道生一,一生二,二生三,三生万物

2022-02-20 · 1 min read

React Redux 实现 (Context 版)

React Redux 实现 (Context 版)

2022-02-16 · 1 min read

mobx-react 使用

虽然不常用,但是可以学一下

2022-02-16 · 1 min read

Less中的for和forEach循环

循环的使用是保持代码干燥和避免重复的好方法

2022-02-15 · 1 min read

Webpack Splitchunks 详解

webpack 优化

2022-02-15 · 1 min read

ssh-keygen命令详解

为ssh生成、管理和转换认证密钥

2022-02-15 · 1 min read

使用CURL发送POST请求

curl 是常用的命令行工具,用来请求 Web 服务器。

2022-02-15 · 1 min read

Webpack Plugin 开发

让我们来学一下如何开发一个webpack插件

2022-02-14 · 1 min read

JS 实现两个大数相加?

algo-adding-large-numbers

2022-02-10 · 1 min read

字典树 trie

字典树 trie

2022-02-09 · 1 min read

浏览器原理问题

浏览器原理问题

2022-01-11 · 1 min read

MacBook快速进入一个文件夹目录

mac如何快速进入一个文件夹

2021-10-26 · 1 min read

react合成事件

react-synthetic-event

2021-10-25 · 1 min read

mini webpack实现

通过babel核心来实现迷你版的webpack

2021-10-10 · 1 min read

设计模式

在程序设计中有很多实用的设计模式,而其中大部分语言的实现都是基于类

2021-10-10 · 1 min read

babel核心

babel核心介绍

2021-10-07 · 1 min read

React 15 和 React 16 的区别

react-15-16

2021-10-06 · 1 min read

React性能优化

浅谈react性能优化的方法

2021-10-05 · 1 min read

交通信号灯实现

如何用js来实现交通信号灯呢

2021-09-25 · 1 min read

内存管理

前端中的内存管理

2021-09-25 · 1 min read

前端安全

前端关于安全方面的知识

2021-09-25 · 2 min read

网络和并发

http各版本对于并发的支持,前端如何控制并发量?

2021-09-25 · 1 min read

跨域方法

总结了9种跨域方法

2021-09-24 · 1 min read

react virtualList 虚拟列表无限滚动实现

用react实现虚拟滚动

2021-09-16 · 1 min read

监控埋点方案

前端监控埋点方案

2021-09-16 · 1 min read

Mini useEffect实现

如何实现useEffect?

2021-09-11 · 1 min read

Mini useState 实现

我们来思考一下useState是怎么实现的呢?

2021-09-11 · 1 min read

React Fiber

react-fiber

2021-09-11 · 1 min read

React class组件和function组件异同

类组件和函数组件有何相同点有何不同点呢?

2021-09-11 · 1 min read

Lodash Get 实现

algo-lodash-get

2021-09-08 · 1 min read

手写reduce实现

algo-reduce

2021-09-08 · 1 min read

Hooks 原理概览

react-hooks

2021-09-05 · 1 min read

Hook原理——状态Hook

react-hook-state

2021-09-05 · 1 min read

手写Mini Redux实现

手写一个简易版的redux实现,包含了核心逻辑

2021-09-04 · 1 min read

legacy和concurrent模式

react-legacy-concurrent

2021-09-01 · 1 min read

react架构

总体 react 的核心可以用 ui=fn(state)来表示 3 大核心对象、3 大核心阶段、2 大工作循环 Scheduler(调度器): 排序优先级,让优先级高的任务先进行 reconcile Reconciler…

2021-09-01 · 2 min read

react核心api和jsx

为什么要有jsx,为什么会有虚拟bom

2021-09-01 · 1 min read

setState是同步的还是异步的

react-setstate-usestate

2021-08-31 · 1 min read

如何使用NodeJs创建HTTP服务?

如何使用NodeJs创建HTTP服务?

2021-08-17 · 1 min read

NodeJS 事件循环模型

nodejs-eventloop

2021-08-11 · 2 min read

Buffer

nodejs中的内存管理

2021-08-10 · 1 min read

微前端解决方案-qiankun

目前国内最好的微前端解决方案-qiankun

2021-08-10 · 1 min read

React Mini版实现(1)

学一门技术最好的方法就是做一个其玩具版的实现,我们来尝试实现一下react和react-dom最简单版本吧

2021-08-04 · 1 min read

brew安装

brew 是 MacOS 上的包管理工具,可以简化 macOS 和 Linux 操作系统上软件的安装。

2021-08-04 · 1 min read

CommonJS简易版实现

CommonJS我们经常用,如何实现一个简易版的commonJS呢?

2021-08-01 · 1 min read

极品透明Dashboard样式分享

一个极品透明Dashboard样式分享

2021-07-30 · 1 min read

Stream

nodejs中的流

2021-07-11 · 1 min read

NodeJS全局对象

JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。

2021-05-18 · 1 min read

如何部署Nodejs服务

如何快速的部署一个Nodejs服务到公网呢?

2021-05-12 · 1 min read

Events

events模块是node的核心模块之一,几乎所有常用的node模块都继承了events模块,比如http、fs等。

2021-05-11 · 1 min read

用JS绘制背景,让CSS直接使用 'background:paint(xxx)'

CSS对象新特性,新的background设置方式

2021-04-30 · 1 min read

Block Formatting Context 块级格式化上下文

可以将BFC看成是元素的一种属性,拥有了这种属性的元素就会使他的子元素与世隔绝,不会影响到外部其他元素

2021-04-17 · 1 min read

如何使用Nodejs来创建一个TCP/UDP服务?

如何使用Nodejs来创建一个TCP/UDP服务?

2021-04-17 · 1 min read

Mac使用tree生成目录结构

程序员经常会有需求,需要列出项目的结构树。Mac或者Linux下可以使用tree列出项目结构

2021-04-04 · 1 min read

常见算法

一些比较常见算法

2021-03-20 · 1 min read

前端缓存

对于性能优化离不开缓存

2021-02-28 · 1 min read

前端答疑

一些比较常见的问题

2020-09-02 · 1 min read

Nodejs 网络 & HTTP

nodejs-network

2020-08-14 · 1 min read

JavaScript AST 抽象语法树

源代码的抽象语法结构的树状表现形式

2020-08-02 · 1 min read

nodejs里面向切面编程的一种范式

在一些场景下我们可能需要一种面向切面的编程方式

2020-08-01 · 1 min read

各种JS模块化特性

AMD、CMD、CJS、ESM

2020-05-30 · 1 min read

手写Ajax实现

使用HMR一步步实现Ajax

2020-05-30 · min read

手写PromiseA+实现

如何自己实现promiseA+规范,手写一个promise实现

2020-04-30 · 1 min read

debug和内存泄露

nodejs的debug方法

2020-04-08 · 1 min read

Javascript prototype 原型链

js-prototype

2019-09-07 · 1 min read

this指针、作用域

this是在执行时动态读取上下文决定的,不是在定义时决定

2019-06-14 · 1 min read

call、apply、bind的极简实现

使用symbol实

2019-06-03 · 1 min read

CSS联合选择器区分列表元素个数不同所要求的不同样式

对于列表,在有些时候针对于不同个数的item会有不同的显示,比如col份数,用js固然可以,是否可以用css更简便的实现呢?

2019-04-30 · 1 min read

TypeScript基础

介绍TypeScript基础知识

2019-04-30 · 1 min read

JS中的变量提升

为什么js当时要这样设计

2019-03-07 · 1 min read

ES6之Class

关于ES6里的class, 我们有什么不知道的事?

2019-03-01 · 1 min read

HTTP详解

HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传输协议

2019-02-17 · 2 min read

极简代码实现节流Throttle和防抖Debounce

使用各9行代码实现节流和防抖函数

2019-01-30 · 1 min read

Javascript 事件循环 EventLoop

js-eventloop

2018-09-07 · 1 min read

POST和GET区别

POST和GET区别是什么呢?

2018-08-14 · 1 min read

正则表达式

正则表达式一锅端

2018-07-30 · 1 min read

cloneDeep 深克隆实现

algo-clonedeep

2017-09-07 · 1 min read

webpack

engin-webpack

2017-09-07 · 1 min read

http1.1和http2.0有什么区别

http1.1和http2.0有什么区别

2017-06-14 · 1 min read

手写EventEmitter事件巴士

咱们来手写实现一个EventEmitter事件巴士

2017-01-10 · 1 min read

OOP 面向对象编程

对象是什么?为什么要面向对象?

2016-05-08 · 1 min read
Stay hungry & Stay foolish
战歌
16
The Reluctant Warrior
Immediate Music
To Glory
Two Steps From Hell
Victory
Two Steps From Hell
Empire of Angels
Thomas Bergersen
Serenata Immortale
Immediate Music
Cornfield Chase
Hans Zimmer
Tennessee
Hans Zimmer
He's a Pirate
Martin Ermen
Rise
Hans Zimmer
On Thin Ice
Hans Zimmer
Angels Will Rise
Twisted Jukebo
When It All Falls Down
Audiomachine
Icarus
Ivan Torrent
Star Sky - Instrumental
Two Steps From Hell
亡灵序曲
L
Up Is Down
Hans Zimm
回到首页

react合成事件

袁官东
October 25th, 2021 · 1 min read
图:Amrit Pal Singh

React 并不会在该 DOM 元素上直接绑定事件处理器. React 内部自定义了一套事件系统,在这个系统上统一进行事件订阅和分发.

具体来讲,React 利用事件委托机制在 Document(17 是 rootFiber) 上统一监听 DOM 事件,再根据触发的 target 将事件分发到具体的组件实例。另外上面 e 是一个合成事件对象(SyntheticEvent), 而不是原始的 DOM 事件对象.

原理

从实现上讲, 主要分为 3 步:

  1. 监听原生事件: 对齐 DOM 元素和 fiber 元素
  2. 收集 listeners: 遍历 fiber 树, 收集所有监听本事件的 listener 函数.
  3. 派发合成事件: 构造合成事件, 遍历 listeners 进行派发.

动机

  1. 抹平浏览器之间的兼容性差异。
  2. 抽象跨平台事件机制
  3. React 打算做更多优化
  4. React 打算干预事件的分发

事件代理

  1. 区别于浏览器事件处理方式,React 并未将事件处理函数与对应的 DOM 节点直接关联,而是在顶层使用 了一个全局事件监听器监听所有的事件;
  2. React 会在内部维护一个映射表记录事件与组件事件处理函数的对应关系;
  3. 当某个事件触发时,React 根据这个内部映射表将事件分派给指定的事件处理函数;
  4. 当映射表中没有事件处理函数时,React 不做任何操作;
  5. 当一个组件安装或者卸载时,相应的事件处理函数会自动被添加到事件监听器的内部映射表中或从表中删除。

合成事件

与浏览器事件处理稍微有不同的是,React 中的事件处理程序所接收的事件参数是被称为“合成事件(SyntheticEvent)”的实例。

  1. 合成事件是对浏览器原生事件跨浏览器的封装,并与浏览器原生事件有着同样的接口,如 stopPropagation(),preventDefault()等,并且 这些接口是跨浏览器兼容的。
  2. 如果需要使用浏览器原生事件,可以通过合成事件的 nativeEvent 属性获取
  3. React 合成事件原理

使用 JSX,在 React 中绑定事件:

1<button onClick={this.onClick}>单击触发react事件</button>

React 并不是将 click 事件绑在该 div 的真实 DOM 上,而是在 document 处监听所有支持的事件,当事件发生并冒泡至 document 处时,React 将事件内容封装并交由真正的处理函数运行

2

基本概念

1
  • ReactEventListener - 事件处理器. 在这里进行事件处理器的绑定。当 DOM 触发事件时,会从这里开始调度分发到 React 组件树

  • ReactEventEmitter - 暴露接口给 React 组件层用于添加事件订阅

  • EventPluginHub - 如其名,这是一个‘插件插槽’,负责管理和注册各种插件。在事件分发时,调用插件来生成合成事件

  • Plugin - React 事件系统使用了插件机制来管理不同行为的事件。这些插件会处理自己感兴趣的事件类型,并生成合成事件对象。目前 ReactDOM 有以下几种插件类型:

  • SimpleEventPlugin - 简单事件, 处理一些比较通用的事件类型,例如 click、input、keyDown、mouseOver、mouseOut、pointerOver、pointerOut

  • EventPropagators 按照 DOM 事件传播的两个阶段,遍历 React 组件树,并收集所有组件的事件处理器.

  • EventBatching 负责批量执行事件队列和事件处理器,处理事件冒泡。

  • SyntheticEvent 这是‘合成’事件的基类,可以对应 DOM 的 Event 对象。只不过 React 为了减低内存损耗和垃圾回收,使用一个对象池来构建和释放事件对象, 也就是说 SyntheticEvent 不能用于异步引用,它在同步执行完事件处理器后就会被释放。

事件分类与优先级

SimpleEventPlugin 将事件类型划分成了三类, 对应不同的优先级(优先级由低到高):

  • DiscreteEvent 离散事件. 例如 blur、focus、 click、 submit、 touchStart. 这些事件都是离散触发的
  • UserBlockingEvent 用户阻塞事件. 例如 touchMove、mouseMove、scroll、drag、dragOver 等等。这些事件会’阻塞’用户的交互。
  • ContinuousEvent 可连续事件。例如 load、error、loadStart、abort、animationEnd. 这个优先级最高,也就是说它们应该是立即同步执行的,这就是 Continuous 的意义,即可连续的执行,不被打断.

React 有 5 个优先级级别:

  • Immediate - 这个优先级的任务会同步执行, 或者说要马上执行且不能中断
  • UserBlocking(250ms timeout) 这些任务一般是用户交互的结果, 需要即时得到反馈 .
  • Normal (5s timeout) 应对哪些不需要立即感受到的任务,例如网络请求
  • Low (10s timeout) 这些任务可以放后,但是最终应该得到执行. 例如分析通知
  • Idle (no timeout) 一些没有必要做的任务 (e.g. 比如隐藏的内容).

如何绑定

  1. 在 props 初始化和更新时会进行事件绑定。首先 React 会判断元素是否是媒体类型,媒体类型的事件是无法在 Document 监听的,所以会直接在元素上进行绑定
  2. 反之就在 rootFiber 上绑定. 这里面需要两个信息,一个就是上文提到的’事件依赖列表’, 比如 onMouseEnter 依赖 mouseover/mouseout; 第二个是 ReactBrowserEventEmitter 维护的’已订阅事件表’。事件处理器只需在 Document 订阅一次,所以相比在每个元素上订阅事件会节省很多资源.
1export function listenTo(
2 registrationName: string, // 注册名称,如onClick
3 mountAt: Document | Element | Node // 组件树容器,一般是Document
4): void {
5 const listeningSet = getListeningSetForElement(mountAt); // 已订阅事件表
6 const dependencies = registrationNameDependencies[registrationName]; // 事件依赖
7
8 for (let i = 0; i < dependencies.length; i++) {
9 const dependency = dependencies[i];
10 if (!listeningSet.has(dependency)) {
11 // 未订阅
12 switch (dependency) {
13 // ... 特殊的事件监听处理
14 default:
15 const isMediaEvent = mediaEventTypes.indexOf(dependency) !== -1;
16 if (!isMediaEvent) {
17 trapBubbledEvent(dependency, mountAt); // 设置事件处理器
18 }
19 break;
20 }
21 listeningSet.add(dependency); // 更新已订阅表
22 }
23 }
24}

如何触发

不同的事件类型有不同的事件处理器, 它们的区别是调度的优先级不一样:

1// 离散事件
2// discrentUpdates 在UserBlocking优先级中执行
3function dispatchDiscreteEvent(topLevelType, eventSystemFlags, nativeEvent) {
4 flushDiscreteUpdatesIfNeeded(nativeEvent.timeStamp);
5 discreteUpdates(dispatchEvent, topLevelType, eventSystemFlags, nativeEvent);
6}
7
8// 阻塞事件
9function dispatchUserBlockingUpdate(
10 topLevelType,
11 eventSystemFlags,
12 nativeEvent
13) {
14 // 如果开启了enableUserBlockingEvents, 则在UserBlocking优先级中调度,
15 // 开启enableUserBlockingEvents可以防止饥饿问题,因为阻塞事件中有scroll、mouseMove这类频繁触发的事件
16 // 否则同步执行
17 if (enableUserBlockingEvents) {
18 runWithPriority(
19 UserBlockingPriority,
20 dispatchEvent.bind(null, topLevelType, eventSystemFlags, nativeEvent)
21 );
22 } else {
23 dispatchEvent(topLevelType, eventSystemFlags, nativeEvent);
24 }
25}
26
27// 可连续事件则直接同步调用dispatchEvent

最终都会调用 dispatchEvent 函数. dispatchEvent 中会从 DOM 原生事件对象获取事件触发的 target,再根据这个 target 获取关联的 React 节点实例.

插件是如何处理事件

SimpleEventPlugin

1const SimpleEventPlugin: PluginModule<MouseEvent> & {
2 getEventPriority: (topLevelType: TopLevelType) => EventPriority,
3} = {
4 eventTypes: eventTypes,
5 // 抽取事件对象
6 extractEvents: function (
7 topLevelType: TopLevelType,
8 targetInst: null | Fiber,
9 nativeEvent: MouseEvent,
10 nativeEventTarget: EventTarget
11 ): null | ReactSyntheticEvent {
12 // 事件配置
13 const dispatchConfig = topLevelEventsToDispatchConfig[topLevelType];
14
15 // 1️⃣ 根据事件类型获取SyntheticEvent子类事件构造器
16 let EventConstructor;
17 switch (topLevelType) {
18 // ...
19 case DOMTopLevelEventTypes.TOP_KEY_DOWN:
20 case DOMTopLevelEventTypes.TOP_KEY_UP:
21 EventConstructor = SyntheticKeyboardEvent;
22 break;
23 case DOMTopLevelEventTypes.TOP_BLUR:
24 case DOMTopLevelEventTypes.TOP_FOCUS:
25 EventConstructor = SyntheticFocusEvent;
26 break;
27 // ... 省略
28 case DOMTopLevelEventTypes.TOP_GOT_POINTER_CAPTURE:
29 // ...
30 case DOMTopLevelEventTypes.TOP_POINTER_UP:
31 EventConstructor = SyntheticPointerEvent;
32 break;
33 default:
34 EventConstructor = SyntheticEvent;
35 break;
36 }
37
38 // 2️⃣ 构造事件对象, 从对象池中获取
39 const event = EventConstructor.getPooled(
40 dispatchConfig,
41 targetInst,
42 nativeEvent,
43 nativeEventTarget
44 );
45
46 // 3️⃣ 根据DOM事件传播的顺序获取用户事件处理器
47 accumulateTwoPhaseDispatches(event);
48 return event;
49 },
50};
  1. 根据事件的类型确定 SyntheticEvent 构造器
  2. 构造 SyntheticEvent 对象。
  3. 根据 DOM 事件传播的顺序获取用户事件处理器列表

为了避免频繁创建和释放事件对象导致性能损耗(对象创建和垃圾回收),React 使用一个事件池来负责管理事件对象,使用完的事件对象会放回池中,以备后续的复用。

More articles from Favori 重剑

mini webpack实现

通过babel核心来实现迷你版的webpack

October 10th, 2021 · 1 min read

设计模式

在程序设计中有很多实用的设计模式,而其中大部分语言的实现都是基于类

October 10th, 2021 · 1 min read
© 2016–2023 Favori 重剑
Link to $https://github.com/yuanguandongLink to $https://favori.zcool.com.cn/Link to $https://codepen.io/favori