本文源于本人在学习react
过程当中遇到的一个问题;本文内容为本人的一些的理解,若有不对的地方,还请你们指出来。本文是讲react的事件,不是介绍其api,而是猜测一下react
合成事件的实现方式react
class EventTest extends Component { handleParentClick(e) { console.log('click parent div'); } handleChildClick(e) { e.stopPropagation(); console.log('click child div'); } componentDidMount() { document.querySelector('.parent').addEventListener('click', this.handleParentClick); } render() { return ( <div className="parent"> <div className="child" onClick={this.handleChildClick}></div> </div> ); } }
上述代码render
出来后,尝试点击一下div.child
,诡异的现象产生了:web
控制台中输出如上图所示,这彻底不符合浏览器的事件执行啊,我所指望的是指输出click child div
,由于已经利用了e.stopPropagation()
来阻止冒泡,说明阻止冒泡失效了,可是仅仅如此吗,能够发现的是首先输出的是click parent div
(wtf)。chrome
为了解决上述问题,先来了解下react
的事件,react
事件是合成事件,为原生事件的一个子集,仅仅是进行了一个跨浏览器的封装。可是真的只有这么简单?图样图森破。
利用控制台,看下div.child
对应的事件处理函数:api
一个空函数,事件的监听函数不是所定义的handleChildClick
,而是emptyFunction
,也就是说react
没有在真实的DOM
节点上绑定事件(在DOM
节点上绑定事件比较消耗内存,由于当dom
节点被remove
后,虽然不存在与dom tree
中,可是仍存在与内存中,须要手动remove
事件orchild = null
),react
的合成事件利用的是事件代理方式实现,也就是说会将事件监听器绑定到整个文档document
上,是否是这样呢?来验证一下,利用chrome:浏览器
能够发现,document
上的确被绑定了click
事件,dom
节点的真实的事件处理函数所有以一个特定的结构存储在了内存中,当点击div.child
时,这时其事件处理函数为emptyFunction
,执行这个函数无任何做用,按照浏览器标准事件模型,开始向上冒泡,这时到了div.parent
,因而输出了click parent div
,一直向上到了document
上,这时根据e.target
进行处理,而react
并不会根据dom
层级式传播那样遍历virtual dom
结构,这样有时遍历的层级会不少,并且会有不少的无效遍历。dom
react
是怎么作的呢?react
依靠每一个React component
各自独立的id
来编码这个层级。这样就能经过简单的字符串操做来获取全部父级 component 的父级内容,再把事件监听存储在hashmap当中,好比有以下结构而且为没一层div
添加onClick
函数
div.a div.b div.c
当点击div.c
时,处理方式:学习
clickBubbleListeners['a.b.c'](event); clickBubbleListeners['a.b'](event); clickBubbleListeners['a'](event);
在合成事件中用e.stopPropagation
只能阻断上述冒泡过程。this
由此能够看出:编码
阻止react
事件冒泡的行为只能用于react
合成事件中,对于原生事件无效(合成事件中的e.stopPropagation
与原生事件中的e.stopPropagation
并非一回事)
阻止原生事件的冒泡行为,能够阻止react
合成事件的传播(根本不会冒泡到document
上,因此不会触发react
的合成事件)
在写react
时,最好不要将合成事件与原生事件混用。
本文部分参考自IMWeb—React事件初探