资讯专栏INFORMATION COLUMN

React-事件机制杂记

zhangyucha0 / 2134人阅读

摘要:前提最近通过阅读官方文档的事件模块,有了一些思考和收获,在这里记录一下调用方法时需要手动绑定先从一段官方代码看起代码中的注释提到了一句话的绑定是必须的,其实这一块是比较容易理解的,因为这并不是的一个特殊点,而是这门语言的特性。

前提

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

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

先从一段官方代码看起:

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

</>复制代码

  1. 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标准定义的,无需担心浏览器之间的差异

</>复制代码

  1. 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也可以手动设置不回收,如下:

</>复制代码

  1. 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, 该函数通过注册一个自定义的元素和自定义的事件, 并触发它来达到执行回调函数的功能

流程总结

通过Fiber中的属性, 将事件统一委托 注册到document上,并为document注册相应的事件回调函数 dispatch函数。

先获取实际触发元素对应的fiber.

生成相应的React事件属性event,将对应的回调函数赋值给event._dispatchListeners, 将fiber赋值给event._dispatchInstances

通过fiber向上遍历, 找到所有的祖先fiber, 并按原生事件的机制先捕获后冒泡的执行事件

注册一个react节点, 为其注册一个监听事件并触发来执行事件回调函数

最后,根据用户的设置, 决定是否释放事件。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101593.html

相关文章

  • React-生命周期杂记

    摘要:前言自从发布之后,更新速度日新月异,而生命周期也随之改变,虽然原有的一些生命周期函数面临废弃,但理解其背后更新的机制也是一种学习在这里根据官方文档以及社区上其他优秀的文章进行一个对于生命周期的总结,大致上分为以下三个模块新老生命周期的区别为 前言 自从React发布Fiber之后,更新速度日新月异,而生命周期也随之改变,虽然原有的一些生命周期函数面临废弃,但理解其背后更新的机制也是一种...

    KoreyLee 评论0 收藏0
  • React-setState杂记

    摘要:简单的举下例子如等生命周期以及的事件即为异步更新,这里不显示具体代码。因为只有当父组件后才传给子组件,那么如果要变成同步的,就需要放弃。 前言 在看React的官方文档的时候, 发现了这么一句话,State Updates May Be Asynchronous,于是查询了一波资料, 最后归纳成以下3个问题 setState为什么要异步更新,它是怎么做的? setState什么时候会...

    yuxue 评论0 收藏0
  • React-flux杂记

    摘要:简介是一种搭建客户端的应用架构,更像是一种模式而不是一个框架。 简介 Flux是一种搭建WEB客户端的应用架构,更像是一种模式而不是一个框架。 特点 单向数据流 showImg(https://segmentfault.com/img/remote/1460000018128072?w=1300&h=708); 与MVC的比较 1.传统的MVC如下所示(是一个双向数...

    王岩威 评论0 收藏0
  • webpack系列-插件机制杂记

    摘要:系列文章系列第一篇基础杂记系列第二篇插件机制杂记系列第三篇流程杂记前言本身并不难,他所完成的各种复杂炫酷的功能都依赖于他的插件机制。的插件机制依赖于一个核心的库,。是什么是一个类似于的的库主要是控制钩子函数的发布与订阅。 系列文章 Webpack系列-第一篇基础杂记 Webpack系列-第二篇插件机制杂记 Webpack系列-第三篇流程杂记 前言 webpack本身并不难,他所完成...

    Neilyo 评论0 收藏0
  • React-Router 杂记

    摘要:三种的区别即对应中的值,如,服务器对任务都返回同一个,具体的路径由浏览器区分,因为浏览器不会发送后面的值给服务器。如果是即变成这样,,所以要对服务器配置不同的返回不同的资源。就是没有的情况,比如。 三种Router的区别 1. HashRouter: 即对应url中的hash值,如xx.com/#/a、xx.com/#/a/b, 服务器对任务url都返回同一个url,具体的路径由浏览器...

    keelii 评论0 收藏0

发表评论

0条评论

zhangyucha0

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<