React 为了消除不同浏览器上的 Event 差异,设计了一套合成事件(SyntheticEvent)。一般常用的有 onClick
,onKeyDown
等等。
类似于原生的浏览器事件,React 的合成事件也有捕获和冒泡两个不同的阶段。一般常用的 onClick
是在冒泡阶段的回调函数,对应的捕获阶段的回调函数是 onClickCapture
。
需要注意的是,React 合成事件的设计,是在顶层元素上捕获事件,然后通过 React 内部的机制生成对应的合成事件,并转发给 React 元素。其中的捕获和冒泡是由 React 自身来维护的。通过下面的例子,可以直观的看到 React 合成事件和原生浏览器的事件之间的执行顺序。
假设有下面一段 HTML 代码:
<div id="not-react-dom-outer">
<div id ="not-react-dom-inner">
<div id="app"></div>
</div>
</div>
以及下面这段配套的 JavaScript 代码:
const outer = document.querySelector('#not-react-dom-outer');
outer.addEventListener('click', function(e) {
console.log('not-react-dom-outer (bubble)');
}, false);
outer.addEventListener('click', function(e) {
console.log('not-react-dom-outer (capture)');
}, true);
const inner = document.querySelector('#not-react-dom-inner');
inner.addEventListener('click', function(e) {
console.log('not react div inner (bubble)');
}, false);
inner.addEventListener('click', function(e) {
console.log('not react div inner (capture)');
}, true);
const Button = () => (
<div
onClick={() => console.log('react div (bubble)')}
onClickCapture={() => console.log('react div (capture)')}
>
<button
onClick={() => console.log('react button (bubble)')}
onClickCapture={() => console.log('react button (capture)')}
>
Click Me
</button>
</div>
);
ReactDOM.render(<Button />, document.getElementById('app'));
那么,在点击了 <button>
按钮之后,控制台的输出顺序为:
not-react-dom-outer (capture)
not react div inner (capture)
not react div inner (bubble)
not-react-dom-outer (bubble)
react div (capture)
react button (capture)
react button (bubble)
react div (bubble)
执行顺序上是先完成了从捕获到冒泡的所有原生事件,然后再执行从捕获到冒泡的所有 React 合成事件。
关于合成事件,可以参考官方给出的文档。