合成事件(SyntheticEvent)

React 为什么要自己搞一套事件系统?

先说结论:为了抹平浏览器差异 + 性能优化。

为什么需要合成事件?

1. 抹平浏览器差异

原生事件对象在 IE、Safari、Chrome 上的属性名不一致。React 统一了接口:

  • e.target
  • e.preventDefault()

开发者不用关心兼容性问题。

2. 性能优化:事件委托

React 不会把事件监听器绑定到每个 DOM 元素上。

而是把所有事件都监听在根节点上:

  • 事件触发时,React 自己模拟捕获和冒泡
  • 找到对应的组件执行回调
  • 大大减少内存消耗

React 17 的变化

委托节点变了

  • React 16:绑定到 document
  • React 17+:绑定到 Root 容器#root div)

为什么改?

解决微前端场景下的事件冲突。

多个 React 应用共存,或者 React 嵌入 jQuery 页面,绑定在 Document 上的全局监听器会互相干扰。

绑定到 Root 容器实现了应用隔离。

和原生事件的区别

阻止冒泡

e.stopPropagation();
// 既阻止 React 合成事件冒泡
// 也阻止原生事件继续往上冒

执行顺序

  1. 原生事件监听器(绑定在子元素)先执行
  2. React 合成事件(绑定在 Root)后执行
  3. Document 上的原生监听器最后执行

Event Pooling(事件池)

  • React 16:SyntheticEvent 对象用完会被重置(属性变 null),不能在异步代码中访问
  • React 17已移除

因为现代浏览器 JS 引擎优化这块开销微不足道,而这个特性带来的心智负担太大。现在可以放心在异步代码里用 e 了。

总结

特性说明
合成事件统一接口,抹平浏览器差异
事件委托监听在根节点,减少内存
React 17绑定到 Root 容器
Event PoolingReact 17 已移除