欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

react源碼中的生命周期和事件系統(tǒng)實例解析

 更新時間:2023年01月03日 09:11:20   作者:flyzz177  
這篇文章主要為大家介紹了react源碼中的生命周期和事件系統(tǒng)實例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

這一章我想跟大家探討的是React的生命周期與事件系統(tǒng)。

jsx的編譯結(jié)果

因為前面也講到jsxv17中的編譯結(jié)果,除了標(biāo)簽名,其他的掛在標(biāo)簽上的屬性(比如class),事件(比如click事件),都是放在_jsxRuntime.jsx函數(shù)的第二參數(shù)上。表現(xiàn)為key:value的形式,這里我們就會產(chǎn)生幾個問題。

  • react是怎么知道函數(shù)體(事件處理函數(shù))是什么的呢?
  • react又是在什么階段去處理這些事件的呢?

這里我們先賣個關(guān)子,我們先來看看一個完整的React應(yīng)用的完整的生命周期是怎么樣的,我們都知道React分為類組件函數(shù)組件,兩種組件的部分生命周期函數(shù)發(fā)生了一些變化,在這里我會分別對兩種組件的生命周期做講解。

React組件的生命周期

組件掛載的時候的執(zhí)行順序

因為在_jsxRuntime.jsx編譯jsx對象的時候,我們會去做處理defaultPropspropType靜態(tài)類型檢查。所以這也算是一個生命周期吧。Class組件具有單獨的constructor,在mount階段會去執(zhí)行這個構(gòu)造函數(shù),我曾經(jīng)做了部分研究,這個constructor是類組件獨有的,還是class獨有的?后來發(fā)現(xiàn)這個constructorclass獨有的,怎么理解這句話呢?

  • 在《重學(xué)ES6》這本書中提到:ES6中新增了類的概念,一個類必須要有constructor方法,如果在類中沒有顯示定義,則一個空的constructor方法會被默認添加。對于ReactClassComponent來講需要constructor的作用就是用來初始化state和綁定事件,另外一點就是聲明了constructor,就必須要調(diào)用super,我們一般用來接收props傳遞。假如你不寫constructor,那就沒法用props了,當(dāng)然了要在constructor中使用props,也必須用super接收才行。
  • 所以對于類組件來講的話,constructor也算是一個生命周期鉤子。

getDerivedStateFromProps 會在調(diào)用 render 方法之前調(diào)用,并且在初始掛載及后續(xù)更新時都會被調(diào)用。它應(yīng)返回一個對象來更新 state,如果返回 null 則不更新任何內(nèi)容。

render被調(diào)用時,它會檢查 this.propsthis.state 的變化并返回以下類型之一:

  • React 元素。通常通過 JSX 創(chuàng)建。例如,<div /> 會被 React 渲染為 DOM 節(jié)點,<MyComponent /> 會被 React 渲染為自定義組件,無論是 <div /> 還是 <MyComponent /> 均為 React 元素。
  • 數(shù)組或 fragments。 使得 render 方法可以返回多個元素。
  • Portals。可以渲染子節(jié)點到不同的 DOM 子樹中。
  • 字符串或數(shù)值類型。它們在 DOM 中會被渲染為文本節(jié)點。
  • 布爾類型或 null。什么都不渲染。(主要用于支持返回 test && <Child /> 的模式,其中 test 為布爾類型。)

componentDidMount() 會在組件掛載后(插入 DOM 樹中)立即調(diào)用。依賴于 DOM 節(jié)點的初始化應(yīng)該放在這里。在這里適合去發(fā)送異步請求。

組件更新的時候的執(zhí)行順序

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

  • 其中shouldComponentUpdate也被稱作為性能優(yōu)化的一種鉤子,其作用在于比較兩次更新的stateprops是否發(fā)生變化,決定是否更新當(dāng)前組件,比較的方式是淺比較,以前講過這里不再復(fù)述。
  • getSnapshotBeforeUpdate函數(shù)在最近一次渲染輸出(提交到DOM節(jié)點)之前調(diào)用。它使得組件能在發(fā)生更改之前從DOM中捕獲一些信息。此生命周期方法的任何返回值將作為參數(shù)傳遞給componentDidUpdate()。
  • componentDidUpdate() 會在更新后會被立即調(diào)用。首次渲染不會執(zhí)行此方法。

