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

React合成事件的原理是什么

事件介绍

什么是事件?

事件是在编程时系统内发生的动作或者发生的事情,而开发者可以某种方式对事件做出回应,而这里有几个先决条件

  • 事件对象

  • 给事件对象注册事件,当事件被触发后需要做什么

  • 事件触发

举个例子

在机场等待检票,听到广播后排队检票

  • 事件对象,也就是检票事件

  • 事件注册/监听,也就是我们在机场等待的时候

  • 事件触发,广播响起开始检票

  • 事件触发之后的回调,我们开始排队...

// 检票事件
var event = new Event('check-ticket');
// 事件注册,并编写回调
elem.addEventListener('check-ticket', function (e) { // 开始排队LiningUp()
});
// 事件触发,广播开始检票
elem.dispatchEvent(event);

React合成事件基础知识

什么是合成事件?

不是浏览器本身触发的事件,自己创建和触发的事件。

在React中事件的写法和原生事件写法的区别?

  • React 事件的命名采用小驼峰式(camelCase),而不是纯小写。

  • 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。

// HTML中的写法
<button onclick="handleClick()">Activate Lasers
</button>
// React中的写法
<button onClick={handleClick}>Activate Lasers
</button>

为什么会有合成事件?

  • 对事件进行归类,可以在事件产生的任务上包含不同的优先级

  • 提供合成事件对象,抹平浏览器的兼容性差异

合成事件机制简述

提供了一种“顶层注册,事件收集,统一触发”的事件机制

  • “顶层注册”,其实是在root元素上绑定一个统一的事件处理函数

  • “事件收集”, 事件触发时(实际上是root上的事件处理函数被执行),构造合成事件对象,按照冒泡或捕获的路径去组件中收集真正的事件处理函数

  • “统一触发”,在收集过程之后,对收集的事件逐一执行,并共享同一个合成事件对象

React合成事件实现原理

事件注册

时机:在fiber节点进入render阶段的complete阶段时,名为onClick的props会被识别为事件进行处理

