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


要解决这个问题,这里有不止一种方法 。
用 window 替换 document
来自 React issue 回答中提供的这个方法是最快速有效的 。使用 window 替换掉 document 后,前面的代码可按期望的方式执行 。

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

文章插图
这里 button 事件处理器上接到到的 event 来自 React 系统,也就是 document 上代理过来的,所以通过它阻止冒泡后,事件到 document 就结束了,而不会往上到 window 。
Event.stopImmediatePropagation()
组件中事件处理器接收到的 event 事件对象是 React 包装后的 SyntheticEvent 事件对象 。但可通过它的 nativeEvent 属性获取到原生的 DOM 事件对象 。通过调用这个原生的事件对象上的 stopImmediatePropagation() 方法可达到阻止冒泡的目的 。
React中阻止事件冒泡的问题详析

文章插图
至于原理,其实前面已经有展示过 。React 在 render 时监听了 document 冒泡阶段的事件,当我们的 App 组件执行时,准确地说是渲染完成后(useEffect 渲染完成后执行),又在 document 上注册了 click 的监听 。此时 document 上有两个事件处理器了,并且组件中的这个顺序在 React 后面 。
当调用 event.nativeEvent.stopImmediatePropagation() 后,阻止了 document 上同类型后续事件处理器的执行,达到了想要的效果 。
但这种方式有个缺点很明显,那就是要求需要被阻止的事件是在 React render 之后绑定,如果在之前绑定,是达不到效果的 。
通过元素自身来绑定事件处理器
当绕开 React 直接通过调用元素自己身上的方法来绑定事件时,此时走的是原生 DOM 的流程,都没在 React 的流程里面 。
React中阻止事件冒泡的问题详析

文章插图
很明显这样是能解决问题,但你根本不会想要这样做 。代码丑陋,不直观也不易理解 。
结论
注意区分 React 组件的事件及原生 DOM 事件,一般情况下,尽量使用 React 的事件而不要混用 。如果必需要混用比如监听 document,window 上的事件,处理 mousemove,resize 等这些场景,那么就需要注意本文提到的顺序问题,不然容易出 bug 。

推荐阅读