加载笔记内容...
加载笔记内容...
浏览器原生事件系统基于 DOM 树的事件传播机制,包含捕获阶段(Capturing Phase)、目标阶段(Target Phase)和冒泡阶段(Bubbling Phase)。React 在此基础之上构建了合成事件系统(SyntheticEvent),实现了以下核心特性:
1// React 事件委托机制示意图
2document.getElementById('root').addEventListener('click', (e) => {
3 // React 在此处构造 SyntheticEvent
4 // 模拟完整的事件传播流程
5});
React 17 对事件系统进行了重要升级:
这一改变解决了以下问题:
e.persist()
)React 通过插件化架构管理不同事件类型,核心模块包括:
graph TD A[DOM Event] --> B(EventPluginHub) B --> C{EventPlugin 匹配} C -->|匹配成功| D[生成 SyntheticEvent] C -->|无匹配| E[忽略事件] D --> F[触发 React 组件处理]
React 将事件分为三个优先级等级:
优先级 | 对应事件 | 响应延迟 |
---|---|---|
DiscreteEvent | click, keydown, focus 等 | 立即 |
UserBlocking | mouseMove, scroll 等 | ~250ms |
Continuous | load, progress 等 | 无限制 |
这种分级机制使得高优先级交互(如点击按钮)能快速响应,而低优先级事件(如滚动)不会阻塞渲染。
当合成事件与原生事件混用时,执行顺序成为关键问题:
1// 原生事件监听器
2document.addEventListener('click', () => {
3 console.log('Native event');
4});
5
6// React 组件
7function MyComponent() {
8 const handleClick = () => {
9 console.log('Synthetic event');
10 };
11
12 return <button onClick={handleClick}>Click me</button>;
13}
执行顺序:
注意:React 17 之前合成事件统一在 document 的冒泡阶段处理
两种事件系统的传播控制存在差异:
操作 | 原生事件系统 | React 合成事件系统 |
---|---|---|
e.stopPropagation() | 阻止后续事件传播 | 阻止 React 事件传播 |
e.nativeEvent | 无 | 访问底层原生事件对象 |
典型陷阱:
1// 错误示例:无法阻止原生事件传播
2const handleClick = (e) => {
3 e.stopPropagation(); // 只影响合成事件
4 document.dispatchEvent(new CustomEvent('custom'));
5};
6
7// 正确做法
8const handleClick = (e) => {
9 e.nativeEvent.stopImmediatePropagation();
10 // 同时阻止合成事件传播
11 e.stopPropagation();
12};
针对滚动等高频事件,使用 passive: true
提升性能:
1// React 实现方案
2useEffect(() => {
3 const elem = document.getElementById('scroll-area');
4 elem.addEventListener('touchmove', handler, { passive: true });
5 return () => elem.removeEventListener('touchmove', handler);
6}, []);
性能对比:
常见内存泄漏场景及解决方案:
1// 错误示例
2useEffect(() => {
3 window.addEventListener('resize', handleResize);
4}, []);
5
6// 正确方案
7useEffect(() => {
8 window.addEventListener('resize', handleResize);
9 return () => window.removeEventListener('resize', handleResize);
10}, []);
1// 潜在内存泄漏
2useEffect(() => {
3 const timer = setInterval(() => {
4 // 引用过时 state
5 }, 1000);
6 return () => clearInterval(timer);
7}, []);
8
9// 解决方案:使用 ref 保持最新引用
10const stateRef = useRef(state);
11stateRef.current = state;
结合 CustomEvent 实现跨组件通信:
1// 发布事件
2const emitCustomEvent = (detail) => {
3 const event = new CustomEvent('app:notification', {
4 detail,
5 bubbles: true,
6 });
7 document.dispatchEvent(event);
8};
9
10// 订阅事件
11useEffect(() => {
12 const handler = (e) => {
13 console.log('Received:', e.detail);
14 };
15 document.addEventListener('app:notification', handler);
16 return () => document.removeEventListener('app:notification', handler);
17}, []);
对于需要极高响应速度的场景(如游戏开发),建议:
requestAnimationFrame
节流1// 高性能处理示例
2useEffect(() => {
3 let animationFrame;
4
5 const handleMove = (e) => {
6 cancelAnimationFrame(animationFrame);
7 animationFrame = requestAnimationFrame(() => {
8 // 执行轻量级操作
9 element.style.transform = `translate(${e.clientX}px, ${e.clientY}px)`;
10 });
11 };
12
13 window.addEventListener('mousemove', handleMove);
14 return () => {
15 window.removeEventListener('mousemove', handleMove);
16 cancelAnimationFrame(animationFrame);
17 };
18}, []);
并发模式下的优先级调度
React 18 引入的并发渲染器将事件优先级与调度深度整合,实现更细粒度的更新控制。
Web Components 集成
随着 Web Components 的普及,React 事件系统需要更好地处理 Shadow DOM 的边界问题。
编译时优化
类似 SolidJS 的编译时事件处理优化可能被引入,减少运行时开销。
WASM 集成
对于性能敏感操作,未来可能通过 WebAssembly 实现事件处理逻辑。
理解 React 合成事件与原生事件的交互机制,是构建高性能 React 应用的关键。开发者需要根据具体场景选择合适的事件处理策略:对于常规交互优先使用合成事件,在性能关键路径谨慎使用原生事件。随着 React 的持续演进,建议定期关注官方文档更新,特别是在升级主版本时注意事件系统的行为变化。