Ce dont je veux discuter avec vous dans ce chapitre, c'est oui React
et non .生命周期
事件系统
Le résultat de la compilation de jsx
Parce que j'ai également mentionné jsx
les v17
résultats de la compilation dans ce qui précède, à l'exception 标签名
des autres balises 属性
(telles que class
) et 事件
(telles que les click
événements), elles sont toutes placées sur _jsxRuntime.jsx
le deuxième paramètre de la fonction. Sous forme de performance key:value
, nous aurons ici plusieurs problèmes.
react
Comment savez-vous ce qu'est le corps de la fonction (gestionnaire d'événements) ?react
A quel stade ces événements sont-ils gérés ?
Faisons d'abord un point ici. Examinons d'abord à quoi ressemble React
le cycle de vie complet d'une application complète. Nous savons tous qu'il React
est divisé en 类组件
et 函数组件
fait partie des fonctions de cycle de vie des deux composants. 发生了一些变化
Ici, j'analyserai le deux composants séparément Expliquer le cycle de vie des composants.
Cycle de vie des composants React
Ordre d'exécution lorsque les composants sont montés
Parce que lors de la _jsxRuntime.jsx
compilation d' jsx
objets, nous effectuerons un traitement defaultProps
et propType
une vérification de type statique. C'est donc aussi un cycle de vie. Class
Les composants sont séparés et ce constructeur sera exécuté constructor
par étapes. J'ai fait quelques recherches. Est-ce unique aux composants de classe, ou est-ce unique ? Plus tard, j'ai découvert que c'était unique, comment comprendre cette phrase ?mount
constructor
class
constructor
class
- Il est mentionné dans le livre "Re-learning ES6" :
ES6
Le concept de classe est ajouté dans , une classe doit avoir uneconstructor
méthode, s'il n'y a pas de définition explicite dans la classe, uneconstructor
méthode vide sera ajoutée par défaut. PourReactClassComponent
parlerconstructor
, la fonction requise est d'initialiserstate
et de lier des événements. L'autre point estconstructor
qu'elle doit être appelée après avoir été déclaréesuper
. Nous l'utilisons généralement pour recevoir etprops
passer. Si vous ne l'écrivezconstructor
pas, cela ne fonctionnera pasprops
Bien sûr, si vous voulezconstructor
l'utiliser dansprops
, vous devez utilisersuper
la réception. - Ainsi, pour les composants de classe, il
constructor
peut être considéré comme un crochet de cycle de vie.
getDerivedStateFromProps
Sera appelé avant l'appel de la méthode de rendu et sera appelé lors du montage initial et des mises à jour ultérieures. Il doit renvoyer un objet à l'état de mise à jour, s'il null
renvoie rien ne sera mis à jour.
render
Lorsqu'il est appelé, il this.props
vérifie this.state
les modifications et renvoie l'un des types suivants :
- Réagissez les éléments . Généralement créé via JSX. Par exemple,
<div />
il sera rendu en tant que nœud DOM<MyComponent />
par React, et il sera rendu en tant que composant personnalisé par React, qu'il soit<div />
ou<MyComponent />
soit un élément React. - tableau ou fragments . Permet à la méthode render de renvoyer plusieurs éléments.
- Portails . Il est possible de rendre les nœuds enfants dans différents sous-arbres DOM.
- Type chaîne ou numérique . Ils sont rendus sous forme de nœuds de texte dans le DOM.
- Booléen ou
null
. Rien n'est rendu. (Principalement utilisé pour prendre en charge les modèles quitest && <Child />
renvoient , où test est un booléen.)
componentDidMount()
Sera appelé immédiatement après le montage du composant (inséré dans l'arborescence DOM). L'initialisation qui dépend des nœuds DOM doit aller ici. Cela convient à l'envoi de requêtes asynchrones.
Ordre d'exécution lors de la mise à jour des composants
getDerivedStateFromProps
=> shouldComponentUpdate()
=> render()
=> getSnapshotBeforeUpdate()
=>componentDidUpdate()
- On
shouldComponentUpdate
l'appelle aussi un crochet pour l'optimisation des performances. Sa fonction est de comparer les deux mises à jourstate
ouprops
si elles ont changé, et de décider de mettre à jour ou non le composant actuel. La méthode de comparaison est浅比较
celle qui a été mentionnée précédemment et ne sera pas répétée ici. - Et la
getSnapshotBeforeUpdate
fonction est appelée avant la sortie rendue la plus récente (soumise auDOM
nœud). Il permet aux composants de capturer certaines informationsDOM
à partir d'eux . Toute valeur de retour de cette méthode de cycle de vie sera transmise en tant qu'argument àcomponentDidUpdate()
. componentDidUpdate()
sera appelé immédiatement après la mise à jour.不会执行
Cette méthode est d'abord rendue .
Ordre d'exécution lorsque les composants sont désinstallés
componentWillUnmount()
卸载
Sera appelé directement avant le composant 销毁
. Effectuez les opérations de nettoyage nécessaires dans cette méthode, telles que le nettoyage timer
, 取消网络请求
etc.
Ordre d'exécution des composants lorsqu'une erreur survient
getDerivedStateFromError
=> componentDidCatch
Concernant ces deux accroches, les étudiants peuvent se déplacer seuls sur le site officiel.
Bien sûr, ce qui précède n'est que ClassComponent
l'ordre d'exécution du cycle de vie, et dans la nouvelle version de React componentDidMount
, componentDidUpdate
, , , ont été supprimés componentWillUnMount
et remplacés par useEffect
, useLayoutEffect
. Alors, qui les a remplacés exactement tous les trois ? J'ai déjà expliqué ce problème dans la série d'analyses de code source React (8) - le principe des crochets en profondeur, donc je ne le répéterai pas ici.
Maintenant, pour répondre à la première question : comment réagir sait-il ce qu'est le corps de la fonction ? Cette question est en fait une très bonne question. babel
Après l'analyse, vous jsx
ne ferez qu'y prêter attention {事件名:函数名}
, mais chaque événement doit être enregistré et lié, puis déclenché par l'événement pour exécuter le corps de la fonction liée. Pour expliquer ce genre de problème, il nous reste à regarder l'implémentation spécifique dans le code source.
listenToAllSupportedEventslisteToAllSupportedEvents
Nous avons mentionné dans la série d'analyses de code source React (2) - le processus de création et de mise à jour du composant d'initialisation. Une rootFiber
fois FiberRoot
la création terminée, nous devons créer un événement. La fonction d'entrée pour créer un événement est listenToAllSupportedEvents
.
// 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);
Nous devons faire attention à ce qu'il allNativeEvents
y a ici, allNativeEvents
qui se reflète dans le code source sous la forme d'une Set
structure stockant les noms d'événements :
export const allNativeEvents: Set<DOMEventName> = new Set();
Voyons listenToNativeEvent
ce qui s'est passé ensuite.
listenToNativeEvent
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, ) {// 创建具有优先级的事件监听函数,返回值为functionlet 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,);
}
Ici, nous nous concentrons sur l'obtention de la priorité getEventPriorityForPluginSystem
ici. Avez-vous une questionReact
? Nous savons que les événements internes React
seront définitivement prioritaires, mais qu'en est-il des non React
-événements, par exemple 原生事件
, comment leurs priorités sont-elles déterminées ? Ne vous inquiétez pas, nous verrons quand nous jetterons un coup d'œil.
getEventPriorityForPluginSystem
export function getEventPriorityForPluginSystem( domEventName: DOMEventName, ): EventPriority {// 通过事件名获取优先级const priority = eventPriorities.get(domEventName);// ContinuousEvent为默认优先级 return priority === undefined ? ContinuousEvent : priority;
}
//eventPriorities
const eventPriorities = new Map();
eventPriorities
C'est une structure Map elle-même, et nous pouvons trouver les opérations effectuées à deux endroits 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添加优先级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 => onClickconst reactName = 'on' + capitalizedEvent;// 往eventPriorities添加优先级eventPriorities.set(topEvent, priority);topLevelEventsToReactNames.set(topEvent, reactName);// 注册捕获阶段,冒泡阶段的事件registerTwoPhaseEvent(reactName, [topEvent]);}
}
Cela signifie que le traitement prioritaire a été effectué dans ces deux fonctions, nous pouvons donc regarder où ces deux fonctions sont appelées. Nous avons constaté que dans la fonction registerSimpleEvents
, ces deux fonctions étaient eventPriorities
exécutées et ajoutées à la priorité.
// packages/react-dom/src/events/DOMEventProperties.js
export function registerSimpleEvents() {// 处理离散事件优先级registerSimplePluginEventsAndSetTheirPriorities(discreteEventPairsForSimpleEventPlugin,DiscreteEvent,);// 处理用户阻塞事件优先级registerSimplePluginEventsAndSetTheirPriorities(userBlockingPairsForSimpleEventPlugin,UserBlockingEvent,);// 处理默认事件优先级registerSimplePluginEventsAndSetTheirPriorities(continuousPairsForSimpleEventPlugin,ContinuousEvent,);// 处理其他事件优先级setEventPriorities(otherDiscreteEvents, DiscreteEvent);
}
Vous pouvez voir qu'il y en a beaucoup dans le code ci-dessus , le Plugin
code est le suivant :
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',
];
Dans la registerSimplePluginEventsAndSetTheirPriorities
fonction, nous avons trouvé l'événement d'enregistrement registerTwoPhaseEvent
, continuons pour savoir comment il est enregistré.
registerTwoPhaseEvent
export function registerTwoPhaseEvent( registrationName: string, // 注册事件reactNamedependencies: Array<DOMEventName>, // 依赖 ): void {registerDirectEvent(registrationName, dependencies);registerDirectEvent(registrationName + 'Capture', dependencies);
}
enregistrerDirectEvent
// Mapping from registration name to event name
export const registrationNameDependencies = {};
export function registerDirectEvent( registrationName: string, //react事件名onClickdependencies: 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]);}
}
On dit qu'il allNativeEvents
s'agit d'un nom d'événement de stockage Set
, ajoutez-le ici 事件名
et vous avez terminé 事件注册
. Ce n'est pas encore fini, il est mentionné ci-dessus que l'inscription à l'événement est liée à l'événement, mais lorsque l'utilisateur clique, comment doit-elle être déclenchée ? Dans le code ci-dessus, après avoir obtenu la priorité, chaque événement en générera un en fonction de la priorité actuelle listenerWrapper
, qui listenerWrapper
est la fonction de liaison de déclencheur d'événement correspondante. dispatchDiscreteEvent
, dispatchUserBlockingUpdate
et les dispatchEvent
trois fonctions sont toutes exécutées via bind. Nous savons tous que la fonction liée par bind renverra une nouvelle fonction et ne sera pas exécutée immédiatement. Nous devons donc également voir quelle est son entrée.
this
:null
argments
:domEventName
: nom de l'événement,eventSystemFlags
: balise de type d'événement,targetContainer
: conteneur cible.
dispatchEvent
Parce que quoi qu'il en soit dispatchDiscreteEvent
, dispatchUserBlockingUpdate
il sera exécuté à la fin dispatchEvent
, nous pouvons donc jeter un œil à son implémentation.
// 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: undefinedreturn batchedEventUpdatesImpl(fn, a, b); // batchedEventUpdatesImpl(fn, a, b) =>// Defaults// let batchedUpdatesImpl = function(fn, bookkeeping) {// return fn(bookkeeping); 执行dispatchEventsForPlugins
};} finally {...}
}
dispatchEventsForPlugins
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:当前domlisteners.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();
}
Donc, à ce stade, React
l'analyse du système d'événements est terminée, et les questions ci-dessus peuvent être facilement répondues ici. React
Le nom de l'événement et la paire de fonctions de traitement d'événement sont liés, et 创建rootFiber
quand c'est fait 事件注册
, 事件绑定
, 事件调度
. Ensuite, leur processus d'exécution est à peu près le suivant :
Résumer
Ce chapitre présente principalement la séquence d'exécution du cycle de vie des composants dans les phases mount
, update
, et et l'enregistrement, la liaison, la mise à jour de la planification, etc. du système d'événements.destroy
React
Enfin
Organisation d'un ensemble de "Collection d'interviews de fabricants frontaux", comprenant HTML, CSS, JavaScript, HTTP, protocole TCP, navigateur, VUE, React, structure de données et algorithme, un total de 201 questions d'entretien, et réponse à chacune question Répondez et analysez.
Amis dans le besoin, vous pouvez cliquer sur la carte en fin d'article pour recevoir ce document et le partager gratuitement
Une partie de la documentation montre :
La longueur de l'article est limitée, et les contenus suivants ne seront pas affichés un par un
Amis dans le besoin, vous pouvez cliquer sur la carte ci-dessous pour l'obtenir gratuitement