react源码中的生命周期和事件系统

2022-11-23 08:38:24 浏览数 (2)

这一章我想跟大家探讨的是React生命周期事件系统

jsx的编译结果

在这里插入图片描述在这里插入图片描述

因为前面也讲到jsxv17中的编译结果,除了标签名,其他的挂在标签上的属性(比如class),事件(比如click事件),都是放在_jsxRuntime.jsx函数的第二参数上。表现为key:value的形式,这里我们就会产生几个问题。

  • react是怎么知道函数体(事件处理函数)是什么的呢?
  • react又是在什么阶段去处理这些事件的呢?

这里我们先卖个关子,我们先来看看一个完整的React应用的完整的生命周期是怎么样的,我们都知道React分为类组件函数组件,两种组件的部分生命周期函数发生了一些变化,在这里我会分别对两种组件的生命周期做讲解。

React组件的生命周期

组件挂载的时候的执行顺序

因为在_jsxRuntime.jsx编译jsx对象的时候,我们会去做处理defaultPropspropType静态类型检查。所以这也算是一个生命周期吧。Class组件具有单独的constructor,在mount阶段会去执行这个构造函数,我曾经做了部分研究,这个constructor是类组件独有的,还是class独有的?后来发现这个constructorclass独有的,怎么理解这句话呢?

  • 在《重学ES6》这本书中提到:ES6中新增了类的概念,一个类必须要有constructor方法,如果在类中没有显示定义,则一个空的constructor方法会被默认添加。对于ReactClassComponent来讲需要constructor的作用就是用来初始化state和绑定事件,另外一点就是声明了constructor,就必须要调用super,我们一般用来接收props传递。假如你不写constructor,那就没法用props了,当然了要在constructor中使用props,也必须用super接收才行。
  • 所以对于类组件来讲的话,constructor也算是一个生命周期钩子。

getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。

render被调用时,它会检查 this.propsthis.state 的变化并返回以下类型之一:

  • React 元素。通常通过 JSX 创建。例如,<div /> 会被 React 渲染为 DOM 节点,<MyComponent /> 会被 React 渲染为自定义组件,无论是 <div /> 还是 <MyComponent /> 均为 React 元素。
  • 数组或 fragments。 使得 render 方法可以返回多个元素。
  • Portals。可以渲染子节点到不同的 DOM 子树中。
  • 字符串或数值类型。它们在 DOM 中会被渲染为文本节点。
  • 布尔类型或 null。什么都不渲染。(主要用于支持返回 test && <Child /> 的模式,其中 test 为布尔类型。)

componentDidMount() 会在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。在这里适合去发送异步请求。

组件更新的时候的执行顺序

getDerivedStateFromProps => shouldComponentUpdate() => render() => getSnapshotBeforeUpdate() => componentDidUpdate()

  • 其中shouldComponentUpdate也被称作为性能优化的一种钩子,其作用在于比较两次更新的stateprops是否发生变化,决定是否更新当前组件,比较的方式是浅比较,以前讲过这里不再复述。
  • getSnapshotBeforeUpdate函数在最近一次渲染输出(提交到DOM节点)之前调用。它使得组件能在发生更改之前从DOM中捕获一些信息。此生命周期方法的任何返回值将作为参数传递给componentDidUpdate()
  • componentDidUpdate() 会在更新后会被立即调用。首次渲染不会执行此方法。
组件卸载的时候执行顺序

componentWillUnmount() 会在组件卸载销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除timer取消网络请求等等。

组件在发生错误的时候执行顺序

getDerivedStateFromError => componentDidCatch 关于这两个钩子,同学们可自行移步官网。

当然上面的只是ClassComponent的生命周期执行顺序,而在新版本的React中已经删除掉了componentDidMountcomponentDidUpdatecomponentWillUnMount,取而代之的是useEffectuseLayoutEffect。那究竟是谁代替了他们三个呢?这个问题我已经在React源码解析系列(八) -- 深入hooks的原理 中阐述过了,这里不再复述。

现在来回答第一个问题:react是怎么知道函数体是什么的呢? 这个问题其实问的非常好,babel解析后的jsx本身只会去关注{事件名:函数名},但是每一个事件都是需要被注册、绑定的,然后通过事件触发,来执行绑定函数的函数体。解释这种问题还是得要去看一下源码里面的具体实现。

listenToAllSupportedEvents

我们在React源码解析系列(二) -- 初始化组件的创建更新流程中提到rootFiberFiberRoot的创建,创建完毕之后我们就需要去创建事件,创建事件的入口函数为listenToAllSupportedEvents

代码语言:javascript复制
// packages/react-dom/src/events/DOMPluginEventSystem.js
export function listenToAllSupportedEvents(rootContainerElement: EventTarget) {
  if (enableEagerRootListeners) { // enableEagerRootListeners默认值为false

    // listeningMarker就是一个随机数 字符串,作为唯一值
    if (rootContainerElement[listeningMarker]) {
      ...
      return;
    }
    rootContainerElement[listeningMarker] = true;

    // 遍历allNativeEvents的所有事件
    allNativeEvents.forEach(domEventName => {

      // 如果不是委托事件,没有冒泡阶段
      // nonDelegatedEvents全部媒体事件,
      if (!nonDelegatedEvents.has(domEventName)) {
        listenToNativeEvent(
          domEventName,
          false,
          ((rootContainerElement: any): Element),
          null,
        );
      }
      // 有冒泡阶段
      listenToNativeEvent(
        domEventName,
        true,
        ((rootContainerElement: any): Element),
        null,
      );
    });
  }
}

//listeningMarker
// 唯一标识
const listeningMarker =
  '_reactListening'  
  Math.random()
    .toString(36)
    .slice(2);

我们在这里必须要关注一下allNativeEvents是什么东西,allNativeEvents在源码里体现为一个存储着事件名的Set结构:

代码语言:javascript复制
export const allNativeEvents: Set<DOMEventName> = new Set();

接下来看看listenToNativeEvent究竟干了些什么。

listenToNativeEvent

代码语言:javascript复制
export function listenToNativeEvent(
  domEventName: DOMEventName,// 事件名
  isCapturePhaseListener: boolean, // 根据上个函数,这里应该是确定是是能够冒泡的事件
  rootContainerElement: EventTarget,  targetElement: Element | null,  eventSystemFlags?: EventSystemFlags = 0, // 事件标记
): void {

  let target = rootContainerElement;

  //如果是selectionchange事件,加到dom上
  if (
    domEventName === 'selectionchange' &&
    (rootContainerElement: any).nodeType !== DOCUMENT_NODE
  ) {
    target = (rootContainerElement: any).ownerDocument;
  }

  if (
    targetElement !== null &&
    !isCapturePhaseListener &&
    nonDelegatedEvents.has(domEventName) // 非冒泡事件
  ) {
   ...

    //滚动事件不冒泡
    if (domEventName !== 'scroll') {
      return;
    }

    eventSystemFlags |= IS_NON_DELEGATED; // is_non_delegated 不是委托事件
    target = targetElement;
  }
  //获取dom上绑定的事件名数组 Set[] || 
  const listenerSet = getEventListenerSet(target);
  // 处理事件名为捕获阶段与冒泡阶段 Set[click_bubble]
  const listenerSetKey = getListenerSetKey(
    domEventName,
    isCapturePhaseListener,
  );

  // 把没有打过的IS_CAPTURE_PHASE的符合条件的事件,打上标签
  if (!listenerSet.has(listenerSetKey)) {
    if (isCapturePhaseListener) {
      // 打上捕获的标签
      eventSystemFlags |= IS_CAPTURE_PHASE;
    }

    // 往节点上添加事件绑定
    addTrappedEventListener(
      target,
      domEventName,
      eventSystemFlags,
      isCapturePhaseListener,
    );

    // 往listenerSet中添加事件名
    listenerSet.add(listenerSetKey);
  }
}

//getEventListenerSet
export function getEventListenerSet(node: EventTarget): Set<string> {
  let elementListenerSet = (node: any)[internalEventHandlersKey];

  if (elementListenerSet === undefined) {
    // 创建一个Set来存放事件名
    elementListenerSet = (node: any)[internalEventHandlersKey] = new Set();
  }
  return elementListenerSet;
}

// getListenerSetKey
export function getListenerSetKey(
  domEventName: DOMEventName,  capture: boolean,
): string {
  // capture捕获,bubble冒泡
  return `${domEventName}__${capture ? 'capture' : 'bubble'}`;
}

// addTrappedEventListener
function addTrappedEventListener(
  targetContainer: EventTarget, // 容器
  domEventName: DOMEventName, // 事件名
  eventSystemFlags: EventSystemFlags, //事件名标识
  isCapturePhaseListener: boolean, // 事件委托
  isDeferredListenerForLegacyFBSupport?: boolean, 
) {

  // 创建具有优先级的事件监听函数,返回值为function
  let listener = createEventListenerWrapperWithPriority(
    targetContainer,
    domEventName,
    eventSystemFlags,
  );

  ...

  targetContainer =
    enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport
      ? (targetContainer: any).ownerDocument
      : targetContainer;

  let unsubscribeListener;
  ...

  // 区分捕获、冒泡 通过node.addEventListener绑定事件到节点上
  if (isCapturePhaseListener) {
    if (isPassiveListener !== undefined) {
      unsubscribeListener = addEventCaptureListenerWithPassiveFlag(
        targetContainer,
        domEventName,
        listener,
        isPassiveListener,
      );
    } else {
      unsubscribeListener = addEventCaptureListener(
        targetContainer,
        domEventName,
        listener,
      );
    }
  } else {
    if (isPassiveListener !== undefined) {
      unsubscribeListener = addEventBubbleListenerWithPassiveFlag(
        targetContainer,
        domEventName,
        listener,
        isPassiveListener,
      );
    } else {
      unsubscribeListener = addEventBubbleListener(
        targetContainer,
        domEventName,
        listener,
      );
    }
  }
}

// createEventListenerWrapperWithPriority
export function createEventListenerWrapperWithPriority(
  targetContainer: EventTarget, // 容器
  domEventName: DOMEventName, // 事件名
  eventSystemFlags: EventSystemFlags, //标识
): Function {

  // 获取事件Map里面已经标记好的优先级
  const eventPriority = getEventPriorityForPluginSystem(domEventName);

  let listenerWrapper;
  // 根据优先级不同绑定不同的执行函数
  switch (eventPriority) {
    //离散事件
    case DiscreteEvent:
      listenerWrapper = dispatchDiscreteEvent;
      break;
    // 用户交互阻塞渲染的事件 
    case UserBlockingEvent:
      listenerWrapper = dispatchUserBlockingUpdate;
      break;
    // 其他事件
    case ContinuousEvent:
    // 默认事件
    default:
      listenerWrapper = dispatchEvent;
      break;
  }

  return listenerWrapper.bind(
    null,
    domEventName,
    eventSystemFlags,
    targetContainer,
  );
}

相关参考视频讲解:进入学习

在这里我们关注一下获取优先级getEventPriorityForPluginSystem这里,你会不会产生一个疑问,React内部事件我们知道React本身一定会给优先级的,但是非React事件呢,比如原生事件,他们的优先级是怎么确定的呢?不要急,我们看一看就知道了。

getEventPriorityForPluginSystem

代码语言:javascript复制
export function getEventPriorityForPluginSystem(
  domEventName: DOMEventName,
): EventPriority {
  // 通过事件名获取优先级
  const priority = eventPriorities.get(domEventName);

  // ContinuousEvent为默认优先级 
  return priority === undefined ? ContinuousEvent : priority;
}

//eventPriorities
const eventPriorities = new Map();

eventPriorities本身是一个Map结构,我们可以发现两个地方进行了eventPriorities.set()的操作。

