事件机制

本系列以React v16.8.3为基础进行源码分析

React事件主要分为两部分: 事件注册与事件分发。下面先从事件注册说起。

事件注册

假设我们的程序如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>React App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
import React from 'react';
import ReactDOM from 'react-dom'; class ClickCounter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
handleClick = () => {
this.setState((state) => {
return {count: state.count + 1};
});
};
render() {
return [
<button key="1" onClick={this.handleClick}>Update counter</button>,
<span key="2">{this.state.count}</span>,
]
}
}
ReactDOM.hydrate(<ClickCounter />, document.getElementById('root'));

事件注册主要发生在初始化Dom属性的时候,调用setInitialProperties方法,对一些类型dom进行事件绑定。

switch (tag) {
case 'iframe':
case 'object':
trapBubbledEvent(TOP_LOAD, domElement);
props = rawProps;
break; case 'video':
case 'audio':
for (var i = 0; i < mediaEventTypes.length; i++) {
trapBubbledEvent(mediaEventTypes[i], domElement);
} props = rawProps;
break;
...
} setInitialDOMProperties(tag, domElement, rootContainerElement, props, isCustomComponentTag);
...

接着调用setInitialDOMProperties来真正初始化Dom属性。根据当前workInProgresspendingProps对象,给Dom对象设置属性。其中,有个分支会专门处理事件。

// registrationNameModules是一个map对象,存储着React支持的事件类型
else if (registrationNameModules.hasOwnProperty(propKey)) {
if (nextProp != null) {
ensureListeningTo(rootContainerElement, propKey);
}
}

执行ensureListeningTo方法:

// rootContainerElement为React应用的挂载点, registrationName为onClick
function ensureListeningTo(rootContainerElement, registrationName) {
// 判断rootContainerElement是document还是fragment
var isDocumentOrFragment = rootContainerElement.nodeType === DOCUMENT_NODE || rootContainerElement.nodeType === DOCUMENT_FRAGMENT_NODE;
// 获取rootContainerElement所在的document。
var doc = isDocumentOrFragment ? rootContainerElement : rootContainerElement.ownerDocument;
listenTo(registrationName, doc);
}

开始执行listenTo方法,注册事件入口。

// 获取当前已监听的原生事件类型的map
var isListening = getListeningForDocument(mountAt);
// 获取对应的原生事件类型,registrationNameDependencies存储了React事件类型与浏览器原生事件类型映射的一个map
var dependencies = registrationNameDependencies[registrationName];
for (var i = 0; i < dependencies.length; i++) {
var dependency = dependencies[i];
if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) {
switch (dependency) {
...// 除了scroll blur focus cancel close方法调trapCapturedEvent方法,invalid submit reset不处理之外,其余都调trapBubbledEvent方法。
default:
var isMediaEvent = mediaEventTypes.indexOf(dependency) !== -1;
if (!isMediaEvent) {
trapBubbledEvent(dependency, mountAt);
}
break;
}
// 标记该原生事件类型已被注册,下次注册同类型事件时会被忽略
isListening[dependency] = true;
}
}

trapCapturedEventtrapBubbledEvent的区别是前者注册捕获阶段的事件监听器,后者注册冒泡阶段的事件监听器。trapCapturedEvent使用比较少,所以重点看下trapBubbledEvent

//click document
function trapBubbledEvent(topLevelType, element) {
if (!element) {
return null;
}
// 从字面意能看出,前者是交互类事件,优先级会比普通事件高(click的分发者是dispatchInteractiveEvent)
var dispatch = isInteractiveTopLevelEventType(topLevelType) ? dispatchInteractiveEvent : dispatchEvent; // 注册事件,在冒泡阶段捕获
addEventBubbleListener(element, getRawEventName(topLevelType),
// Check if interactive and wrap in interactiveUpdates
dispatch.bind(null, topLevelType));
}

总结

可以发现,React把某一类型事件通过事件代理绑定到documentfragment上(fragment的情况比较少)。即workInProgresscomplete过程中,如果之前已经注册过onClick事件,后续workInProgress中的onClick事件将不再注册,统一由document中注册的click事件代理处理。

更好的阅读体验在我的github,欢迎