組件卸載的時候執(zhí)行順序

componentWillUnmount() 會在組件卸載及銷毀之前直接調(diào)用。在此方法中執(zhí)行必要的清理操作,例如,清除timer,取消網(wǎng)絡(luò)請求等等。

組件在發(fā)生錯誤的時候執(zhí)行順序

getDerivedStateFromError => componentDidCatch 關(guān)于這兩個鉤子,同學(xué)們可自行移步官網(wǎng)。

當(dāng)然上面的只是ClassComponent的生命周期執(zhí)行順序,而在新版本的React中已經(jīng)刪除掉了componentDidMount、componentDidUpdate、componentWillUnMount,取而代之的是useEffect、useLayoutEffect。那究竟是誰代替了他們?nèi)齻€呢?這個問題我已經(jīng)在React源碼解析系列(八) -- 深入hooks的原理 中闡述過了,這里不再復(fù)述。

現(xiàn)在來回答第一個問題:react是怎么知道函數(shù)體是什么的呢? 這個問題其實問的非常好,babel解析后的jsx本身只會去關(guān)注{事件名:函數(shù)名},但是每一個事件都是需要被注冊、綁定的,然后通過事件觸發(fā),來執(zhí)行綁定函數(shù)的函數(shù)體。解釋這種問題還是得要去看一下源碼里面的具體實現(xiàn)。

listenToAllSupportedEvents

我們在React源碼解析系列(二) -- 初始化組件的創(chuàng)建更新流程中提到rootFiberFiberRoot的創(chuàng)建,創(chuàng)建完畢之后我們就需要去創(chuàng)建事件,創(chuàng)建事件的入口函數(shù)為listenToAllSupportedEvents。