代码语言:javascript复制
// packages/react-dom/src/events/DOMEventProperties.js
function setEventPriorities(
  eventTypes: Array<DOMEventName>,  priority: EventPriority,
): void {
  for (let i = 0; i < eventTypes.length; i  ) {
    // 往eventPriorities添加优先级
    eventPriorities.set(eventTypes[i], priority);
  }
}

//registerSimplePluginEventsAndSetTheirPriorities
function registerSimplePluginEventsAndSetTheirPriorities(
  eventTypes: Array<DOMEventName | string>,  priority: EventPriority,
): void {
  for (let i = 0; i < eventTypes.length; i  = 2) {
    const topEvent = ((eventTypes[i]: any): DOMEventName);
    const event = ((eventTypes[i   1]: any): string);
    const capitalizedEvent = event[0].toUpperCase()   event.slice(1);
    // 改变事件名 click => onClick
    const reactName = 'on'   capitalizedEvent;
    // 往eventPriorities添加优先级
    eventPriorities.set(topEvent, priority);
    topLevelEventsToReactNames.set(topEvent, reactName);

    // 注册捕获阶段,冒泡阶段的事件
    registerTwoPhaseEvent(reactName, [topEvent]);
  }
}

这就说明,在这两个函数里面已经做好了优先级的处理,那我们可以去看一下在哪里调用的这两个函数,我们发现在函数registerSimpleEvents中,执行了这两个函数,往eventPriorities里面添加优先级。

代码语言:javascript复制
// packages/react-dom/src/events/DOMEventProperties.js
export function registerSimpleEvents() {
  // 处理离散事件优先级
  registerSimplePluginEventsAndSetTheirPriorities(
    discreteEventPairsForSimpleEventPlugin,
    DiscreteEvent,
  );
  // 处理用户阻塞事件优先级
  registerSimplePluginEventsAndSetTheirPriorities(
    userBlockingPairsForSimpleEventPlugin,
    UserBlockingEvent,
  );
  // 处理默认事件优先级
  registerSimplePluginEventsAndSetTheirPriorities(
    continuousPairsForSimpleEventPlugin,
    ContinuousEvent,
  );
  // 处理其他事件优先级
  setEventPriorities(otherDiscreteEvents, DiscreteEvent);
}

上述代码中可以看到有非常多的Plugin,代码如下:

代码语言:javascript复制
const discreteEventPairsForSimpleEventPlugin = [
  ('cancel': DOMEventName), 'cancel',
  ('click': DOMEventName), 'click',
  ('close': DOMEventName), 'close',
  ('contextmenu': DOMEventName), 'contextMenu',
  ('copy': DOMEventName), 'copy',
  ('cut': DOMEventName), 'cut',
  ('auxclick': DOMEventName), 'auxClick',
  ('dblclick': DOMEventName), 'doubleClick', // Careful!
  ('dragend': DOMEventName), 'dragEnd',
  ('dragstart': DOMEventName), 'dragStart',
  ('drop': DOMEventName), 'drop',
  ('focusin': DOMEventName), 'focus', // Careful!
  ('focusout': DOMEventName), 'blur', // Careful!
  ('input': DOMEventName), 'input',
  ('invalid': DOMEventName), 'invalid',
  ('keydown': DOMEventName), 'keyDown',
  ('keypress': DOMEventName), 'keyPress',
  ('keyup': DOMEventName), 'keyUp',
  ('mousedown': DOMEventName), 'mouseDown',
  ('mouseup': DOMEventName), 'mouseUp',
  ('paste': DOMEventName), 'paste',
  ('pause': DOMEventName), 'pause',
  ('play': DOMEventName), 'play',
  ('pointercancel': DOMEventName), 'pointerCancel',
  ('pointerdown': DOMEventName), 'pointerDown',
  ('pointerup': DOMEventName), 'pointerUp',
  ('ratechange': DOMEventName), 'rateChange',
  ('reset': DOMEventName), 'reset',
  ('seeked': DOMEventName), 'seeked',
  ('submit': DOMEventName), 'submit',
  ('touchcancel': DOMEventName), 'touchCancel',
  ('touchend': DOMEventName), 'touchEnd',
  ('touchstart': DOMEventName), 'touchStart',
  ('volumechange': DOMEventName), 'volumeChange',
];

