[译]合成事件对象

1,705 阅读6分钟

前言

本文为意译,翻译过程中掺杂本人的理解,如有误导,请放弃继续阅读。

原文地址:SyntheticEvent

简介

这是一份关于SyntheticEvent wrapper的文档。SyntheticEvent wrapper是什么?可以这么说,react的合成事件系统是由(React’s Event SystemSyn)一个个的SyntheticEvent wrapper组成的。而SyntheticEvent wrapper有时候又简称为SyntheticEvent,中文翻译为合成事件对象。要想了解更多?请参考如何应用合成事件对象

概览

在run time中,你的react事件处理器将会被传入一个SyntheticEvent实例对象作为实参。SyntheticEvent是一个对浏览器原生事件对象(native event)进行跨浏览器封装的wrapper。它与浏览器的原生事件对象有着同样的接口,比如,它也包括了stopPropagation()preventDefault()这两个方法。它的目的是使得事件对象在不同的浏览器上都表现一致,提升开发者的开发体验。

如果你发现,你因为某种原因需要使用到浏览器原生的事件对象,那么你可以简单地通过读取SyntheticEvent对象的nativeEvent属性来访问它。像下面那样:

class SomeComponent extends React.Component {
    handleClick=(e)=> {
        const nativeEvent = e.nativeEvent;
        console.log(nativeEvent)
    }
    render(){
        return <button onClick={this.handleClick}>click me</button>
    }
}

每一个SyntheticEvent实例对象都包含了以下属性:

boolean bubbles
boolean cancelable
DOMEventTarget currentTarget
boolean defaultPrevented
number eventPhase
boolean isTrusted
DOMEvent nativeEvent
void preventDefault()
boolean isDefaultPrevented()
void stopPropagation()
boolean isPropagationStopped()
DOMEventTarget target
number timeStamp
string type

注意: 自v0.14版本起,在一个事件处理器中返回false,已经是无法阻止事件的传播了。你需要手动地调用e.stopPropagation()或者e.preventDefault()方法才行。

事件对象的共享机制(Event Pooling)

pooling,在这里我意译为“共享”。因为react的事件系统中,1) 同一个事件类型下的所有事件处理器都是共享同一个SyntheticEvent实例对象(浏览器原生的事件系统也是这样的); 2) 在不手动持久化的前提下,同一个事件处理器在不同的调用次序中都是共享同一个SyntheticEvent实例对象。基于上述的事实,我觉得把它翻译为“共享”较为贴切。这个翻译参考了剑桥英语词典对该单词的释义。也有人把它翻译为“池化”,我不知道哪个扑街带头这么干的,详见知乎吐槽

SyntheticEvent实例对象是共享的。这意味着SyntheticEvent实例对象是会被反复使用的,并且会在事件处理器被调用之后回收。所谓的回收,就是对该对象的所有属性值置为空值(will be nullified ),以便于在下一轮事件传播中使用。之所以采用这种机制,是为了提升react的运行性能。所以说,你不能使用异步的方式来访问SyntheticEvent实例对象的属性值

function onClick(event) {
  console.log(event); // => nullified object.
  console.log(event.type); // => "click"
  const eventType = event.type; // => "click"

  setTimeout(function() {
    console.log(event.type); // => null
    console.log(eventType); // => "click"
  }, 0);

  // will not work. this.state.clickEvent will only contain null values.
  this.setState({clickEvent: event});

  // You can still export event properties.
  this.setState({eventType: event.type});
}

上面的setTimeout虽然是写在的事件处理器里面,但是它终究是异步代码。上面代码的实际执行流程是:同步代码 --> SyntheticEvent对象(属性)置空值 --> 异步代码。因为异步代码总是在置空值的后面才执行,所以,我们访问到的总是空值。

注意: 如果你想在异步代码里面访问SyntheticEvent对象的属性。那么你应该手动调用event.persist(),你一旦这么做了,react就会把这个SyntheticEvent对象从共享池中去掉。这样子,用户就可以放心地去访问这个被从共享池中解放出来的SyntheticEvent对象了。

支持的事件

React对合成事件对象做了统一化,使得在不同的浏览器上对象所支持的属性都是一致的。

如果你想把一个事件处理器绑定在捕获阶段(capture phase),那么你就需要在该事件名后面追加一个Capture:例如,如果你想把点击事件的事件处理器绑定在事件的捕获阶段,那么在事件注册时,你应该写onClickCapture,而不是onClick

以下都是一些在事件冒泡阶段触发(bubbling phase)的事件。

  • Clipboard Events
  • Composition Events
  • Keyboard Events
  • Focus Events
  • Form Events
  • Mouse Events
  • Pointer Events
  • Touch Events
  • UI Events
  • Wheel Events
  • Media Events
  • Animation Events
  • Transiton Events
  • Other Events

