React笔记-事件注册
事件机制
本系列以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属性。根据当前workInProgress的pendingProps对象,给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;
}
}
trapCapturedEvent与trapBubbledEvent的区别是前者注册捕获阶段的事件监听器,后者注册冒泡阶段的事件监听器。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把某一类型事件通过事件代理绑定到document或fragment上(fragment的情况比较少)。即workInProgress在complete过程中,如果之前已经注册过onClick事件,后续workInProgress中的onClick事件将不再注册,统一由document中注册的click事件代理处理。
更好的阅读体验在我的github,欢迎
React笔记-事件注册的更多相关文章
- React笔记-事件分发
事件分发 之前讲述了事件如何绑定在document上,那么具体事件触发的时候是如何分发到具体的监听者呢?我们接着上次注册的事件代理看.当我点击update counter按钮时,触发注册的click事 ...
- 深入理解React:事件机制原理
目录 序言 DOM事件流 事件捕获阶段.处于目标阶段.事件冒泡阶段 addEventListener 方法 React 事件概述 事件注册 document 上注册 回调函数存储 事件分发 小结 参考 ...
- [技术博客]react native事件监听、与原生通信——实现对通知消息的响应
在react native中会涉及到很多页面之间的参数传递问题.静态的参数传递通常利用组件的Props属性,在初始化组件时即可从父组件中将参数传递到子组件中.对于非父子关系的组件来说,无法直接传递参数 ...
- libevent (三) 事件注册与循环监听
事件注册与循环监听 在libevent中为了监听某种事件的发生,设置事件触发后的回调函数,也就是说对该事件注册到当前的IO模型中. 事件注册 事件初始化 使用`event_new`函数来对事件进行初始 ...
- React笔记_(3)_react语法2
React笔记_(3)_react语法2 state和refs props就是在render渲染时,向组件内传递的变量,这个传递是单向的,只能继承下来读取. 如何进行双向传递呢? state (状态机 ...
- Flex timer使用 keydown事件注册到stage
Flex timer使用 keydown事件注册到stage: <?xml version="1.0" encoding="utf-8"?> < ...
- flex 事件注册和鼠标拖动
flex 事件注册和鼠标拖动 <?xml version="1.0" encoding="utf-8"?> <s:Application xm ...
- [JavaScript] JavaScript事件注册,事件委托,冒泡,捕获,事件流
面试题 event 事件 事件委托是什么? 如何阻止事件冒泡,阻止默认事件呢? Javascript 的事件流模型都有什么? 事件绑定和普通事件有什么区别? Event 对象 Event 对象,当事件 ...
- react 阻止事件冒泡
前言 在学习react阻止事件冒泡,需要先了解 合成事件 和 原生事件 合成事件:在jsx中直接绑定的事件,就是合成事件: 原生事件: 通过js原生代码绑定的事件,就是原生事件: react事件:re ...
随机推荐
- Django2.0路由层-URLconf
目录 DJango2.0路由层-URLconf 概述 urlpatterns 实例 path转换器 自定义path转换器 使用正则表达式 命名组(有名分组) URLconf匹配请求URL中的哪些部分 ...
- 牢记 31 种 CSS 选择器用法
原文 The 30 CSS Selectors you Must Memorize 由 Jeffrey Way 发表于 2011 年 6 月,介绍了 30 种最常用的 CSS 选择器用法,多加了一种, ...
- October 24th, 2017 Week 43rd Tuesday
We can't give up trying. The fight was worth it. 我们不能放弃尝试,奋斗是值得的. When doing researches in some cutt ...
- [python] 统计某一路径下所有代码真实行数(空行已被过滤)
#-*- coding:utf-8 -*- ''' Created on 2018年8月15日 @author: anyd ''' import os list_line = [] filepat ...
- iptables设置规则
iptables -A INPUT -s 127.0.0.1 -p tcp --dport 8080 -j ACCEPT 添加到最后一条iptables -I INPUT -p tcp --dpor ...
- sublime设置node.js编译
1. 首先需安装node环境并配置好环境变量,安装教程. 2. 然后在sublime中打开工具(Tools)→编译系统(Build System)→新编译系统(New Build System) 3. ...
- Android Studio快捷键——编辑篇
Android Studio是官方推出的Android开发IDE,本系列讲解Android Studio中常用的快捷键,本文是该系列的第一篇,讲解的内容是与编辑代码相关的快捷键. 本文所讲快捷键基于A ...
- [GXOI/GZOI2019]逼死强迫症
题目 设我们最后的答案是\(g_n\) 我们发现在最后竖着放一个\(2\times 1\)的,和横着放两个\(1\times 2\)的就可以区分开之前的方案了 所以如果仅仅使用\(1\times 2\ ...
- Android--------TabLayout实现新闻客户端顶部导航栏
APP市场中大多数新闻App都有导航菜单,导航菜单是一组标签的集合,在新闻客户端中,每个标签标示一个新闻类别,对应下面ViewPager控件的一个分页面,今日头条, 网易新闻等. 随着版本迭代的更新, ...
- IIS7.5全站301跳转,内页+带参数url,这才是真正的全站跳转
说好的转型安全领域,可是我还是忍不住要给大家分享这个教程.因为这个问题很常见,大部分人都遇到了(可能你没注意),困扰了我很久,相信这是一篇真正适合你的IIS301跳转教程. 背景 说到301跳转,作为 ...