const otherDiscreteEvents: Array<DOMEventName> = [
  'change',
  'selectionchange',
  'textInput',
  'compositionstart',
  'compositionend',
  'compositionupdate',
];

const userBlockingPairsForSimpleEventPlugin: Array<string | DOMEventName> = [
  ('drag': DOMEventName), 'drag',
  ('dragenter': DOMEventName), 'dragEnter',
  ('dragexit': DOMEventName), 'dragExit',
  ('dragleave': DOMEventName), 'dragLeave',
  ('dragover': DOMEventName), 'dragOver',
  ('mousemove': DOMEventName), 'mouseMove',
  ('mouseout': DOMEventName), 'mouseOut',
  ('mouseover': DOMEventName), 'mouseOver',
  ('pointermove': DOMEventName), 'pointerMove',
  ('pointerout': DOMEventName), 'pointerOut',
  ('pointerover': DOMEventName), 'pointerOver',
  ('scroll': DOMEventName), 'scroll',
  ('toggle': DOMEventName), 'toggle',
  ('touchmove': DOMEventName), 'touchMove',
  ('wheel': DOMEventName), 'wheel',
];

const continuousPairsForSimpleEventPlugin: Array<string | DOMEventName> = [
  ('abort': DOMEventName), 'abort',
  (ANIMATION_END: DOMEventName), 'animationEnd',
  (ANIMATION_ITERATION: DOMEventName), 'animationIteration',
  (ANIMATION_START: DOMEventName), 'animationStart',
  ('canplay': DOMEventName), 'canPlay',
  ('canplaythrough': DOMEventName), 'canPlayThrough',
  ('durationchange': DOMEventName), 'durationChange',
  ('emptied': DOMEventName), 'emptied',
  ('encrypted': DOMEventName), 'encrypted',
  ('ended': DOMEventName), 'ended',
  ('error': DOMEventName), 'error',
  ('gotpointercapture': DOMEventName), 'gotPointerCapture',
  ('load': DOMEventName), 'load',
  ('loadeddata': DOMEventName), 'loadedData',
  ('loadedmetadata': DOMEventName), 'loadedMetadata',
  ('loadstart': DOMEventName), 'loadStart',
  ('lostpointercapture': DOMEventName), 'lostPointerCapture',
  ('playing': DOMEventName), 'playing',
  ('progress': DOMEventName), 'progress',
  ('seeking': DOMEventName), 'seeking',
  ('stalled': DOMEventName), 'stalled',
  ('suspend': DOMEventName), 'suspend',
  ('timeupdate': DOMEventName), 'timeUpdate',
  (TRANSITION_END: DOMEventName), 'transitionEnd',
  ('waiting': DOMEventName), 'waiting',
];

而在registerSimplePluginEventsAndSetTheirPriorities函数里面,我们发现了注册事件registerTwoPhaseEvent,我们继续去深究一下,究竟是怎么注册的。

registerTwoPhaseEvent

代码语言:javascript复制
export function registerTwoPhaseEvent(
  registrationName: string, // 注册事件reactName
  dependencies: Array<DOMEventName>, // 依赖
): void {
  registerDirectEvent(registrationName, dependencies);
  registerDirectEvent(registrationName   'Capture', dependencies);
}

registerDirectEvent

代码语言:javascript复制
// Mapping from registration name to event name
export const registrationNameDependencies = {};

export function registerDirectEvent(
  registrationName: string, //react事件名onClick
  dependencies: Array<DOMEventName>, // 依赖
) {
  ...

  // 以react事件名为key,dependencies为value的map对象
  registrationNameDependencies[registrationName] = dependencies;

  if (__DEV__) {
    ...
  }

  // 遍历依赖,把每一项加入到allNativeEvents中去
  for (let i = 0; i < dependencies.length; i  ) {
    allNativeEvents.add(dependencies[i]);
  }
}