// packages/react-dom/src/events/DOMPluginEventSystem.js
export function listenToAllSupportedEvents(rootContainerElement: EventTarget) {
  if (enableEagerRootListeners) { // enableEagerRootListeners默認值為false
    // listeningMarker就是一個隨機數(shù)+字符串,作為唯一值
    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
// 唯一標(biāo)識
const listeningMarker =
  '_reactListening' +
  Math.random()
    .toString(36)
    .slice(2);

我們在這里必須要關(guān)注一下allNativeEvents是什么東西,allNativeEvents在源碼里體現(xiàn)為一個存儲著事件名的Set結(jié)構(gòu):

export const allNativeEvents: Set<DOMEventName> = new Set();

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

listenToNativeEvent

export function listenToNativeEvent(
  domEventName: DOMEventName,// 事件名
  isCapturePhaseListener: boolean, // 根據(jù)上個函數(shù),這里應(yīng)該是確定是是能夠冒泡的事件
  rootContainerElement: EventTarget,  targetElement: Element | null,  eventSystemFlags?: EventSystemFlags = 0, // 事件標(biāo)記
): 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上綁定的事件名數(shù)組 Set[] || 
  const listenerSet = getEventListenerSet(target);
  // 處理事件名為捕獲階段與冒泡階段 Set[click_bubble]
  const listenerSetKey = getListenerSetKey(
    domEventName,
    isCapturePhaseListener,
  );
  // 把沒有打過的IS_CAPTURE_PHASE的符合條件的事件,打上標(biāo)簽
  if (!listenerSet.has(listenerSetKey)) {
    if (isCapturePhaseListener) {
      // 打上捕獲的標(biāo)簽
      eventSystemFlags |= IS_CAPTURE_PHASE;
    }
    // 往節(jié)點上添加事件綁定
    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) {
    // 創(chuàng)建一個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, //事件名標(biāo)識
  isCapturePhaseListener: boolean, // 事件委托
  isDeferredListenerForLegacyFBSupport?: boolean, 
) {
  // 創(chuàng)建具有優(yōu)先級的事件監(jiān)聽函數(shù),返回值為function
  let listener = createEventListenerWrapperWithPriority(
    targetContainer,
    domEventName,
    eventSystemFlags,
  );
  ...
  targetContainer =
    enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport
      ? (targetContainer: any).ownerDocument
      : targetContainer;
  let unsubscribeListener;
  ...
  // 區(qū)分捕獲、冒泡 通過node.addEventListener綁定事件到節(jié)點上
  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, //標(biāo)識
): Function {
  // 獲取事件Map里面已經(jīng)標(biāo)記好的優(yōu)先級
  const eventPriority = getEventPriorityForPluginSystem(domEventName);
  let listenerWrapper;
  // 根據(jù)優(yōu)先級不同綁定不同的執(zhí)行函數(shù)
  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,
  );
}

在這里我們關(guān)注一下獲取優(yōu)先級getEventPriorityForPluginSystem這里,你會不會產(chǎn)生一個疑問,React內(nèi)部事件我們知道React本身一定會給優(yōu)先級的,但是非React事件呢,比如原生事件,他們的優(yōu)先級是怎么確定的呢?不要急,我們看一看就知道了。

getEventPriorityForPluginSystem

export function getEventPriorityForPluginSystem(
  domEventName: DOMEventName,
): EventPriority {
  // 通過事件名獲取優(yōu)先級
  const priority = eventPriorities.get(domEventName);
  // ContinuousEvent為默認優(yōu)先級 
  return priority === undefined ? ContinuousEvent : priority;
}
//eventPriorities
const eventPriorities = new Map();

eventPriorities本身是一個Map結(jié)構(gòu),我們可以發(fā)現(xiàn)兩個地方進行了eventPriorities.set()的操作。

// packages/react-dom/src/events/DOMEventProperties.js
function setEventPriorities(
  eventTypes: Array<DOMEventName>,  priority: EventPriority,
): void {
  for (let i = 0; i < eventTypes.length; i++) {
    // 往eventPriorities添加優(yōu)先級
    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添加優(yōu)先級
    eventPriorities.set(topEvent, priority);
    topLevelEventsToReactNames.set(topEvent, reactName);
    // 注冊捕獲階段,冒泡階段的事件
    registerTwoPhaseEvent(reactName, [topEvent]);
  }
}

這就說明,在這兩個函數(shù)里面已經(jīng)做好了優(yōu)先級的處理,那我們可以去看一下在哪里調(diào)用的這兩個函數(shù),我們發(fā)現(xiàn)在函數(shù)registerSimpleEvents中,執(zhí)行了這兩個函數(shù),往eventPriorities里面添加優(yōu)先級。

// packages/react-dom/src/events/DOMEventProperties.js
export function registerSimpleEvents() {
  // 處理離散事件優(yōu)先級
  registerSimplePluginEventsAndSetTheirPriorities(
    discreteEventPairsForSimpleEventPlugin,
    DiscreteEvent,
  );
  // 處理用戶阻塞事件優(yōu)先級
  registerSimplePluginEventsAndSetTheirPriorities(
    userBlockingPairsForSimpleEventPlugin,
    UserBlockingEvent,
  );
  // 處理默認事件優(yōu)先級
  registerSimplePluginEventsAndSetTheirPriorities(
    continuousPairsForSimpleEventPlugin,
    ContinuousEvent,
  );
  // 處理其他事件優(yōu)先級
  setEventPriorities(otherDiscreteEvents, DiscreteEvent);
}

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

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函數(shù)里面,我們發(fā)現(xiàn)了注冊事件registerTwoPhaseEvent,我們繼續(xù)去深究一下,究竟是怎么注冊的。

registerTwoPhaseEvent

export function registerTwoPhaseEvent(
  registrationName: string, // 注冊事件reactName
  dependencies: Array<DOMEventName>, // 依賴
): void {
  registerDirectEvent(registrationName, dependencies);
  registerDirectEvent(registrationName + 'Capture', dependencies);
}

registerDirectEvent

// 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,這里往里面添加事件名,就完成了事件注冊。還沒有完,上面說過了事件注冊,與事件綁定,但是用戶點擊的時候,應(yīng)該怎么去觸發(fā)呢?上面的代碼,在獲取了優(yōu)先級之后,每個事件會根據(jù)當(dāng)前優(yōu)先級生成一個listenerWrapper,這個listenerWrapper也就是對應(yīng)的事件觸發(fā)綁定的函數(shù)。dispatchDiscreteEvent、dispatchUserBlockingUpdatedispatchEvent三個函數(shù)都通過bind執(zhí)行,我們都知道bind綁定的函數(shù),會返回一個新函數(shù),并不會立即執(zhí)行。所以我們也必須看看他的入?yún)⑹鞘裁础?/p>

  • thisnull
  • argmentsdomEventName:事件名,eventSystemFlags:事件類型標(biāo)記,targetContainer:目標(biāo)容器。

dispatchEvent

因為不管是dispatchDiscreteEventdispatchUserBlockingUpdate最后都會去執(zhí)行dispatchEvent,所以我們可以看看他的實現(xiàn)。

// packages/react-dom/src/events/ReactDOMEventListener.js
export function dispatchEvent(
  domEventName: DOMEventName, // 事件名
  eventSystemFlags: EventSystemFlags, // 事件類型標(biāo)記
  targetContainer: EventTarget, // 目標(biāo)容器
  nativeEvent: AnyNativeEvent, // native事件
): void {
  ...
  // 如果被阻塞了,嘗試調(diào)度事件 并返回掛載的實例或者容器
  const blockedOn = attemptToDispatchEvent(
    domEventName,
    eventSystemFlags,
    targetContainer,
    nativeEvent,
  );
  if (blockedOn === null) {
    // We successfully dispatched this event.
    ...
    return;
  }
  ...
  // 調(diào)度事件,觸發(fā)事件
  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); 執(zhí)行dispatchEventsForPlugins
};
  } finally {
    ...
  }
}

