前提

最近通过阅读React官方文档的事件模块,发现了其主要提到了以下三个点

  1.  调用方法时需要手动绑定this
  2.  React事件是一种合成事件SyntheticEvent,什么是合成事件?
  3.  事件属性会在事件调用后被回收,即不能异步访问
  4.  事件机制的源码分析
        1).
    注册阶段源码分析
        2).触发阶段源码分析
        3).总结相关流程

带着问题,通过查询资料和源码来探寻~

1.调用方法时需要手动绑定this

先从一段官方代码看起:

代码中的注释提到了一句话:

  This binding is necessary to make `this` work in the callback

this的绑定是必须的,其实这一块是比较容易理解的, 因为这并不是React的一个特殊点, 而是Javascript这门语言的特性

可以看到,调用的是this.handleClick函数,handleClick函数里面又读取到了this属性,但是该函数的调用位置又是在render函数里面,render返回的是一个JSX,最后经过babel编译成调用React.createElement函数,

在这之前,我们掌握的是this永远指向的是最后调用它的对象,经过这样的一个转换, 实际上this最后指向的是undeined了, 那么调用handleClick函数自然会报错。

当然,如果你不在函数里面使用this的话,通常会没事,但并不建议这么做。

关于this的指向与function的原理,推荐阅读 how functions work in JavaScript

既然知道了是因为this的指向原因而采用绑定的做法,那当然可以用箭头函数来解决了,箭头函数中的this是在定义函数的时候绑定,也就是说this是继承自父执行上下文,如下:

这样this也能达到我们的预期效果

React事件是一种合成事件SyntheticEvent,什么是合成事件?

先从官方上的一段话看起,他的意思是合成事件是React根据W3C标准定义的,无需担心浏览器之间的差异

Here, e is a synthetic event. React defines these synthetic events according to the W3C spec, so you don’t need to worry about cross-browser compatibility

这样看起来React的合成事件只是兼容浏览器? 答案当然是远远不止啦!

在探寻其优点之前,我们先看一下其是怎样的一个机制。

React的事件机制其实网上有很多同学都分析过了, 他并没有将事件注册在对应的元素或者组件上面,而是通过委托的方式,将所有的事件都注册到了document对象上,并统一调用一个dispatch回调函数,其流程图如下

我们也可以从一个实际的简单例子看看:

我们把回调函数绑定到了button上,但是在事件上却没有看到button元素, 但是却有document,并且可以看到他的回调函数就是dispatchInteractiveEvent

最后触发事件的回调函数时,在原生的DOM会传入一个事件属性event,但是因为React将 所有事件委托给document处理, 那么这个event就和我们想要的不一样,如target指向的是document,于是React就有了自己的一个合成事件,通过一个叫SyntheticEvent的基类来生成所需要的事件属性,并传入回调函数作为方法。

说到底,React就是把所有事件委托给document处理, 那么这样做有什么好处:

  1.  可以统一在组件挂载和卸载时做处理
  2.  只需要注册一个事件即可,节省内存开销
  3.  可以手动控制事件流程,特别是对state的batch处理(参考React系列的setState)

事件属性会在事件调用后被回收,即不能异步访问

老规矩,先上一段代码:

可以看到在setTimeout函数中,访问事件属性是null。这是为啥?

其实这也是合成事件的一个优化手段。 React会在事件调用完成后清理掉属性,否则每点击一次就生成一个事件,那么内存的开销会越来越大,具体的代码可以在后面的源码分析中看到:

当然了, React也可以手动设置不回收,如下:

If you want to access the event properties in an asynchronous way, you should call event.persist() on the event

我们可以通过调用event,persist来设置不回收。

事件机制的源码分析

注册阶段

首先在某一个任务单元fiber调用compeleteWork函数时, React会判断其是否具有事件属性, 如果有则调用ensureListeningTo函数

ensureListeningTo函数主要是获取到document对象, 并调用listenTo函数

listerTo函数 主要是通过调用trapBubbledEvent或者trapCapturedEvent将事件放在document事件上监听

trapBubbledEvent主要是监听事件, 但也可以看出, 所有事件最后触发的都是注册在document上的dispatch函数

调用阶段

dispatch函数, 主要是获取实际触发的元素以及对应的fiber, 最后调用batchedUpdates函数,  batchedUpdates函数里面的逻辑主要是关于setState的,这里主要是看事件机制, 只要知道最后调用的是handleTopLevel(bookkeeping)就好

handleTopLevel函数主要是拿到需要触发事件的相关fiber, 并调用runExtractedEventsInBatch函数

extractEvents函数是一个生成React事件的函数,React事件是通过继承一个通用类SyntheticEvent生成的,如一个鼠标事件的生成

React事件内部做了优化, 只要生成过SyntheticMouseEvent类, 就会再释放事件的时候将这个类存储起来,在下一个事件触发时可以直接使用

React生成事件后, 会调用accumulateTwoPhaseDispatches(event)函数,该函数一直追溯下去, 最后会调用traverseTwoPhase函数,

traverseTwoPhase函数主要是获取祖先组件的fiber, 并进行捕获和冒泡的阶段处理

accumulateDirectionalDispatches函数相对简单, 就是把fiber上对应的事件函数赋值给evnet_dispatchListeners属性

React事件获取完成后, 回到runExtractedEventsInBatch函数继续调用runEventsInBatch(events, false); 函数的中间作了一系列的处理, 但最后执行的是executeDispatchesAndRelease函数

executeDispatchesAndRelease函数会在执行完事件后判断用户是否有设置不销毁事件, 如果没有, 则销毁事件并保存事件类, 一个事件类实例一次并重复使用, 这也是为什么官方提到事件属性只能在当前循环中读到

