写点什么

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

作者:flyzz177
  • 2022-10-18
    浙江
  • 本文字数:11819 字

    阅读完需:约 1 分钟

这一章我想跟大家探讨的是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.props 和 this.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


// packages/react-dom/src/events/DOMPluginEventSystem.jsexport 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结构:


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


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

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); }}
//getEventListenerSetexport function getEventListenerSet(node: EventTarget): Set<string> { let elementListenerSet = (node: any)[internalEventHandlersKey];
if (elementListenerSet === undefined) { // 创建一个Set来存放事件名 elementListenerSet = (node: any)[internalEventHandlersKey] = new Set(); } return elementListenerSet;}
// getListenerSetKeyexport function getListenerSetKey( domEventName: DOMEventName, capture: boolean,): string { // capture捕获,bubble冒泡 return `${domEventName}__${capture ? 'capture' : 'bubble'}`;}
// addTrappedEventListenerfunction 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, ); } }}
// createEventListenerWrapperWithPriorityexport 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

export function getEventPriorityForPluginSystem(  domEventName: DOMEventName,): EventPriority {  // 通过事件名获取优先级  const priority = eventPriorities.get(domEventName);
// ContinuousEvent为默认优先级 return priority === undefined ? ContinuousEvent : priority;}
//eventPrioritiesconst eventPriorities = new Map();
复制代码


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


// packages/react-dom/src/events/DOMEventProperties.jsfunction setEventPriorities(  eventTypes: Array<DOMEventName>,  priority: EventPriority,): void {  for (let i = 0; i < eventTypes.length; i++) {    // 往eventPriorities添加优先级    eventPriorities.set(eventTypes[i], priority);  }}
//registerSimplePluginEventsAndSetTheirPrioritiesfunction 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里面添加优先级。


// packages/react-dom/src/events/DOMEventProperties.jsexport function registerSimpleEvents() {  // 处理离散事件优先级  registerSimplePluginEventsAndSetTheirPriorities(    discreteEventPairsForSimpleEventPlugin,    DiscreteEvent,  );  // 处理用户阻塞事件优先级  registerSimplePluginEventsAndSetTheirPriorities(    userBlockingPairsForSimpleEventPlugin,    UserBlockingEvent,  );  // 处理默认事件优先级  registerSimplePluginEventsAndSetTheirPriorities(    continuousPairsForSimpleEventPlugin,    ContinuousEvent,  );  // 处理其他事件优先级  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函数里面,我们发现了注册事件registerTwoPhaseEvent,我们继续去深究一下,究竟是怎么注册的。

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 nameexport 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,所以我们可以看看他的实现。


// packages/react-dom/src/events/ReactDOMEventListener.jsexport 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, );}
// dispatchEventForPluginEventSystemexport function dispatchEventForPluginEventSystem( domEventName: DOMEventName, eventSystemFlags: EventSystemFlags, nativeEvent: AnyNativeEvent, targetInst: null | Fiber, targetContainer: EventTarget,): void { ... //批量更新事件 batchedEventUpdates(() => dispatchEventsForPlugins( domEventName, eventSystemFlags, nativeEvent, ancestorInst, targetContainer, ), );}
// batchedEventUpdatesexport 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

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);}
//extractEventsfunction 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);}
//accumulateEnterLeaveTwoPhaseListenersexport 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, ); }}
// accumulateEnterLeaveListenersForEventfunction 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}); }}// processDispatchQueueexport 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事件系统的注册,绑定,调度更新等


用户头像

flyzz177

关注

还未添加个人签名 2021-12-07 加入

还未添加个人简介

评论

发布
暂无评论
react源码中的生命周期和事件系统_React_flyzz177_InfoQ写作社区