参考指南

Clipboard Events

事件名:

onCopy
onCut
onPaste

属性:

DOMDataTransfer clipboardData

composition Events

The DOM CompositionEvent represents events that occur due to the user indirectly entering text. --- MDN

事件名:

onCompositionStart
onCompositionUpdate
onCompositionEnd

属性:

string data

keyboard Events

事件名:

onKeyDown
onKeyPress
onKeyUp

属性:

boolean altKey
number charCode
boolean ctrlKey
boolean getModifierState(key)
string key 
number keyCode
string locale
number location
boolean metaKey
boolean repeat
boolean shiftKey
number which

key的属性值的取值为任意DOM Level 3 Events spec所指定的值。

Focus Events

事件名:

onFocus
onBlue

属性:

DOMEventTarget relatedTarget

注意:不同于浏览器原生的focus事件只会在form元素上触发,这个合成的focus事件会在所有的React DOM元素上触发

Form Events

事件名:

onChange
onInput
onInvalid
onSubmit

更多关于onChange事件的信息,详见Forms

Mouse Events

事件名:

onClick
onDoubleClick
onDrag
onDragEnd
onDragEnter
onDragExit
onDragLeave
onDragOver
onDragStart
onDrop
onMouseDown
onMouseEnter
onMouseLeave
onMouseMove
onMouseOut
onMouseOver
onMouseUp
onContextMenu
onInvalid
onSubmit

The onMouseEnter and onMouseLeave events propagate from the element being left to the one being entered instead of ordinary bubbling and do not have a capture phase.(这段话意思难懂)。

属性名:

boolean altKey
number button
number buttons
number clientX
number clientY
boolean ctrlKey
boolean getModifierState(key)
boolean metaKey
number pageX
number pageY
DOMEventTarget relatedTarget
number screenX
number screenY
boolean shiftKey

Pointer Events

Pointer应该是在HTML5增加的事件,具体见MDN

事件名:

onPointerDown
onPointerMove
onPointerUp
onPointerCancel
onGotPointerCapture
onLostPointerCapture
onPointerEnter
onPointerLeave
onPointerOver
onPointerOut

The onPointerEnter and onPointerLeave events propagate from the element being left to the one being entered instead of ordinary bubbling and do not have a capture phase.(again)

属性:

如W3C规范所规定的那样,pointer事件继承自Mouse Events,并且多出了以下以下属性:

number pointerId
number width
number height
number pressure
number tangentialPressure
number tiltX
number tiltY
number twist
string pointerType
boolean isPrimary

在跨浏览器支持方面的一个提示:

Pointer events are not yet supported in every browser (at the time of writing this article, supported browsers include: Chrome, Firefox, Edge, and Internet Explorer). React之所以故意不对其他不支持的浏览器添加相应的polyfill,那是因为一个符合标准的(standard-conform)的polyfill会大大地增加react-dom的文件大小。

如果你的应用中需要用到pointer事件,那么我们会建议你单独引入一个第三方的pointer事件的polyfill。

selection Events

事件名:

onSelect

Touch Events

事件名:

onTouchStart
onTouchMove
onTouchCancel
onTouchEnd

属性:

boolean altKey
DOMTouchList changedTouches
boolean ctrlKey
boolean getModifierState(key)
boolean metaKey
boolean shiftKey
DOMTouchList targetTouches
DOMTouchList touches

UI Events

事件名:

onScroll

属性:

number detail
DOMAbstractView view

Wheel Events

事件名:

onWheel

属性:

number deltaMode
number deltaX
number deltaY
number deltaZ

Media Events

事件名:

onAbort
onCanPlay
onCanPlayThrough
onDurationChange // 播放时长的改变事件
onEmptied // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/emptied_event
onEncrypted // 多媒体加密事件
onEnded
onError
onLoadedData
onLoadedMetadata
onLoadStart
onPause
onPlay
onPlaying
onProgress
onRateChange
onSeeked
onSeeking
onStalled // stalled的意思是“停滞不前的”
onSuspend
onTimeUpdate
onVolumeChange
onWaiting

Image Events

事件名:

onLoad
onError

Animation Events

事件名:

onAnimationStart
onAnimationEnd
onAnimationIteration // 在一次动画迭代结束的时候触发

属性名:

string animationName
string pseudoElement
float elapsedTime

Transition Events

事件名:

onTransitionEnd

属性名:

string propertyName
string pseudoElement
float elapsedTime

其他事件

事件名:

onToggle