在completeWork的 HostComponent中提到过,finalizeInitialChildren会绑定元素的属性。react 将事件当做属性进行处理的。ensureListeningTo 用于绑定元素的事件,执行代码如下:
function ensureListeningTo(rootContainerElement, registrationName) {var isDocumentOrFragment = rootContainerElement.nodeType === DOCUMENT_NODE ||rootContainerElement.nodeType === DOCUMENT_FRAGMENT_NODE;var doc = isDocumentOrFragment ? rootContainerElement : rootContainerElement.ownerDocument;listenTo(registrationName, doc);}//简化代码export function listenTo(registrationName: string,mountAt: Document | Element,) {const isListening = getListeningForDocument(mountAt);const dependencies = registrationNameDependencies[registrationName];for (let i = 0; i < dependencies.length; i++) {const dependency = dependencies[i];if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) {//注意:仅挂载一次switch (dependency) {case TOP_SCROLL:trapCapturedEvent(TOP_SCROLL, mountAt);break;case TOP_FOCUS:case TOP_BLUR:trapCapturedEvent(TOP_FOCUS, mountAt);trapCapturedEvent(TOP_BLUR, mountAt);isListening[TOP_BLUR] = true;isListening[TOP_FOCUS] = true;break;case TOP_CANCEL:case TOP_CLOSE:if (isEventSupported(getRawEventName(dependency))) {trapCapturedEvent(dependency, mountAt);}break;case TOP_INVALID:case TOP_SUBMIT:case TOP_RESET:break;default:const isMediaEvent = mediaEventTypes.indexOf(dependency) !== -1;if (!isMediaEvent) {trapBubbledEvent(dependency, mountAt);}break;}isListening[dependency] = true;}}}
registrationNameDependencies 是初始的事件名称。
react 使用事件委托处理事件。当一个元素有事件时,将会为doc添加事件监听,且只添加一次,例如:
<div onClick={()=>{ console.log(2) }}><span onClick={()=>{ console.log(1) }}>1</span><span>2</span></div>
div和 span上都有事件,但在挂载时doc上只挂载一次事件。
注意:为了处理iphone中的click bug,会为onClick添加一个空方法。
export function setInitialProperties(domElement,tag,rawProps,rootContainerElement){const isCustomComponentTag = isCustomComponent(tag, rawProps);//...省略 react子遇到一dom元素时会绑定一些事件,例如为input元素绑定onChange事件。assertValidProps(tag, props);setInitialDOMProperties(tag,domElement,rootContainerElement,props,isCustomComponentTag);switch (tag) {// 省略...其它操作default:if (typeof props.onClick === 'function') {//绑定一个 functiontrapClickOnNonInteractiveElement(((domElement: any): HTMLElement));}break;}}
react中的事件分为 CapturedEvent 和 BubbledEvent。此处的onClick属于 BubbledEvent,listenTo 中使用trapBubbledEvent 绑定事件。
export function trapBubbledEvent(topLevelType, element) {if (!element) { return null; }const dispatch = isInteractiveTopLevelEventType(topLevelType)? dispatchInteractiveEvent : dispatchEvent;addEventBubbleListener(element, getRawEventName(topLevelType), dispatch.bind(null, topLevelType));}export function addEventBubbleListener(element,eventType, listener): void {element.addEventListener(eventType, listener, false);}//包了一层 dispatchEventfunction dispatchInteractiveEvent(topLevelType, nativeEvent) {interactiveUpdates(dispatchEvent, topLevelType, nativeEvent);}
trapBubbledEvent绑定是一个 dispatch 事件。dispatch.bind(null, topLevelType) 是一个偏函数,偏函数的作用是当调用dispatch时第一个参数是 topLevelType。
事件是在completeUnitOfWork中绑定的,调用栈为:
