转载

窥探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...