function setInitialDOMProperties(tag: string,domElement: Element,rootContainerElement: Element | Document,nextProps: Object,isCustomComponentTag: boolean,
): void {for (const propKey in nextProps) {if (!nextProps.hasOwnProperty(propKey)) {...} else if (registrationNameDependencies.hasOwnProperty(propKey)) {// 如果propKey属于事件类型,则进行事件绑定ensureListeningTo(rootContainerElement, propKey, domElement);}}}
}
// registrationNameDependencies为合成事件名和真实事件的映射,格式如下:
{onChange: ['change', 'click', 'focusin', 'focusout', 'input', 'keydown', 'keyup', 'selectionchange']onChangeCapture: ['change', 'click', 'focusin', 'focusout', 'input', 'keydown', 'keyup', 'selectionchange']onClick: ['click']
}

调用ensureListeningTo进行事件绑定

然后调用DOMPluginEventSystem.js中的addTrappedEventListener进行事件注册,而监听器listener是React通过createEventListenerWrapperWithPriority创建出优先级不同的时间监听包装器

总的来说,会有三种事件监听包装器:

  • dispatchDiscreteEvent: 处理离散事件

  • dispatchUserBlockingUpdate:处理用户阻塞事件

  • dispatchEvent:处理连续事件

事件触发-事件监听器做了什么

负责以不同的优先级权重来触发真正的事件流程

createEventListenerWrapperWithPriority中,根据eventPriority事件优先级不同调用事件dispatchDiscreteEventdispatchUserBlockingUpdatedispatchUserBlockingUpdatedispatchEvent进行事件绑定

export function createEventListenerWrapperWithPriority(targetContainer: EventTarget,domEventName: DOMEventName,eventSystemFlags: EventSystemFlags,
): Function {const eventPriority = getEventPriorityForPluginSystem(domEventName);let listenerWrapper;switch (eventPriority) {case DiscreteEvent:listenerWrapper = dispatchDiscreteEvent;break;case UserBlockingEvent:listenerWrapper = dispatchUserBlockingUpdate;break;case ContinuousEvent:default:listenerWrapper = dispatchEvent;break;}return listenerWrapper.bind(null,domEventName,eventSystemFlags,targetContainer,);
}

在每一个事件绑定函数中,会调用dispatchEventsForPlugins函数

  • 合成事件,放入dispatchQueue中

    而dispatchQueue由两部分组成

    • 合成事件对象

    • 多个listeners(可以共用一个合成事件对象)

  • 执行事件执行路径下的事件,从dispatchQueue中取出事件对象events和具体执行路径listeners,然后遍历执行

function dispatchEventsForPlugins(domEventName: DOMEventName,eventSystemFlags: EventSystemFlags,nativeEvent: AnyNativeEvent,targetInst: null | Fiber,targetContainer: EventTarget,
): void {const nativeEventTarget = getEventTarget(nativeEvent);const dispatchQueue: DispatchQueue = [];// 通过不同事件类型插件进行事件对象合成,将合成事件放入事件队列中:dispatchQueue中extractEvents(dispatchQueue,domEventName,targetInst,nativeEvent,nativeEventTarget,eventSystemFlags,targetContainer,);// 执行dispatchQueue中具体事件执行路径下的事件processDispatchQueue(dispatchQueue, eventSystemFlags);
}
// 通过extractEvents合成的dispatchQueue的格式如下
[{// event是合成事件对象event, // 具体的事件执行路径,currentTarget、listener等信息listeners: [{currentTarget: div.counter,instance: FiberNode {tag: 5, key: null, elementType: 'div', type: 'div', stateNode: div.counter, …},listener: e => {…}},{currentTarget: div.counter-parent,instance: FiberNode {tag: 5, key: null, elementType: 'div', type: 'div', stateNode: div.counter, …},listener: e => {…}}] }
]

React中模拟冒泡和捕获

原理:收集的事件放在dispatchQueue数组中,而冒泡和捕获的区别在于执行时机和顺序,那么我们只需要对数组按照不同顺序循环执行即可

function processDispatchQueueItemsInOrder(event: ReactSyntheticEvent,dispatchListeners: Array<DispatchListener>,inCapturePhase: boolean,
): void {let previousInstance;if (inCapturePhase) {// 事件捕获倒序循环for (let i = dispatchListeners.length - 1; i >= 0; i--) {const {instance, currentTarget, listener} = dispatchListeners[i];if (instance !== previousInstance && event.isPropagationStopped()) {return;}// 执行事件,传入event对象,和currentTargetexecuteDispatch(event, listener, currentTarget);previousInstance = instance;}} else {// 事件冒泡正序循环for (let i = 0; i < dispatchListeners.length; i++) {const {instance, currentTarget, listener} = dispatchListeners[i];// 如果事件对象阻止了冒泡,则return掉循环过程if (instance !== previousInstance && event.isPropagationStopped()) {return;}executeDispatch(event, listener, currentTarget);previousInstance = instance;}}
}

http://www.lryc.cn/news/15706.html

相关文章:

  • Arduino-交通灯
  • 【论文笔记】Manhattan-SDF == ZJU == CVPR‘2022 Oral
  • 好消息!Ellab(易来博)官方微信公众号开通了!携虹科提供专业验证和监测解决方案
  • 想要去字节跳动面试Android岗,给你这些面试知识点
  • Java的Lambda表达式的使用
  • Spring MVC 源码 - HandlerMapping 组件(三)之 AbstractHandlerMethodMapping
  • 超店有数,为什么商家要使用tiktok达人进行营销推广呢?
  • 【分享】订阅万里牛集简云连接器同步企业采购审批至万里牛系统
  • C++类和对象_02----对象模型和this指针
  • 瑞芯微RK3568开发:烧录过程
  • 【数据结构】——树和二叉树的概念
  • Meta分析在生态环境领域里的应用
  • PrivateLoader PPI服务发现RisePro恶意软件窃取分发信息
  • SQL93 返回购买 prod_id 为 BR01 的产品的所有顾客的电子邮件(一)
  • Git ---- 概述
  • 用 tensorflow.js 做了一个动漫分类的功能(二)
  • 小林coding
  • 操作系统真相还原_第6章:完善内核
  • SmoothNLP新词发现算法的改进实现
  • 实时渲染为什么快,能不能局域网部署点量云
  • 网络游戏该如何防护ddos/cc攻击
  • 项目管理体系1-4练习题1-10答案
  • sHMIctrl智能屏幕使用记录
  • 2.20 crm day01 配置路由router less使用 axios二次封装
  • 【LeetCode】剑指 Offer 10- I. 斐波那契数列 p74 -- Java Version
  • 论文笔记:DropMessage: Unifying Random Dropping for Graph Neural Networks
  • 木鱼cms系统审计小结
  • 软件测试面试-一线大厂必问的测试思维面试题
  • 企业级分布式应用服务 EDAS
  • 弄懂 Websocket 你得知道的这 3 点