这一章我想跟大家探讨的是React
的生命周期
与事件系统
。
因为前面也讲到jsx
在v17
中的编译结果,除了标签名
,其他的挂在标签上的属性
(比如class
),事件
(比如click
事件),都是放在_jsxRuntime.jsx
函数的第二参数上。表现为key:value
的形式,这里我们就会产生几个问题。
react
是怎么知道函数体(事件处理函数)是什么的呢?react
又是在什么阶段去处理这些事件的呢?这里我们先卖个关子,我们先来看看一个完整的React
应用的完整的生命周期是怎么样的,我们都知道React
分为类组件
与函数组件
,两种组件的部分生命周期函数发生了一些变化
,在这里我会分别对两种组件的生命周期做讲解。
因为在_jsxRuntime.jsx
编译jsx
对象的时候,我们会去做处理defaultProps
和propType
静态类型检查。所以这也算是一个生命周期吧。Class
组件具有单独的constructor
,在mount
阶段会去执行这个构造函数,我曾经做了部分研究,这个constructor
是类组件独有的,还是class
独有的?后来发现这个constructor
是class
独有的,怎么理解这句话呢?
ES6
中新增了类的概念,一个类必须要有constructor
方法,如果在类中没有显示定义,则一个空的constructor
方法会被默认添加。对于ReactClassComponent
来讲需要constructor
的作用就是用来初始化state
和绑定事件,另外一点就是声明了constructor
,就必须要调用super
,我们一般用来接收props
传递。假如你不写constructor
,那就没法用props
了,当然了要在constructor
中使用props
,也必须用super
接收才行。constructor
也算是一个生命周期钩子。getDerivedStateFromProps
会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null
则不更新任何内容。
render
被调用时,它会检查 this.props
和 this.state
的变化并返回以下类型之一:
<div />
会被 React 渲染为 DOM 节点,<MyComponent />
会被 React 渲染为自定义组件,无论是 <div />
还是 <MyComponent />
均为 React 元素。null
。什么都不渲染。(主要用于支持返回 test && <Child />
的模式,其中 test 为布尔类型。)componentDidMount()
会在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。在这里适合去发送异步请求。
getDerivedStateFromProps
=> shouldComponentUpdate()
=> render()
=> getSnapshotBeforeUpdate()
=> componentDidUpdate()
shouldComponentUpdate
也被称作为性能优化的一种钩子,其作用在于比较两次更新的state
或props
是否发生变化,决定是否更新当前组件,比较的方式是浅比较
,以前讲过这里不再复述。getSnapshotBeforeUpdate
函数在最近一次渲染输出(提交到DOM
节点)之前调用。它使得组件能在发生更改之前从DOM
中捕获一些信息。此生命周期方法的任何返回值将作为参数传递给componentDidUpdate()
。componentDidUpdate()
会在更新后会被立即调用。首次渲染不会执行
此方法。componentWillUnmount()
会在组件卸载
及销毁
之前直接调用。在此方法中执行必要的清理操作,例如,清除timer
,取消网络请求
等等。
getDerivedStateFromError
=> componentDidCatch
关于这两个钩子,同学们可自行移步官网。
当然上面的只是ClassComponent
的生命周期执行顺序,而在新版本的React中已经删除掉了componentDidMount
、componentDidUpdate
、componentWillUnMount
,取而代之的是useEffect
、useLayoutEffect
。那究竟是谁代替了他们三个呢?这个问题我已经在React源码解析系列(八) -- 深入hooks的原理 中阐述过了,这里不再复述。
现在来回答第一个问题:react是怎么知道函数体是什么的呢? 这个问题其实问的非常好,babel
解析后的jsx
本身只会去关注{事件名:函数名}
,但是每一个事件都是需要被注册、绑定的,然后通过事件触发,来执行绑定函数的函数体。解释这种问题还是得要去看一下源码里面的具体实现。
我们在React源码解析系列(二) -- 初始化组件的创建更新流程中提到rootFiber
与FiberRoot
的创建,创建完毕之后我们就需要去创建事件,创建事件的入口函数为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
究竟干了些什么。
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
事件呢,比如原生事件
,他们的优先级是怎么确定的呢?不要急,我们看一看就知道了。
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
,我们继续去深究一下,究竟是怎么注册的。
export function registerTwoPhaseEvent(
registrationName: string, // 注册事件reactName
dependencies: Array<DOMEventName>, // 依赖
): void {
registerDirectEvent(registrationName, dependencies);
registerDirectEvent(registrationName + 'Capture', dependencies);
}
// 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
也就是对应的事件触发绑定的函数。dispatchDiscreteEvent
、dispatchUserBlockingUpdate
、dispatchEvent
三个函数都通过bind执行,我们都知道bind绑定的函数,会返回一个新函数,并不会立即执行。所以我们也必须看看他的入参是什么。
this
:null
argments
:domEventName
:事件名,eventSystemFlags
:事件类型标记,targetContainer
:目标容器。因为不管是dispatchDiscreteEvent
、dispatchUserBlockingUpdate
最后都会去执行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 {
...
}
}
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
的时候就做了事件注册
、事件绑定
、事件调度
。那么他们的执行流程大致如下:
这一章主要是介绍组件在mount
、update
、destroy
阶段的生命周期执行顺序与React
事件系统的注册,绑定,调度更新等
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。