React 事件系统的设计原理 1

14 阅读1分钟

著有《React 源码》《React 用到的一些算法》《javascript地月星》等多个专栏。欢迎关注。

文章不好写,要是有帮助别忘了点赞,收藏,评论 ~你的鼓励是我继续挖干货的动力🔥。

另外,本文为原创内容,商业转载请联系作者获得授权,非商业转载需注明出处,感谢理解~

总流程

事件系统的设计原理:

  1. ✅ 给容器绑定统一的事件监听器
  2. 创建合成事件对象
  3. 收集Fiber事件(详细、推荐阅读)
  4. 事件回调的派发

React采用事件委托机制,所有可以被委托的事件都委托到DOM容器上。(少数不能委托的事件,需要绑定在元素本身。)
虽然React采用的是事件委托,但具体的事件、事件回调函数是写在各个Fiber节点上的,后续还需要事件收集。

listenToAllSupportedEvents

给容器绑定统一的事件监听器。容器有两种类型tag=HostRoot以及tag=HostPortal,它们的container。

// HostRoot
function createRoot(container, options) {
  ...
  var rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container;
  listenToAllSupportedEvents(rootContainerElement);//#rootA #rootB #modal-container
  return new ReactDOMRoot(root);
}
// HostPortal
function preparePortalMount(portalInstance) {
  listenToAllSupportedEvents(portalInstance);
}
function completeWork(current, workInProgress, renderLanes) {

  switch (workInProgress.tag) {
      ...
    case HostPortal:
      popHostContainer(workInProgress);
      updateHostContainer(current, workInProgress);

      if (current === null) {
        preparePortalMount(workInProgress.stateNode.containerInfo); //给HostPortal的容器添加事件,绑定dispatchEventForPluginEventSystem
      }

      bubbleProperties(workInProgress);
      return null;
  }
}
var listeningMarker = '_reactListening' + Math.ranDOM().toString(36).slice(2);
function listenToAllSupportedEvents(rootContainerElement) {
  if (!rootContainerElement[listeningMarker]) {
    rootContainerElement[listeningMarker] = true;
    //包含了所有可以委托的事件
    allNativeEvents.forEach(function (DOMEventName) {
      // We handle selectionchange separately because it
      // doesn't bubble and needs to be on the document.
      if (DOMEventName !== 'selectionchange') {
        if (!nonDelegatedEvents.has(DOMEventName)) {
          listenToNativeEvent(DOMEventName, false, rootContainerElement);
        }
        //包含了dispatchEventForPluginEventSystem
        listenToNativeEvent(DOMEventName, true, rootContainerElement);
      }
    });
    var ownerDocument = rootContainerElement.nodeType === DOCUMENT_NODE ? rootContainerElement : rootContainerElement.ownerDocument;

    if (ownerDocument !== null) {
      // The selectionchange event also needs deduplication
      // but it is attached to the document.
      if (!ownerDocument[listeningMarker]) {
        ownerDocument[listeningMarker] = true;
        listenToNativeEvent('selectionchange', false, ownerDocument);
      }
    }
  }
}