转载

窥探react事件

写在前面

本文源于本人在学习 react 过程中遇到的一个问题;本文内容为本人的一些的理解,如有不对的地方,还请大家指出来。 本文是讲react的事件,不是介绍其api,而是猜想一下 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 ,诡异的现象产生了:

窥探react事件

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

解决问题

为了解决上述问题,先来了解下 react 的事件, react 事件是合成事件,为原生事件的一个子集,仅仅是进行了一个跨浏览器的封装。但是真的只有这么简单?图样图森破。

利用控制台,看下 div.child 对应的事件处理函数:

窥探react事件

一个空函数,事件的监听函数不是所定义的 handleChildClick ,而是 emptyFunction ,也就是说 react 没有在真实的 DOM 节点上绑定事件(在 DOM 节点上绑定事件比较消耗内存,因为当 dom 节点被 remove 后,虽然不存在与 dom tree 中,但是仍存在与内存中,需要手动 remove 事件or child = null ), react 的合成事件利用的是事件代理方式实现 ,也就是说会将事件监听器绑定到整个文档 document 上,是不是这样呢?来验证一下,利用chrome:

窥探react事件

可以发现, 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

div.a
    div.b
        div.c

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

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

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

结论

由此可以看出:

  • 阻止 react 事件冒泡的行为只能用于 react 合成事件中,对于原生事件无效(合成事件中的 e.stopPropagation 与原生事件中的 e.stopPropagation 并不是一回事)

  • 阻止原生事件的冒泡行为,可以阻止 react 合成事件的传播(根本不会冒泡到 document 上,所以不会触发 react 的合成事件)

  • 在写 react 时, 最好不要将合成事件与原生事件混用 。

参考

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

原文  https://segmentfault.com/a/1190000008044759
正文到此结束
Loading...