事件机制

本系列以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. 【待补充】[Python_1] Python 安装

    0. 说明 安装教程网上有很多,等下次安装再补充笔记 Python 下载地址

  2. Mybatis 学习笔记

    可学习渠道  MYBATIS 入门教程 1. Mybatis 介绍 Mybatis 是 sqlmap 技术,对 JDBC 进行封装,将大量的 SQL 语句外部化. 平时我们都用JDBC访问数据库,除了 ...

  3. Centos7源码安装httpd2.4版本web服务器

    我们的系统平台是在centos7.5的环境下安装httpd2.4版本的软件,2.4版本的软件有一个特征就是需要安装arp包以及arp-util包才可以. 1.首先是下载httpd2.4版本的包,以及安 ...

  4. 3.Dubbo2.5.3快速启动Hello World

    转载请出自出处:http://www.cnblogs.com/hd3013779515/ 1.服务提供者 (1)工程目录 (2)接口代码 HelloService.java package cn.lj ...

  5. webstorm 2017 激活

    参考:https://blog.csdn.net/wangyingwing/article/details/79119592

  6. Vue图片懒加载

    图片懒加载的原理 先将img标签中的src链接设为同一张图片(空白图片),将其真正的图片地址存储再img标签的自定义属性中(比如data-src).当js监听到该图片元素进入可视窗口时,即将自定义属性 ...

  7. python textwrap.md

    textwrap textwrap模块可以用来格式化文本, 使其在某些场合输出更美观. 他提供了一些类似于在很多文本编辑器中都有的段落包装或填充特性的程序功能. Example Data 本节中的示例 ...

  8. CSP vs Actor Go vs Erlang

    源于从Erlang到Go的一些思维碰撞,就像当初从C++到Erlang一样,整理下来记于此. Actor Actor模型,又叫参与者模型,其”一切皆参与者(actor)”的理念与面向对象编程的“一切皆 ...

  9. [GXOI/GZOI2019]逼死强迫症

    题目 设我们最后的答案是\(g_n\) 我们发现在最后竖着放一个\(2\times 1\)的,和横着放两个\(1\times 2\)的就可以区分开之前的方案了 所以如果仅仅使用\(1\times 2\ ...

  10. 问题解决:Apache: You don't have permission to access / on this server

    虚拟主机(Virtual Host)是指在一个机器上运行多个网络站点 (比如:www.company1.com和www.company2.com). 如果每个网络站点拥有不同的IP地址,则虚拟主机可以 ...