在completeWork的 HostComponent中提到过,finalizeInitialChildren会绑定元素的属性。react 将事件当做属性进行处理的。ensureListeningTo 用于绑定元素的事件,执行代码如下:

    1. function ensureListeningTo(rootContainerElement, registrationName) {
    2. var isDocumentOrFragment = rootContainerElement.nodeType === DOCUMENT_NODE ||
    3. rootContainerElement.nodeType === DOCUMENT_FRAGMENT_NODE;
    4. var doc = isDocumentOrFragment ? rootContainerElement : rootContainerElement.ownerDocument;
    5. listenTo(registrationName, doc);
    6. }
    7. //简化代码
    8. export function listenTo(registrationName: string,mountAt: Document | Element,) {
    9. const isListening = getListeningForDocument(mountAt);
    10. const dependencies = registrationNameDependencies[registrationName];
    11. for (let i = 0; i < dependencies.length; i++) {
    12. const dependency = dependencies[i];
    13. if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) {//注意:仅挂载一次
    14. switch (dependency) {
    15. case TOP_SCROLL:
    16. trapCapturedEvent(TOP_SCROLL, mountAt);
    17. break;
    18. case TOP_FOCUS:
    19. case TOP_BLUR:
    20. trapCapturedEvent(TOP_FOCUS, mountAt);
    21. trapCapturedEvent(TOP_BLUR, mountAt);
    22. isListening[TOP_BLUR] = true;
    23. isListening[TOP_FOCUS] = true;
    24. break;
    25. case TOP_CANCEL:
    26. case TOP_CLOSE:
    27. if (isEventSupported(getRawEventName(dependency))) {
    28. trapCapturedEvent(dependency, mountAt);
    29. }
    30. break;
    31. case TOP_INVALID:
    32. case TOP_SUBMIT:
    33. case TOP_RESET:
    34. break;
    35. default:
    36. const isMediaEvent = mediaEventTypes.indexOf(dependency) !== -1;
    37. if (!isMediaEvent) {
    38. trapBubbledEvent(dependency, mountAt);
    39. }
    40. break;
    41. }
    42. isListening[dependency] = true;
    43. }
    44. }
    45. }

    registrationNameDependencies 是初始的事件名称。

    react 使用事件委托处理事件。当一个元素有事件时,将会为doc添加事件监听,且只添加一次,例如:

    1. <div onClick={()=>{ console.log(2) }}>
    2. <span onClick={()=>{ console.log(1) }}>1</span>
    3. <span>2</span>
    4. </div>

    div和 span上都有事件,但在挂载时doc上只挂载一次事件。

    注意:为了处理iphone中的click bug,会为onClick添加一个空方法。

    1. export function setInitialProperties(domElement,tag,rawProps,rootContainerElement){
    2. const isCustomComponentTag = isCustomComponent(tag, rawProps);
    3. //...省略 react子遇到一dom元素时会绑定一些事件,例如为input元素绑定onChange事件。
    4. assertValidProps(tag, props);
    5. setInitialDOMProperties(tag,domElement,rootContainerElement,props,isCustomComponentTag);
    6. switch (tag) {
    7. // 省略...其它操作
    8. default:
    9. if (typeof props.onClick === 'function') {//绑定一个 function
    10. trapClickOnNonInteractiveElement(((domElement: any): HTMLElement));
    11. }
    12. break;
    13. }
    14. }

    react中的事件分为 CapturedEvent 和 BubbledEvent。此处的onClick属于 BubbledEvent,listenTo 中使用trapBubbledEvent 绑定事件。

    1. export function trapBubbledEvent(topLevelType, element) {
    2. if (!element) { return null; }
    3. const dispatch = isInteractiveTopLevelEventType(topLevelType)
    4. ? dispatchInteractiveEvent : dispatchEvent;
    5. addEventBubbleListener(
    6. element, getRawEventName(topLevelType), dispatch.bind(null, topLevelType));
    7. }
    8. export function addEventBubbleListener(element,eventType, listener): void {
    9. element.addEventListener(eventType, listener, false);
    10. }
    11. //包了一层 dispatchEvent
    12. function dispatchInteractiveEvent(topLevelType, nativeEvent) {
    13. interactiveUpdates(dispatchEvent, topLevelType, nativeEvent);
    14. }

    trapBubbledEvent绑定是一个 dispatch 事件。dispatch.bind(null, topLevelType) 是一个偏函数,偏函数的作用是当调用dispatch时第一个参数是 topLevelType。

    事件是在completeUnitOfWork中绑定的,调用栈为:
    eventTrace.jpg