前提

最近通过阅读React官方文档的事件模块,有了一些思考和收获,在这里记录一下~

调用方法时需要手动绑定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也能达到我们的预期效果

合成事件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处理, 那么这样做有什么好处:

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

  • 可以统一在组件挂载和卸载时做处理
  • 只需要注册一个事件即可,节省内存开销
  • 可以手动控制事件流程,特别是对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. React事件机制 - 源码概览(下)

    上篇文档 React事件机制 - 源码概览(上)说到了事件执行阶段的构造合成事件部分,本文接着继续往下分析 批处理合成事件 入口是 runEventsInBatch // runEventsInBat ...

  2. 探究react的事件机制(合成事件)

    一.react的事件机制 react自身实现了一套事件机制,包括事件的注册.事件的存储.事件的合成及执行等. react 的所有事件并没有绑定到具体的dom节点上而是绑定在了document 上,然后 ...

  3. react的事件机制

    好久没写博客了,前段时间太忙以至于平时的积累都记录在内网的wiki里,趁着这几天有空,将这段时间所积累的干货慢慢的分享出来,如果内容有不正确的地方,欢迎纠正. 本博客大概介绍一下react的事件机制, ...

  4. React Native 手势触摸事件机制详解(进阶篇)

    源码已开源到Github,详细代码可以查看:<React Native 触摸事件代码实践>. 在基础篇,对RN中的触摸事件做了详细的介绍.相信大家对于触摸事件流程机制有了更为清晰的认识.没 ...

  5. 「前端面试题系列7」Javascript 中的事件机制(从原生到框架)

    前言 这是前端面试题系列的第 7 篇,你可能错过了前面的篇章,可以在这里找到: 理解函数的柯里化 ES6 中箭头函数的用法 this 的原理以及用法 伪类与伪元素的区别及实战 如何实现一个圣杯布局? ...

  6. webpack-插件机制杂记

    系列文章 Webpack系列-第一篇基础杂记 webpack系列-插件机制杂记 前言 webpack本身并不难,他所完成的各种复杂炫酷的功能都依赖于他的插件机制.或许我们在日常的开发需求中并不需要自己 ...

  7. javaScript tips —— z-index 对事件机制的影响

    demo // DOM结构 class App extends React.Component {componentDidMount() {const div1 = document.getEleme ...

  8. React事件系统研究总结

    React作为目前前端业界最流行的mvvm框架之一已经被广大前端同学所熟知,而在日常工作中已经熟悉使用React的我们对React内部的工作流程.设计理念是否又有足够的了解呢?本文是对于React事件 ...

  9. React Fiber 机制

    前言 React Fiber  机制是16版本性能重大提升的关键,也是react 面试的核心问题: 一.React 的工作流程 React渲染页面的两个阶段 调和阶段(reconciliation): ...

最新文章

  1. WinAPI: SetTextAlign - 设置绘图环境的文本对齐方式
  2. delphi 2010 调整图片的比例
  3. MCU为什么内部不集成晶振
  4. c语言数据的自动转换类型吗,c语言的自动类型转换
  5. CSS笔记-除了a标签外的hover属性的应用
  6. Keepalived+nginx+redis主从+tomcat一机多实例实现会话共享
  7. python超市管理系统总汇总功能解说_AdminModules --- 管理模块
  8. 宠物游戏系统 java
  9. 计算机科学与技术审核评估专家,中国农业大学 业务动态 审核评估专家Sir Timothy深度走访网络技术中心...
  10. FunCoolShell
  11. P5.js码绘---阿珊自画像
  12. 电脑录屏是哪个快捷键?3个录屏快捷键,教你快速录屏
  13. selenium 校园网自动连接
  14. iToo Software推出Forest Pack 6
  15. mcnpf5输出结果_MCNP入门教程
  16. svn访问版本库时一直提示: please wait while the repository browser is initializing
  17. 机器学习之算法优化(一)
  18. 关于DistroWatch
  19. 加盟京东便利店需要什么(京东便利店加盟条件)
  20. 地方立法限制人肉搜索作用有限

热门文章

  1. 利用tensorflow建立简单的神经网络所需要的几条简单语句
  2. 类中定义自身类的对象
  3. 扩容是元素还是数组_348,数据结构1,数组
  4. Ab Initio软件
  5. linux源码Makefile的详细分析
  6. ./ . 和#!/bin/bash 辨析Linux如何选择当前执行脚本的shell
  7. Day1 -Python program
  8. 445端口关闭后目录文件共享怎么办
  9. Python学习札记(二十八) 模块1
  10. css flex排序居中