窥探react事件

前端之家收集整理的这篇文章主要介绍了窥探react事件前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

写在前面

本文源于本人在学习react过程中遇到的一个问题;本文内容为本人的一些的理解,如有不对的地方,还请大家指出来。本文是讲react的事件,不是介绍其api,而是猜想一下react合成事件的实现方式

遇到的问题

class@H_404_15@ EventTest extends Component {
    handleParentClick(e) {
        console.log('click parent div'@H_404_15@);
    }
    handleChildClick(e) {
        e.stopPropagation();
        console.log('click child div'@H_404_15@);
    }
    componentDidMount() {
        document.querySelector('.parent'@H_404_15@).addEventListener('click'@H_404_15@,this@H_404_15@.handleParentClick);
    }
    render() {
        return@H_404_15@ (
            <div@H_404_15@ className@H_404_15@="parent"@H_404_15@>@H_404_15@ <div@H_404_15@ className@H_404_15@="child"@H_404_15@ onClick@H_404_15@={this.handleChildClick}@H_404_15@>@H_404_15@</div@H_404_15@>@H_404_15@ </div@H_404_15@>@H_404_15@ ); } }@H_404_15@

上述代码render出来后,尝试点击一下div.child,诡异的现象产生了:

控制台中输出如上图所示,这完全不符合浏览器的事件执行啊,我所期望的是指输出click child div,因为已经利用了e.stopPropagation()来阻止冒泡,说明阻止冒泡失效了,但是仅仅如此吗,可以发现的是首先输出的是click parent div(wtf)。

解决问题

为了解决上述问题,先来了解下react的事件,react事件是合成事件,为原生事件的一个子集,仅仅是进行了一个跨浏览器的封装。但是真的只有这么简单?图样图森破。
利用控制台,看下div.child对应的事件处理函数

一个空函数,事件的监听函数不是所定义的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结构,这样有时遍历的层级会很多,而且会有很多的无效遍历。

react是怎么做的呢?

react依靠每个React component各自独立的id来编码这个层级。这样就能通过简单的字符串操作来获取所有父级 component 的父级内容,再把事件监听存储在hashmap当中,比如有如下结构并且为没一层div添加onClick

@H_404_157@div@H_404_15@.@H_404_157@a@H_404_15@
    @H_404_157@div@H_404_15@.b
        @H_404_157@div@H_404_15@.c

当点击div.c时,处理方式:

clickBubbleListeners['a.b.c'@H_404_15@](event);
clickBubbleListeners['a.b'@H_404_15@](event);
clickBubbleListeners['a'@H_404_15@](event);

在合成事件中用e.stopPropagation只能阻断上述冒泡过程。

结论

由此可以看出:
- 阻止react事件冒泡的行为只能用于react合成事件中,对于原生事件无效(合成事件中的e.stopPropagation与原生事件中的e.stopPropagation并不是一回事)
- 阻止原生事件的冒泡行为,可以阻止react合成事件的传播(根本不会冒泡到document上,所以不会触发react的合成事件)
- 在写react时,最好不要将合成事件与原生事件混用

参考

本文部分参考自IMWeb—React事件初探

原文链接:https://www.f2er.com/react/305061.html

猜你在找的React相关文章