在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') {//绑定一个 function
trapClickOnNonInteractiveElement(((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);
}
//包了一层 dispatchEvent
function dispatchInteractiveEvent(topLevelType, nativeEvent) {
interactiveUpdates(dispatchEvent, topLevelType, nativeEvent);
}
trapBubbledEvent绑定是一个 dispatch 事件。dispatch.bind(null, topLevelType) 是一个偏函数,偏函数的作用是当调用dispatch时第一个参数是 topLevelType。
事件是在completeUnitOfWork中绑定的,调用栈为: