React自己实现了一套高效的事件注册、存储、分发和重用逻辑,在DOM事件体系基础上做了很大改进,减少了内存消耗,简化了事件逻辑,并最大程度地解决了IE等浏览器的不兼容问题。
描述React的合成事件SyntheticEvent实际上就是React自己在内部实现的一套事件处理机制,它是浏览器的原生事件的跨浏览器包装器,除兼容所有浏览器外,它还拥有和浏览器原生事件相同的接口,包括stopPropagation()和preventDefault(),合成事件与浏览器的原生事件不同,也不会直接映射到原生事件,也就是说通常不要使用addEventListener为已创建的DOM元素添加监听器,而应该直接使用React中定义的事件机制,而且在混用的情况下原生事件如果定义了阻止冒泡可能会阻止合成事件的执行,当然如果确实需要使用原生事件去处理需求,可以通过事件触发传递的SyntheticEvent对象的nativeEvent属性获得原生Event对象的引用,React中的事件有以下几个特点:
React上注册的事件最终会绑定在document这个DOM上,而不是React组件对应的DOM,通过这种方式减少内存开销,所有的事件都绑定在document上,其他节点没有绑定事件,实际上就是事件委托的。
React自身实现了一套事件冒泡机制,使用React实现的Event对象与原生Event对象不同,不能相互混用。
React通过队列的形式,从触发的组件向父组件回溯,然后调用他们JSX中定义的callback。
React的合成事件SyntheticEvent与浏览器的原生事件不同,也不会直接映射到原生事件。
React通过对象池的形式管理合成事件对象的创建和销毁,减少了垃圾的生成和新对象内存的分配,提高了性能。
对于每个SyntheticEvent对象都包含以下属性:
boolean bubbles boolean cancelable DOMEventTarget currentTarget boolean defaultPrevented number eventPhase boolean isTrusted DOMEvent nativeEvent void preventDefault() boolean isDefaultPrevented() void stopPropagation() boolean isPropagationStopped() void persist() DOMEventTarget target number timeStamp string type支持的合成事件一览,注意以下的事件处理函数在冒泡阶段被触发,如需注册捕获阶段的事件处理函数,则应为事件名添加Capture,例如处理捕获阶段的点击事件请使用onClickCapture,而不是onClick。
<!-- 剪贴板事件 --> onCopy onCut onPaste <!-- 复合事件 --> onCompositionEnd onCompositionStart onCompositionUpdate <!-- 键盘事件 --> onKeyDown onKeyPress onKeyUp <!-- 焦点事件 --> onFocus onBlur <!-- 表单事件 --> onChange onInput onInvalid onReset onSubmit <!-- 通用事件 --> onError onLoad <!-- 鼠标事件 --> onClick onContextMenu onDoubleClick onDrag onDragEnd onDragEnter onDragExit onDragLeave onDragOver onDragStart onDrop onMouseDown onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver onMouseUp <!-- 指针事件 --> onPointerDown onPointerMove onPointerUp onPointerCancel onGotPointerCapture onLostPointerCapture onPointerEnter onPointerLeave onPointerOver onPointerOut <!-- 选择事件 --> onSelect <!-- 触摸事件 --> onTouchCancel onTouchEnd onTouchMove onTouchStart <!-- UI 事件 --> onScroll <!-- 滚轮事件 --> onWheel <!-- 媒体事件 --> onAbort onCanPlay onCanPlayThrough onDurationChange onEmptied onEncrypted onEnded onError onLoadedData onLoadedMetadata onLoadStart onPause onPlay onPlaying onProgress onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting <!-- 图像事件 --> onLoad onError <!-- 动画事件 --> onAnimationStart onAnimationEnd onAnimationIteration <!-- 过渡事件 --> onTransitionEnd <!-- 其他事件 --> onToggle <!-- https://zh-hans.reactjs.org/docs/events.html --> 示例一个简单的示例,同时绑定在一个DOM上的原生事件与React事件,因为原生事件阻止冒泡而导致React事件无法执行,同时我们也可以看到React传递的event并不是原生Event对象的实例,而是React自行实现维护的一个event对象。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>React</title> </head> <body> <div></div> </body> <script src="http://unpkg.zhimg.com/react@17/umd/react.development.js"></script> <script src="http://unpkg.zhimg.com/react-dom@17/umd/react-dom.development.js"></script> <script src="http://unpkg.zhimg.com/@babel/standalone/babel.min.js"></script> <script type="text/babel"> class ReactEvent extends React.PureComponent { componentDidMount(){ document.getElementById("btn-reactandnative").addEventListener("click", (e) => { console.log("原生事件执行", "handleNativeAndReact"); console.log("event instanceof Event:", e instanceof Event); e.stopPropagation(); // 阻止冒泡即会影响了React的事件执行 }); } handleNativeAndReact = (e) => { console.log("React事件执行", "handleNativeAndReact"); console.log("event instanceof Event:", e instanceof Event); } handleClick = (e) => { console.log("React事件执行", "handleClick"); console.log("event instanceof Event:", e instanceof Event); } render() { return ( <div className="pageIndex"> <button onClick={this.handleClick}>React 事件</button> <button onClick={this.handleNativeAndReact}>原生 + React 事件</button> </div> ) } } var vm = ReactDOM.render( <> <ReactEvent /> </>, document.getElementById("root") ); </script> </html> React事件系统