文档章节

React笔记-事件注册

o
 osc_a22drz29
发布于 2019/03/22 23:16
字数 706
阅读 0
收藏 0

精选30+云产品,助力企业轻松上云!>>>

事件机制

本系列以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,欢迎👏提issue。

o
粉丝 0
博文 500
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。
ReactNative快速入门笔记

ReactNative的文档地址有多个,如果你英文够好,就去研读官方的文档吧, 如果读原文比较吃力,中文官网也是不错的选择。 下面是我个人记录的一些笔记,仅供初学者入门参考 预科 入门React N...

木羽zwwill
2017/09/19
0
0
React学习笔记

一、React初探 #####es6写法 code #####es5写法(遗憾的是现在最新版本的react,已经不再能使用createClass去创建react组件了 code) 核心思想:封装组件,各个组件维护自己的状态(state, p...

osc_0i27cy0p
2018/07/31
2
0
深入理解React:事件机制原理

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

forcheng
06/24
0
0
React学习笔记

一、React初探 es6写法 code es5写法(遗憾的是现在最新版本的react,已经不再能使用createClass去创建react组件了 code) 核心思想:封装组件,各个组件维护自己的状态(state, prop)和UI,当...

24k-小清新
2018/07/31
0
0
《深入React技术栈》笔记

一、初入React世界 1.2 JSX语法 class 属性修改为className for 属性修改为 htmFor 展开属性 使用ES6 rest/spread 特性提高效率 自定义HTML 属性 如果要使用HTML自定义属性,要使用data- 前缀...

迷糊的浩二
2017/10/09
0
0

没有更多内容

加载失败,请刷新页面

加载更多

63. Unique Paths II

题目: 63. Unique Paths II A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below). The robot can only move either down or right at any p......

JiaMing
今天
46
0
前后端分离了,跨域问题怎么处理?

利用Nginx反向代理解决跨域问题 使用jsonp 来进行解决,不推荐,老项目可以使用此方案,但是发送的http 请求体有大小限制,并且发送方式为get方式,大小限制、不安全。 服务器代理 CORS 请求...

SpringForA
今天
19
0
Hacker News 简讯 2020-07-10

更新时间: 2020-07-10 01:02 Slate Star Codex and Silicon Valley’s War Against the Media - (newyorker.com) 《板岩之星》与硅谷对媒体的战争 得分:102 | 评论:42 US Supreme Court deem......

FalconChen
今天
109
2
如何在Java中将文本追加到现有文件 - How to append text to an existing file in Java

问题: I need to append text repeatedly to an existing file in Java. 我需要将文本重复添加到Java中的现有文件中。 How do I do that? 我怎么做? 解决方案: 参考一: https://stackoom...

fyin1314
昨天
12
0
Eclipse HotKey:如何在选项卡之间切换? - Eclipse HotKey: how to switch between tabs?

问题: How can I switch between opened windows in Eclipse? 如何在Eclipse中打开的窗口之间切换? There is Ctrl + F6 , but it's asking me which one I want, but I want switch it lik......

富含淀粉
昨天
17
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部