React中阻止事件冒泡的问题详析(2)

bdoy 上阻止冒泡后,你可能会觉得,既然 body 是按钮及按钮容器的父级,那么按钮及容器的事件会正常执行,事件到达 body 后, body 的事件处理器执行,然后就结束了。 document 上的事件处理器一个也不执行。

事实上,按钮及按钮容器上的事件处理器也没执行,只有 body 执行了。

输出:

body

通过下面的分析,你能够完全理解上面的结果。

SyntheticEvent

React 有自身的一套事件系统,叫作 SyntheticEvent。叫什么不重要,实现上,其实就是通过在 document 上注册事件代理了组件树中所有的事件(),并且它监听的是 document 冒泡阶段。你完全可以忽略掉 SyntheticEvent 这个名词,如果觉得它有点让事情变得高大上或者增加了一些神秘的话。

除了事件系统,它有自身的一套,另外还需要理解的是,界面上展示的 DOM 与我们代码中的 DOM 组件,也是两样东西,需要在概念上区分开来。

所以,当你在页面上点击按钮,事件开始在原生 DOM 上走捕获冒泡流程。React 监听的是 document 上的冒泡阶段。事件冒泡到 document 后,React 将事件再派发到组件树中,然后事件开始在组件树 DOM 中走捕获冒泡流程。

现在来尝试理解一下输出结果:

事件最开始从原生 DOM 按钮一路冒泡到 body,body 的事件处理器执行,输出 body。注意此时流程还没进入 React。为什么?因为 React 监听的是 document 上的事件。

继续往上事件冒泡到 document。

事件到达 document 之后,发现 document 上面一共绑定了三个事件处理器,分别是代码中通过 document.addEventListener 在 ReactDOM.render 前后调用的,以及一个隐藏的事件处理器,是 ,也就是前面提到的 React 用来代理事件的那个处理器。

同一元素上如果对同一类型的事件绑定了多个处理器,会按照绑定的顺序来执行。

所以 ReactDOM.render 之前的那个处理器先执行,输出 document:before react mount。

然后是 React 的事件处理器。此时,流程才真正进入 React,走进我们的组件。组件里面就好理解了,从 button 冒泡到 container,依次输出。

最后 ReactDOM.render 之后的那个处理器先执行,输出 document:after react mount。

事件完成了在 document 上的冒泡,往上到了 window,执行相应的处理器并输出 window。

理解 React 是通过监听 document 冒泡阶段来代理组件中的事件,这点很重要。同时,区分原生 DOM 与 React 组件,也很重要。并且,React 组件上的事件处理器接收到的 event 对象也有别于原生的事件对象,不是同一个东西。但这个对象上有个 nativeEvent 属性,可获取到原生的事件对象,后面会用到和讨论它。

紧接着的代码的改动中,我们在 body 上阻止了事件冒泡,这样事件在 body 就结束了,没有到达 document,那么 React 的事件就不会被触发,所以 React 组件树中,按钮及容器就没什么反应。如果没理解到这点,光看表象还以为是 bug。

进而可以理解,如果在 ReactDOM.render() 之前的的 document 事件处理器上将冒泡结束掉,同样会影响 React 的执行。只不过这里需要调用的不是 event.stopPropagation() ,而是 event.stopImmediatePropagation() 。

document.addEventListener("click", event => { + event.stopImmediatePropagation(); console.log("document:bedore react mount"); });

输出:

body
document:bedore react mount

stopImmediatePropagation 会产生这样的效果,即,如果同一元素上同一类型的事件(这里是 click)绑定了多个事件处理器,本来这些处理器会按绑定的先后来执行,但如果其中一个调用了 stopImmediatePropagation,不但会阻止事件冒泡,还会阻止这个元素后续其他事件处理器的执行。

所以,虽然都是监听 document 上的点击事件,但 ReactDOM.render() 之前的这个处理器要先于 React,所以 React 对 document 的监听不会触发。

解答前面按钮未能阻止冒泡的问题

如果你已经忘了,这是相应的代码及输出。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/e0674b3d3f7c102b62e74db7beff2252.html