前面说allNativeEvents是一个存储事件名的Set,这里往里面添加事件名,就完成了事件注册。还没有完,上面说过了事件注册,与事件绑定,但是用户点击的时候,应该怎么去触发呢?上面的代码,在获取了优先级之后,每个事件会根据当前优先级生成一个listenerWrapper,这个listenerWrapper也就是对应的事件触发绑定的函数。dispatchDiscreteEventdispatchUserBlockingUpdatedispatchEvent三个函数都通过bind执行,我们都知道bind绑定的函数,会返回一个新函数,并不会立即执行。所以我们也必须看看他的入参是什么。

  • thisnull
  • argmentsdomEventName:事件名,eventSystemFlags:事件类型标记,targetContainer:目标容器。

dispatchEvent

因为不管是dispatchDiscreteEventdispatchUserBlockingUpdate最后都会去执行dispatchEvent,所以我们可以看看他的实现。

代码语言:javascript复制
// packages/react-dom/src/events/ReactDOMEventListener.js
export function dispatchEvent(
  domEventName: DOMEventName, // 事件名
  eventSystemFlags: EventSystemFlags, // 事件类型标记
  targetContainer: EventTarget, // 目标容器
  nativeEvent: AnyNativeEvent, // native事件
): void {
  ...
  // 如果被阻塞了,尝试调度事件 并返回挂载的实例或者容器
  const blockedOn = attemptToDispatchEvent(
    domEventName,
    eventSystemFlags,
    targetContainer,
    nativeEvent,
  );

  if (blockedOn === null) {
    // We successfully dispatched this event.
    ...
    return;
  }

  ...

  // 调度事件,触发事件
  dispatchEventForPluginEventSystem(
    domEventName,
    eventSystemFlags,
    nativeEvent,
    null,
    targetContainer,
  );
}

// dispatchEventForPluginEventSystem
export function dispatchEventForPluginEventSystem(
  domEventName: DOMEventName,  eventSystemFlags: EventSystemFlags,  nativeEvent: AnyNativeEvent,  targetInst: null | Fiber,  targetContainer: EventTarget,
): void {
  ...
  //批量更新事件 
  batchedEventUpdates(() =>
    dispatchEventsForPlugins(
      domEventName,
      eventSystemFlags,
      nativeEvent,
      ancestorInst,
      targetContainer,
    ),
  );
}

// batchedEventUpdates
export function batchedEventUpdates(fn, a, b) {
  ...
  isBatchingEventUpdates = true;
  try {
    // fn : ()=>dispatchEventsForPlugins
    //(domEventName,eventSystemFlags,ativeEvent,ancestorInst,targetContainer,),
    // a: undefined
    // b: undefined
    return batchedEventUpdatesImpl(fn, a, b); 
    // batchedEventUpdatesImpl(fn, a, b) =>
    // Defaults
    // let batchedUpdatesImpl = function(fn, bookkeeping) {
    // return fn(bookkeeping); 执行dispatchEventsForPlugins
};
  } finally {
    ...
  }
}

dispatchEventsForPlugins

代码语言:javascript复制
function dispatchEventsForPlugins(
  domEventName: DOMEventName,  eventSystemFlags: EventSystemFlags,  nativeEvent: AnyNativeEvent,  targetInst: null | Fiber,  targetContainer: EventTarget,
): void {
  const nativeEventTarget = getEventTarget(nativeEvent);
  const dispatchQueue: DispatchQueue = [];
  //创建合成事件,遍历fiber链表,将会触发的事件加入到dispatchQueue中
  extractEvents(
    dispatchQueue,
    domEventName,
    targetInst,
    nativeEvent,
    nativeEventTarget,
    eventSystemFlags,
    targetContainer,
  );
  //触发时间队列,执行事件
  processDispatchQueue(dispatchQueue, eventSystemFlags);
}

