惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Security Latest
Security Latest
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Stack Overflow Blog
Stack Overflow Blog
WordPress大学
WordPress大学
N
Netflix TechBlog - Medium
GbyAI
GbyAI
云风的 BLOG
云风的 BLOG
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
宝玉的分享
宝玉的分享
博客园 - 【当耐特】
C
Cyber Attacks, Cyber Crime and Cyber Security
雷峰网
雷峰网
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
Threat Research - Cisco Blogs
NISL@THU
NISL@THU
Spread Privacy
Spread Privacy
P
Proofpoint News Feed
J
Java Code Geeks
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
MyScale Blog
MyScale Blog
T
Tor Project blog
P
Proofpoint News Feed
C
CERT Recently Published Vulnerability Notes
P
Privacy & Cybersecurity Law Blog
MongoDB | Blog
MongoDB | Blog
Simon Willison's Weblog
Simon Willison's Weblog
C
Cybersecurity and Infrastructure Security Agency CISA
L
LINUX DO - 热门话题
小众软件
小众软件
G
GRAHAM CLULEY
P
Privacy International News Feed
AWS News Blog
AWS News Blog
Know Your Adversary
Know Your Adversary
P
Palo Alto Networks Blog
人人都是产品经理
人人都是产品经理
S
Schneier on Security
Scott Helme
Scott Helme
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
B
Blog RSS Feed
T
The Exploit Database - CXSecurity.com
Recent Announcements
Recent Announcements
E
Exploit-DB.com RSS Feed
C
CXSECURITY Database RSS Feed - CXSecurity.com
U
Unit 42
The Register - Security
The Register - Security
S
Securelist
Martin Fowler
Martin Fowler
Project Zero
Project Zero
大猫的无限游戏
大猫的无限游戏
Cisco Talos Blog
Cisco Talos Blog

博客园 - 爱喝可乐的咖啡

引入AI辅助的3D游戏美术工作流 基于ThreeJs的大屏3D地图(二)——气泡图、渐变柱体与热力图 基于Three.js的大屏3D地图(一)——地图模型渲染 不止于面向对象的SOLID原则 沿SVG路径的颜色渐变 参照DefenseGrid在Unity中实现合理的塔防寻路机制 slate源码解析(三)- 定位 定位解析一个因脚本劫持导致webpack动态加载异常的问题 slate源码解析(二)- 基本框架与数据模型 slate源码解析(一)- 序言 重新捋一捋React源码之更新渲染流程 从源码入手探究一个因useImperativeHandle引起的Bug [译]深入了解现代web浏览器(四) [译]深入了解现代web浏览器(三) [译]深入了解现代web浏览器(二) [译]深入了解现代web浏览器(一) 依据HTML标准再探Javascript事件循环及其与浏览器渲染的关系 记一次flex布局中子项目尺寸不受flex-shrink限制的问题 浅析富文本编辑器框架Slate.js
关于在异步操作中访问React事件对象的问题
爱喝可乐的咖啡 · 2020-09-12 · via 博客园 - 爱喝可乐的咖啡

最近撸React的代码时踩了个关于事件处理的坑,场景如下:在监听某个元素上会频繁触发的事件时,我们往往会对该事件的回调函数进行防抖的处理;防抖的包装函数大致长这样:

debounce = (fn, delay) => {
    let timer: any = null;
    return function(...args) {
        if(timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(fn, delay, ...args);
    }
}

核心部分就是用setTimeout()做延时执行,而问题就是出在这里。先说下结论,在React中如果要在异步操作中访问事件对象,则需要先在该事件对象上执行event.persist()。否则的话,在异步操作中访问事件对象时你会发现这个对象上大部分属性都是无效的了。

之前在项目中其他地方也见过这个方法也查了下知道这个东西,不过当时也只是知道有这么个方法并不太理解这么个方法存在的意义,现在好了,踩坑了吧,只好专门去了解下其中的缘由(=_=) 。 异步访问事件对象时其属性失效的原因在于事件派发并处理完后 这个对象不会马上被释放,而是将这个事件对象上的一些属性释放再回收放进被称为“事件池”的这么个地方。 看下react-dom中的这段源码:

在上面步骤中,派发完事件后,会判断事件对象event.isPersistent() 即是否有被持久化;而如果我没有在处理函数中执行过event.persist(),所以就进入了分支执行release操作;执行完release后,这个event上的大部分属性就都被清空了然后被放进事件池里。而异步操作是发生在这个过程之后的,这时候如果要访问该event的话 例如我们获取event.target 这时event上的target属性是不存在的了,代码就出错了。

然后再说下事件池;官方文档在说明上述问题时提到了下事件池

SyntheticEvent 是合并而来。这意味着 SyntheticEvent 对象可能会被重用,而且在事件回调函数被调用后,所有的属性都会无效。出于性能考虑,你不能通过异步访问事件。

说的比较笼统,解释一下:所有产生的事件都会生成一个事件对象,按正常逻辑 在我们的事件处理函数执行完后,这个事件对象就应该被释放了,等待着被内存回收;但如果在短时间内触发了许多次事件,就要频繁的生成和销毁事件对象;那么 为了提高性能,React就用了一个“事件池”这么一个池子,被使用完后的事件,并不直接销毁,而是将其身上的属性清空掉了后放进事件池中, 等到了下一次有同类型事件发生时,就不用再new一个新的事件对象了,直接从事件池取出一个现成的就可以用了, 从而实现事件对象的重用

使用这么一套机制最根本的动机在于:在很多业务系统中创建和销毁对象的代价是非常昂贵的。只接触过前端领域的同学可能没怎么听说过XXX对象池这种概念,不过在其他工种的圈子中这个模式被运用在很多地方, 例如后端中经常提及的线程池、数据库连接池,在游戏引擎Unity中也有对象池的概念。 这个模式对于一些场景的性能提升是非常大的,我们想象一下这些场景:Web服务器遇到高并发时,会在瞬时创建和销毁大量的线程、 又或者当我们在愉快地玩耍诸如FPS类型的游戏时,每个弹药都是一个游戏中的对象,那么就会经常会产生大量的对象,并且在短时间内这些对象又会在使用完后等效被销毁,势必就会给游戏的运行带来很大负担;而且很可能还会伴随着长时间的GC,这样的游戏体验可想而知。