react源碼中的生命周期和事件系統(tǒng)實(shí)例解析
引言
這一章我想跟大家探討的是React的生命周期與事件系統(tǒng)。
jsx的編譯結(jié)果

因?yàn)榍懊嬉仓v到jsx在v17中的編譯結(jié)果,除了標(biāo)簽名,其他的掛在標(biāo)簽上的屬性(比如class),事件(比如click事件),都是放在_jsxRuntime.jsx函數(shù)的第二參數(shù)上。表現(xiàn)為key:value的形式,這里我們就會(huì)產(chǎn)生幾個(gè)問(wèn)題。
react是怎么知道函數(shù)體(事件處理函數(shù))是什么的呢?react又是在什么階段去處理這些事件的呢?
這里我們先賣個(gè)關(guān)子,我們先來(lái)看看一個(gè)完整的React應(yīng)用的完整的生命周期是怎么樣的,我們都知道React分為類組件與函數(shù)組件,兩種組件的部分生命周期函數(shù)發(fā)生了一些變化,在這里我會(huì)分別對(duì)兩種組件的生命周期做講解。
React組件的生命周期
組件掛載的時(shí)候的執(zhí)行順序
因?yàn)樵?code>_jsxRuntime.jsx編譯jsx對(duì)象的時(shí)候,我們會(huì)去做處理defaultProps和propType靜態(tài)類型檢查。所以這也算是一個(gè)生命周期吧。Class組件具有單獨(dú)的constructor,在mount階段會(huì)去執(zhí)行這個(gè)構(gòu)造函數(shù),我曾經(jīng)做了部分研究,這個(gè)constructor是類組件獨(dú)有的,還是class獨(dú)有的?后來(lái)發(fā)現(xiàn)這個(gè)constructor是class獨(dú)有的,怎么理解這句話呢?
- 在《重學(xué)ES6》這本書(shū)中提到:
ES6中新增了類的概念,一個(gè)類必須要有constructor方法,如果在類中沒(méi)有顯示定義,則一個(gè)空的constructor方法會(huì)被默認(rèn)添加。對(duì)于ReactClassComponent來(lái)講需要constructor的作用就是用來(lái)初始化state和綁定事件,另外一點(diǎn)就是聲明了constructor,就必須要調(diào)用super,我們一般用來(lái)接收props傳遞。假如你不寫(xiě)constructor,那就沒(méi)法用props了,當(dāng)然了要在constructor中使用props,也必須用super接收才行。 - 所以對(duì)于類組件來(lái)講的話,
constructor也算是一個(gè)生命周期鉤子。
getDerivedStateFromProps 會(huì)在調(diào)用 render 方法之前調(diào)用,并且在初始掛載及后續(xù)更新時(shí)都會(huì)被調(diào)用。它應(yīng)返回一個(gè)對(duì)象來(lái)更新 state,如果返回 null 則不更新任何內(nèi)容。
render被調(diào)用時(shí),它會(huì)檢查 this.props 和 this.state 的變化并返回以下類型之一:
- React 元素。通常通過(guò) JSX 創(chuàng)建。例如,
<div />會(huì)被 React 渲染為 DOM 節(jié)點(diǎn),<MyComponent />會(huì)被 React 渲染為自定義組件,無(wú)論是<div />還是<MyComponent />均為 React 元素。 - 數(shù)組或 fragments。 使得 render 方法可以返回多個(gè)元素。
- Portals??梢凿秩咀庸?jié)點(diǎn)到不同的 DOM 子樹(shù)中。
- 字符串或數(shù)值類型。它們?cè)?DOM 中會(huì)被渲染為文本節(jié)點(diǎn)。
- 布爾類型或
null。什么都不渲染。(主要用于支持返回test && <Child />的模式,其中 test 為布爾類型。)
componentDidMount() 會(huì)在組件掛載后(插入 DOM 樹(shù)中)立即調(diào)用。依賴于 DOM 節(jié)點(diǎn)的初始化應(yīng)該放在這里。在這里適合去發(fā)送異步請(qǐng)求。
組件更新的時(shí)候的執(zhí)行順序
getDerivedStateFromProps => shouldComponentUpdate() => render() => getSnapshotBeforeUpdate() => componentDidUpdate()
- 其中
shouldComponentUpdate也被稱作為性能優(yōu)化的一種鉤子,其作用在于比較兩次更新的state或props是否發(fā)生變化,決定是否更新當(dāng)前組件,比較的方式是淺比較,以前講過(guò)這里不再?gòu)?fù)述。 - 而
getSnapshotBeforeUpdate函數(shù)在最近一次渲染輸出(提交到DOM節(jié)點(diǎn))之前調(diào)用。它使得組件能在發(fā)生更改之前從DOM中捕獲一些信息。此生命周期方法的任何返回值將作為參數(shù)傳遞給componentDidUpdate()。 componentDidUpdate()會(huì)在更新后會(huì)被立即調(diào)用。首次渲染不會(huì)執(zhí)行此方法。
組件卸載的時(shí)候執(zhí)行順序
componentWillUnmount() 會(huì)在組件卸載及銷毀之前直接調(diào)用。在此方法中執(zhí)行必要的清理操作,例如,清除timer,取消網(wǎng)絡(luò)請(qǐng)求等等。
組件在發(fā)生錯(cuò)誤的時(shí)候執(zhí)行順序
getDerivedStateFromError => componentDidCatch 關(guān)于這兩個(gè)鉤子,同學(xué)們可自行移步官網(wǎng)。
當(dāng)然上面的只是ClassComponent的生命周期執(zhí)行順序,而在新版本的React中已經(jīng)刪除掉了componentDidMount、componentDidUpdate、componentWillUnMount,取而代之的是useEffect、useLayoutEffect。那究竟是誰(shuí)代替了他們?nèi)齻€(gè)呢?這個(gè)問(wèn)題我已經(jīng)在React源碼解析系列(八) -- 深入hooks的原理 中闡述過(guò)了,這里不再?gòu)?fù)述。
現(xiàn)在來(lái)回答第一個(gè)問(wèn)題:react是怎么知道函數(shù)體是什么的呢? 這個(gè)問(wèn)題其實(shí)問(wèn)的非常好,babel解析后的jsx本身只會(huì)去關(guān)注{事件名:函數(shù)名},但是每一個(gè)事件都是需要被注冊(cè)、綁定的,然后通過(guò)事件觸發(fā),來(lái)執(zhí)行綁定函數(shù)的函數(shù)體。解釋這種問(wèn)題還是得要去看一下源碼里面的具體實(shí)現(xiàn)。
listenToAllSupportedEvents
我們?cè)赗eact源碼解析系列(二) -- 初始化組件的創(chuàng)建更新流程中提到rootFiber與FiberRoot的創(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默認(rèn)值為false
// listeningMarker就是一個(gè)隨機(jī)數(shù)+字符串,作為唯一值
if (rootContainerElement[listeningMarker]) {
...
return;
}
rootContainerElement[listeningMarker] = true;
// 遍歷allNativeEvents的所有事件
allNativeEvents.forEach(domEventName => {
// 如果不是委托事件,沒(méi)有冒泡階段
// nonDelegatedEvents全部媒體事件,
if (!nonDelegatedEvents.has(domEventName)) {
listenToNativeEvent(
domEventName,
false,
((rootContainerElement: any): Element),
null,
);
}
// 有冒泡階段
listenToNativeEvent(
domEventName,
true,
((rootContainerElement: any): Element),
null,
);
});
}
}
//listeningMarker
// 唯一標(biāo)識(shí)
const listeningMarker =
'_reactListening' +
Math.random()
.toString(36)
.slice(2);
我們?cè)谶@里必須要關(guān)注一下allNativeEvents是什么東西,allNativeEvents在源碼里體現(xiàn)為一個(gè)存儲(chǔ)著事件名的Set結(jié)構(gòu):
export const allNativeEvents: Set<DOMEventName> = new Set();
接下來(lái)看看listenToNativeEvent究竟干了些什么。
listenToNativeEvent
export function listenToNativeEvent(
domEventName: DOMEventName,// 事件名
isCapturePhaseListener: boolean, // 根據(jù)上個(gè)函數(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) // 非冒泡事件
) {
...
//滾動(dòng)事件不冒泡
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,
);
// 把沒(méi)有打過(guò)的IS_CAPTURE_PHASE的符合條件的事件,打上標(biāo)簽
if (!listenerSet.has(listenerSetKey)) {
if (isCapturePhaseListener) {
// 打上捕獲的標(biāo)簽
eventSystemFlags |= IS_CAPTURE_PHASE;
}
// 往節(jié)點(diǎn)上添加事件綁定
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)建一個(gè)Set來(lái)存放事件名
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)識(shí)
isCapturePhaseListener: boolean, // 事件委托
isDeferredListenerForLegacyFBSupport?: boolean,
) {
// 創(chuàng)建具有優(yōu)先級(jí)的事件監(jiān)聽(tīng)函數(shù),返回值為function
let listener = createEventListenerWrapperWithPriority(
targetContainer,
domEventName,
eventSystemFlags,
);
...
targetContainer =
enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport
? (targetContainer: any).ownerDocument
: targetContainer;
let unsubscribeListener;
...
// 區(qū)分捕獲、冒泡 通過(guò)node.addEventListener綁定事件到節(jié)點(diǎn)上
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)識(shí)
): Function {
// 獲取事件Map里面已經(jīng)標(biāo)記好的優(yōu)先級(jí)
const eventPriority = getEventPriorityForPluginSystem(domEventName);
let listenerWrapper;
// 根據(jù)優(yōu)先級(jí)不同綁定不同的執(zhí)行函數(shù)
switch (eventPriority) {
//離散事件
case DiscreteEvent:
listenerWrapper = dispatchDiscreteEvent;
break;
// 用戶交互阻塞渲染的事件
case UserBlockingEvent:
listenerWrapper = dispatchUserBlockingUpdate;
break;
// 其他事件
case ContinuousEvent:
// 默認(rèn)事件
default:
listenerWrapper = dispatchEvent;
break;
}
return listenerWrapper.bind(
null,
domEventName,
eventSystemFlags,
targetContainer,
);
}
在這里我們關(guān)注一下獲取優(yōu)先級(jí)getEventPriorityForPluginSystem這里,你會(huì)不會(huì)產(chǎn)生一個(gè)疑問(wèn),React內(nèi)部事件我們知道React本身一定會(huì)給優(yōu)先級(jí)的,但是非React事件呢,比如原生事件,他們的優(yōu)先級(jí)是怎么確定的呢?不要急,我們看一看就知道了。
getEventPriorityForPluginSystem
export function getEventPriorityForPluginSystem(
domEventName: DOMEventName,
): EventPriority {
// 通過(guò)事件名獲取優(yōu)先級(jí)
const priority = eventPriorities.get(domEventName);
// ContinuousEvent為默認(rèn)優(yōu)先級(jí)
return priority === undefined ? ContinuousEvent : priority;
}
//eventPriorities
const eventPriorities = new Map();
eventPriorities本身是一個(gè)Map結(jié)構(gòu),我們可以發(fā)現(xiàn)兩個(gè)地方進(jì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)先級(jí)
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)先級(jí)
eventPriorities.set(topEvent, priority);
topLevelEventsToReactNames.set(topEvent, reactName);
// 注冊(cè)捕獲階段,冒泡階段的事件
registerTwoPhaseEvent(reactName, [topEvent]);
}
}
這就說(shuō)明,在這兩個(gè)函數(shù)里面已經(jīng)做好了優(yōu)先級(jí)的處理,那我們可以去看一下在哪里調(diào)用的這兩個(gè)函數(shù),我們發(fā)現(xiàn)在函數(shù)registerSimpleEvents中,執(zhí)行了這兩個(gè)函數(shù),往eventPriorities里面添加優(yōu)先級(jí)。
// packages/react-dom/src/events/DOMEventProperties.js
export function registerSimpleEvents() {
// 處理離散事件優(yōu)先級(jí)
registerSimplePluginEventsAndSetTheirPriorities(
discreteEventPairsForSimpleEventPlugin,
DiscreteEvent,
);
// 處理用戶阻塞事件優(yōu)先級(jí)
registerSimplePluginEventsAndSetTheirPriorities(
userBlockingPairsForSimpleEventPlugin,
UserBlockingEvent,
);
// 處理默認(rèn)事件優(yōu)先級(jí)
registerSimplePluginEventsAndSetTheirPriorities(
continuousPairsForSimpleEventPlugin,
ContinuousEvent,
);
// 處理其他事件優(yōu)先級(jí)
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)了注冊(cè)事件registerTwoPhaseEvent,我們繼續(xù)去深究一下,究竟是怎么注冊(cè)的。
registerTwoPhaseEvent
export function registerTwoPhaseEvent(
registrationName: string, // 注冊(cè)事件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對(duì)象
registrationNameDependencies[registrationName] = dependencies;
if (__DEV__) {
...
}
// 遍歷依賴,把每一項(xiàng)加入到allNativeEvents中去
for (let i = 0; i < dependencies.length; i++) {
allNativeEvents.add(dependencies[i]);
}
}
前面說(shuō)allNativeEvents是一個(gè)存儲(chǔ)事件名的Set,這里往里面添加事件名,就完成了事件注冊(cè)。還沒(méi)有完,上面說(shuō)過(guò)了事件注冊(cè),與事件綁定,但是用戶點(diǎn)擊的時(shí)候,應(yīng)該怎么去觸發(fā)呢?上面的代碼,在獲取了優(yōu)先級(jí)之后,每個(gè)事件會(huì)根據(jù)當(dāng)前優(yōu)先級(jí)生成一個(gè)listenerWrapper,這個(gè)listenerWrapper也就是對(duì)應(yīng)的事件觸發(fā)綁定的函數(shù)。dispatchDiscreteEvent、dispatchUserBlockingUpdate、dispatchEvent三個(gè)函數(shù)都通過(guò)bind執(zhí)行,我們都知道bind綁定的函數(shù),會(huì)返回一個(gè)新函數(shù),并不會(huì)立即執(zhí)行。所以我們也必須看看他的入?yún)⑹鞘裁础?/p>
this:nullargments:domEventName:事件名,eventSystemFlags:事件類型標(biāo)記,targetContainer:目標(biāo)容器。
dispatchEvent
因?yàn)椴还苁?code>dispatchDiscreteEvent、dispatchUserBlockingUpdate最后都會(huì)去執(zhí)行dispatchEvent,所以我們可以看看他的實(shí)現(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)度事件 并返回掛載的實(shí)例或者容器
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鏈表,將會(huì)觸發(fā)的事件加入到dispatchQueue中
extractEvents(
dispatchQueue,
domEventName,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer,
);
//觸發(fā)時(shí)間隊(duì)列,執(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 {
// 獲取注冊(cè)的事件名
const registrationName = event._reactName;
// 事件處理函數(shù)容器
const listeners: Array<DispatchListener> = [];
//節(jié)點(diǎn)實(shí)例
let instance = target;
// 遍歷fiber,獲取fiber上的事件對(duì)應(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實(shí)例
// 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實(shí)例的父級(jí)
instance = instance.return;
}
if (listeners.length !== 0) {
// 把事件、事件處理函數(shù)全部推到dispatchQueue中
dispatchQueue.push({event, listeners});
}
}
// processDispatchQueue
export function processDispatchQueue(
dispatchQueue: DispatchQueue, // 事件隊(duì)列
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)就解析完了,在這里上面的問(wèn)題就很好解答了,React對(duì)事件名與事件處理函數(shù)對(duì)做了綁定,并在創(chuàng)建rootFiber的時(shí)候就做了事件注冊(cè)、事件綁定、事件調(diào)度。那么他們的執(zhí)行流程大致如下:

總結(jié)
這一章主要是介紹組件在mount、update、destroy階段的生命周期執(zhí)行順序與React事件系統(tǒng)的注冊(cè),綁定,調(diào)度更新等
以上就是react源碼中的生命周期和事件系統(tǒng)實(shí)例解析的詳細(xì)內(nèi)容,更多關(guān)于react源碼生命周期事件系統(tǒng)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
React class和function的區(qū)別小結(jié)
Class組件和Function組件是React中創(chuàng)建組件的兩種主要方式,本文主要介紹了React class和function的區(qū)別小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10
React中的Context應(yīng)用場(chǎng)景分析
這篇文章主要介紹了React中的Context應(yīng)用場(chǎng)景分析,Context 提供了一種在組件之間共享數(shù)據(jù)的方式,而不必顯式地通過(guò)組件樹(shù)的逐層傳遞 props,通過(guò)實(shí)例代碼給大家介紹使用步驟,感興趣的朋友跟隨小編一起看看吧2021-06-06
React Native項(xiàng)目中使用Lottie動(dòng)畫(huà)的方法
這篇文章主要介紹了React Native 實(shí)現(xiàn)Lottie動(dòng)畫(huà)的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10
使用webpack配置react-hot-loader熱加載局部更新
這篇文章主要介紹了使用webpack配置react-hot-loader熱加載局部更新,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01