//extractEvents
function extractEvents(
  dispatchQueue: DispatchQueue,  domEventName: DOMEventName,  targetInst: null | Fiber,  nativeEvent: AnyNativeEvent,  nativeEventTarget: null | EventTarget,  eventSystemFlags: EventSystemFlags,  targetContainer: EventTarget,
) {
  ...
  let from;
  let to;
  ...

  const leave = new SyntheticEventCtor(
    leaveEventType,
    eventTypePrefix   'leave',
    from,
    nativeEvent,
    nativeEventTarget,
  );
  leave.target = fromNode;
  leave.relatedTarget = toNode;

  let enter: KnownReactSyntheticEvent | null = null;
  ...
  accumulateEnterLeaveTwoPhaseListeners(dispatchQueue, leave, enter, from, to);
}

//accumulateEnterLeaveTwoPhaseListeners
export function accumulateEnterLeaveTwoPhaseListeners(
  dispatchQueue: DispatchQueue,  leaveEvent: KnownReactSyntheticEvent,  enterEvent: null | KnownReactSyntheticEvent,  from: Fiber | null,  to: Fiber | null,
): void {
  const common = from && to ? getLowestCommonAncestor(from, to) : null;

  if (from !== null) {
    accumulateEnterLeaveListenersForEvent(
      dispatchQueue,
      leaveEvent,
      from,
      common,
      false,
    );
  }
  if (to !== null && enterEvent !== null) {
    accumulateEnterLeaveListenersForEvent(
      dispatchQueue,
      enterEvent,
      to,
      common,
      true,
    );
  }
}

// accumulateEnterLeaveListenersForEvent
function accumulateEnterLeaveListenersForEvent(
  dispatchQueue: DispatchQueue,  event: KnownReactSyntheticEvent,  target: Fiber,  common: Fiber | null,  inCapturePhase: boolean,
): void {
  // 获取注册的事件名
  const registrationName = event._reactName;
  // 事件处理函数容器
  const listeners: Array<DispatchListener> = [];
  //节点实例
  let instance = target;

  // 遍历fiber,获取fiber上的事件对应的事件处理函数
  while (instance !== null) {

    if (instance === common) {
      break;
    }

    const {alternate, stateNode, tag} = instance;
    if (alternate !== null && alternate === common) {
      break;
    }

    if (tag === HostComponent && stateNode !== null) {
      const currentTarget = stateNode;

      // 根据捕获阶段,还是冒泡阶段处理不同的函数逻辑
      if (inCapturePhase) {
        const captureListener = getListener(instance, registrationName);
        if (captureListener != null) {

          // 加入到listeners中
          // instance:当前fiebr实例
          // currentTarget:当前dom
          listeners.unshift(
            createDispatchListener(instance, captureListener, currentTarget),
          );
        }
      } else if (!inCapturePhase) {
        // 冒泡
        const bubbleListener = getListener(instance, registrationName);
        if (bubbleListener != null) {
          // 加入到listeners中
          listeners.push(
            createDispatchListener(instance, bubbleListener, currentTarget),
          );
        }
      }
    }
    // 当前fiber实例的父级
    instance = instance.return;
  }
  if (listeners.length !== 0) {
    // 把事件、事件处理函数全部推到dispatchQueue中
    dispatchQueue.push({event, listeners});
  }
}
// processDispatchQueue
export function processDispatchQueue(
  dispatchQueue: DispatchQueue, // 事件队列
  eventSystemFlags: EventSystemFlags, // 事件类型标记
): void {
  const inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;

  for (let i = 0; i < dispatchQueue.length; i  ) {
    const {event, listeners} = dispatchQueue[i];
    // 执行事件,完成触发
    processDispatchQueueItemsInOrder(event, listeners, inCapturePhase);
    //  event system doesn't use pooling.
  }
  // This would be a good time to rethrow if any of the event handlers threw.
  rethrowCaughtError();
}

所以到这里,React的事件系统就解析完了,在这里上面的问题就很好解答了,React对事件名与事件处理函数对做了绑定,并在创建rootFiber的时候就做了事件注册事件绑定事件调度。那么他们的执行流程大致如下:

在这里插入图片描述在这里插入图片描述

总结

这一章主要是介绍组件在mountupdatedestroy阶段的生命周期执行顺序与React事件系统的注册,绑定,调度更新等

0 人点赞