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

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

// 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结构:

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);
 }
}
//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

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()的操作。

// 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里面添加优先级。

// packages/react-dom/src/events/DOMEventProperties.js
export 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 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,所以我们可以看看他的实现。

// 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

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事件系统的注册,绑定,调度更新等

作者:zhang_a555原文地址:https://segmentfault.com/a/1190000043388577

%s 个评论

要回复文章请先登录注册