dispatchEventsForPlugins

function dispatchEventsForPlugins(
  domEventName: DOMEventName,  eventSystemFlags: EventSystemFlags,  nativeEvent: AnyNativeEvent,  targetInst: null | Fiber,  targetContainer: EventTarget,
): void {
  const nativeEventTarget = getEventTarget(nativeEvent);
  const dispatchQueue: DispatchQueue = [];
  //創(chuàng)建合成事件,遍歷fiber鏈表,將會觸發(fā)的事件加入到dispatchQueue中
  extractEvents(
    dispatchQueue,
    domEventName,
    targetInst,
    nativeEvent,
    nativeEventTarget,
    eventSystemFlags,
    targetContainer,
  );
  //觸發(fā)時間隊列,執(zhí)行事件
  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;
  // 事件處理函數(shù)容器
  const listeners: Array<DispatchListener> = [];
  //節(jié)點實例
  let instance = target;
  // 遍歷fiber,獲取fiber上的事件對應(yīng)的事件處理函數(shù)
  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;
      // 根據(jù)捕獲階段,還是冒泡階段處理不同的函數(shù)邏輯
      if (inCapturePhase) {
        const captureListener = getListener(instance, registrationName);
        if (captureListener != null) {
          // 加入到listeners中
          // instance:當(dāng)前fiebr實例
          // currentTarget:當(dāng)前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),
          );
        }
      }
    }
    // 當(dāng)前fiber實例的父級
    instance = instance.return;
  }
  if (listeners.length !== 0) {
    // 把事件、事件處理函數(shù)全部推到dispatchQueue中
    dispatchQueue.push({event, listeners});
  }
}
// processDispatchQueue
export function processDispatchQueue(
  dispatchQueue: DispatchQueue, // 事件隊列
  eventSystemFlags: EventSystemFlags, // 事件類型標(biāo)記
): void {
  const inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;
  for (let i = 0; i < dispatchQueue.length; i++) {
    const {event, listeners} = dispatchQueue[i];
    // 執(zhí)行事件,完成觸發(fā)
    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的事件系統(tǒng)就解析完了,在這里上面的問題就很好解答了,React對事件名與事件處理函數(shù)對做了綁定,并在創(chuàng)建rootFiber的時候就做了事件注冊、事件綁定、事件調(diào)度。那么他們的執(zhí)行流程大致如下:

總結(jié)

這一章主要是介紹組件在mount、update、destroy階段的生命周期執(zhí)行順序與React事件系統(tǒng)的注冊,綁定,調(diào)度更新等

以上就是react源碼中的生命周期和事件系統(tǒng)實例解析的詳細內(nèi)容,更多關(guān)于react源碼生命周期事件系統(tǒng)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • React文件分段上傳實現(xiàn)方法詳解

    React文件分段上傳實現(xiàn)方法詳解

    這篇文章主要介紹了React文件分段上傳實現(xiàn)方法,將文件切成多個小的文件;將切片并行上傳;所有切片上傳完成后,服務(wù)器端進行切片合成;當(dāng)分片上傳失敗,可以在重新上傳時進行判斷,只上傳上次失敗的部分實現(xiàn)斷點續(xù)傳;當(dāng)切片合成為完整的文件,通知客戶端上傳成功
    2022-11-11
  • React class和function的區(qū)別小結(jié)

    React class和function的區(qū)別小結(jié)

    Class組件和Function組件是React中創(chuàng)建組件的兩種主要方式,本文主要介紹了React class和function的區(qū)別小結(jié),具有一定的參考價值,感興趣的可以了解一下
    2023-10-10
  • 一文帶你了解React中的并發(fā)機制

    一文帶你了解React中的并發(fā)機制

    React 18.2.0 引入了一系列并發(fā)機制的新特性,旨在幫助各位開發(fā)者更好地控制和優(yōu)化應(yīng)用程序的性能和用戶體驗,下面我們就來看看如何利用這些新特性構(gòu)建更高效、更響應(yīng)式的應(yīng)用程序吧
    2024-03-03
  • React中的Context應(yīng)用場景分析

    React中的Context應(yīng)用場景分析

    這篇文章主要介紹了React中的Context應(yīng)用場景分析,Context 提供了一種在組件之間共享數(shù)據(jù)的方式,而不必顯式地通過組件樹的逐層傳遞 props,通過實例代碼給大家介紹使用步驟,感興趣的朋友跟隨小編一起看看吧
    2021-06-06
  • 淺談react新舊生命周期鉤子

    淺談react新舊生命周期鉤子

    所謂的生命周期就是指某個事物從開始到結(jié)束的各個階段,本文主要介紹了淺談react新舊生命周期鉤子,具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • React Native項目中使用Lottie動畫的方法

    React Native項目中使用Lottie動畫的方法

    這篇文章主要介紹了React Native 實現(xiàn)Lottie動畫的相關(guān)知識,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-10-10
  • React diff算法的實現(xiàn)示例

    React diff算法的實現(xiàn)示例

    這篇文章主要介紹了React diff算法的實現(xiàn)示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • React Fiber源碼深入分析

    React Fiber源碼深入分析

    Fiber 可以理解為一個執(zhí)行單元,每次執(zhí)行完一個執(zhí)行單元,React Fiber就會檢查還剩多少時間,如果沒有時間則將控制權(quán)讓出去,然后由瀏覽器執(zhí)行渲染操作,這篇文章主要介紹了React Fiber架構(gòu)原理剖析,需要的朋友可以參考下
    2022-11-11
  • 使用webpack配置react-hot-loader熱加載局部更新

    使用webpack配置react-hot-loader熱加載局部更新

    這篇文章主要介紹了使用webpack配置react-hot-loader熱加載局部更新,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • 詳解React?的數(shù)據(jù)流和生命周期

    詳解React?的數(shù)據(jù)流和生命周期

    這篇文章主要介紹了React?的數(shù)據(jù)流和生命周期,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08

最新評論