继续往下走, 最后执行的函数是invokeGuardedCallbackDev 该函数通过注册一个自定义的元素<react>和自定义的事件, 并触发它来达到执行回调函数的功能

最后, 总结下相关的流程:

  1. 通过Fiber中的属性, 将事件统一委托 注册到document上,并为document注册相应的事件回调函数 dispatch函数。
  2. 先获取实际触发元素对应的fiber.
  3. 生成相应的React事件属性event,将对应的回调函数赋值给event._dispatchListeners, 将fiber赋值给event._dispatchInstances
  4. 通过fiber向上遍历, 找到所有的祖先fiber, 并按原生事件的机制先捕获后冒泡的执行事件
  5. 注册一个react节点, 为其注册一个监听事件并触发来执行事件回调函数
  6. 最后,根据用户的设置, 决定是否释放事件。

React事件杂记及源码分析的更多相关文章

  1. Android View事件分发-从源码分析

    View事件分发-从源码分析 学习自 <Android开发艺术探索> https://blog.csdn.net/qian520ao/article/details/78555397?lo ...

  2. Android事件分发机制源码分析

    Android事件分发机制源码分析 Android事件分发机制源码分析 Part1事件来源以及传递顺序 Activity分发事件源码 PhoneWindow分发事件源码 小结 Part2ViewGro ...

  3. Cocos2d-X3.0 刨根问底(七)----- 事件机制Event源码分析

    这一章,我们来分析Cocos2d-x 事件机制相关的源码, 根据Cocos2d-x的工程目录,我们可以找到所有关于事件的源码都存在放在下图所示的目录中. 从这个event_dispatcher目录中的 ...

  4. Android查缺补漏(View篇)--事件分发机制源码分析

    在上一篇博文中分析了事件分发的流程及规则,本篇会从源码的角度更进一步理解事件分发机制的原理,如果对事件分发规则还不太清楚的童鞋,建议先看一下上一篇博文 <Android查缺补漏(View篇)-- ...

  5. (转)spring boot实战(第三篇)事件监听源码分析

    原文:http://blog.csdn.net/liaokailin/article/details/48194777 监听源码分析 首先是我们自定义的main方法: package com.lkl. ...

  6. Qt事件分发机制源码分析之QApplication对象构建过程

    我们在新建一个Qt GUI项目时,main函数里会生成类似下面的代码: int main(int argc, char *argv[]) { QApplication application(argc ...

  7. Android事件传递机制详解及最新源码分析——View篇

    摘要: 版权声明:本文出自汪磊的博客,转载请务必注明出处. 对于安卓事件传递机制相信绝大部分开发者都听说过或者了解过,也是面试中最常问的问题之一.但是真正能从源码角度理解具体事件传递流程的相信并不多, ...

  8. Android事件传递机制详解及最新源码分析——ViewGroup篇

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 在上一篇<Android事件传递机制详解及最新源码分析--View篇>中,详细讲解了View事件的传递机制,没掌握或者掌握不扎实的小伙伴 ...

  9. Android事件传递机制详解及最新源码分析——Activity篇

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 在前两篇我们共同探讨了事件传递机制<View篇>与<ViewGroup篇>,我们知道View触摸事件是ViewGroup传递 ...

随机推荐

  1. django+javascrpt+python实现私有云盘

    代码稍后上,先整理下私有云盘的相关功能介绍. 1.登陆界面 2.首页展示,有个人目录.部门目录以及公司目录,针对不用的目录设置不同的权限控制. 3.个人信息展示 4.账号管理.账号信息展示 5.账号添 ...

  2. 818C.soft thief

    Yet another round on DecoForces is coming! Grandpa Maks wanted to participate in it but someone has ...

  3. pickle 模块

    序列化和反序列化的定义 序列化:就是把不可传输的对象转换为可存储或可传输的过程 反序列化:就是把在磁盘,等介质中的数据转换为对象 import pickle #dic={'name':'alex',' ...

  4. IDEA环境下SSM整合------注解开发

    根据前一篇文章的步骤,目前项目进度应该是:核心过滤器配置完成.DispatcherServlet和ContextLoader配置完成.数据库dataSource配置完成.视图解析器配置完成.Mappe ...

  5. koa2学习(二) 中间件router

    中间件 koa-router 安装 npm install --save koa-router 使用 const Koa = require('koa'); const Router = requir ...

  6. CROS+node-basis+ajax

    $.ajax({ url: this.baseUrl + this.restful.showDesigerViewList, type: "post", dataType: &qu ...

  7. 通用类 对象和XML互转

    public class XMLHealper { /// <summary> /// 将自定义对象序列化为XML字符串 /// </summary> /// <para ...

  8. .NET Core多平台开发体验[3]: Linux (Windows Linux子系统)

    如果想体验Linux环境下开发和运行.NET Core应用,我们有多种选择.一种就是在一台物理机上安装原生的Linux,我们可以根据自身的喜好选择某种Linux Distribution,目前来说像R ...

  9. 实战深度学习OpenCV(二):读取并播放本地或者摄像头的视频

    一.读取并播放的代码如下: #include "pch.h" #include <iostream> #include <opencv2/core/core.hp ...

  10. Android OpenSL ES 开发:OpenSL ES利用SoundTouch实现PCM音频的变速和变调

    缘由 OpenSL ES 学习到现在已经知道 OpenSL ES 不仅能播放和录制PCM音频数据,还能改变声音大小.设置左声道或右声道播放.还能变速播放,可谓是播放音频的王者.但是变速有一点不好的就是 ...