React笔记-事件注册的更多相关文章

  1. React笔记-事件分发

    事件分发 之前讲述了事件如何绑定在document上,那么具体事件触发的时候是如何分发到具体的监听者呢?我们接着上次注册的事件代理看.当我点击update counter按钮时,触发注册的click事 ...

  2. 深入理解React:事件机制原理

    目录 序言 DOM事件流 事件捕获阶段.处于目标阶段.事件冒泡阶段 addEventListener 方法 React 事件概述 事件注册 document 上注册 回调函数存储 事件分发 小结 参考 ...

  3. [技术博客]react native事件监听、与原生通信——实现对通知消息的响应

    在react native中会涉及到很多页面之间的参数传递问题.静态的参数传递通常利用组件的Props属性,在初始化组件时即可从父组件中将参数传递到子组件中.对于非父子关系的组件来说,无法直接传递参数 ...

  4. libevent (三) 事件注册与循环监听

    事件注册与循环监听 在libevent中为了监听某种事件的发生,设置事件触发后的回调函数,也就是说对该事件注册到当前的IO模型中. 事件注册 事件初始化 使用`event_new`函数来对事件进行初始 ...

  5. React笔记_(3)_react语法2

    React笔记_(3)_react语法2 state和refs props就是在render渲染时,向组件内传递的变量,这个传递是单向的,只能继承下来读取. 如何进行双向传递呢? state (状态机 ...

  6. Flex timer使用 keydown事件注册到stage

    Flex timer使用 keydown事件注册到stage: <?xml version="1.0" encoding="utf-8"?> < ...

  7. flex 事件注册和鼠标拖动

    flex 事件注册和鼠标拖动 <?xml version="1.0" encoding="utf-8"?> <s:Application xm ...

  8. [JavaScript] JavaScript事件注册,事件委托,冒泡,捕获,事件流

    面试题 event 事件 事件委托是什么? 如何阻止事件冒泡,阻止默认事件呢? Javascript 的事件流模型都有什么? 事件绑定和普通事件有什么区别? Event 对象 Event 对象,当事件 ...

  9. react 阻止事件冒泡

    前言 在学习react阻止事件冒泡,需要先了解 合成事件 和 原生事件 合成事件:在jsx中直接绑定的事件,就是合成事件: 原生事件: 通过js原生代码绑定的事件,就是原生事件: react事件:re ...

随机推荐

  1. Nginx的配置使用

    因为做了一个聚合支付的服务应用,对于交易系统来说,并发要求比较高,所以需要使用负载均衡来缓解并发的要求,自行开发又太费时费力,Nginx查了些资料基本满足需求,故对其做了下研究,记录下防止遗忘. 一. ...

  2. 1.3Windows控制台的简单操作:

    目录: 1.控制台的打开 2.盘符的跳转 3.跳转到某个文件夹 4.展示文件 5.清屏 6.历史记录 7.拖动添加路径 8.自动补齐 9.正常退出 1.打开控制台:系统图标键 + R,输入cmd,按回 ...

  3. SDN2017 第四次作业

    1.阅读 了解SDN控制器的发展 http://www.sdnlab.com/13306.html http://www.docin.com/p-1536626509.html 了解ryu控制器 ht ...

  4. github与本地电脑关联配置

    参考文章:https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/00137438585 ...

  5. Java实现XML和JavaBean互转 - XStream

    1.简介 XStream是一个序列化JavaBean为XML文件以及反序列化的工具. 2.准备 2.1.XML回顾 在学习这部分内容前,假设你对XML以及有一定的了解,至少能够分辨出XML的元素.属性 ...

  6. BZOJ4269:再见Xor(线性基)

    Description 给定N个数,你可以在这些数中任意选一些数出来,每个数可以选任意多次,试求出你能选出的数的异或和的最大值和严格次大值. Input 第一行一个正整数N. 接下来一行N个非负整数. ...

  7. Spring配置之context:annotation与、component-scan以及annotation-driven

    spring boot帮助我们隐藏了大量的细节,有些配置spring boot都是开启的,因此当我们查看遗留项目使用spring时候遇见问题一定细心排查 <!-- context:annotat ...

  8. ssl,proxy;部分http部分https;80,443,8080;nginx+tomcat;

    ..... user nobody; worker_processes 8; error_log /opt/logs/nginx/nginx_error.log crit; pid /usr/loca ...

  9. jmeter.bat配置(主要关于OOM)

    jmeter.bat是jmeter在windows系统下的启动文件.在使用jmeter压测,线程数设置过多时可能会报内存溢出(Out Of Memory Error),这时候可以去尝试调整一下jmet ...

  10. kafka 部署

    Windows平台kafka环境的搭建 https://blog.csdn.net/u010054969/article/details/70241478