react源码中的生命周期和事件系统实例解析
flyzz177 人气:0引言
这一章我想跟大家探讨的是React
的生命周期与事件系统。
jsx的编译结果
因为前面也讲到jsx
在v17
中的编译结果,除了标签名,其他的挂在标签上的属性(比如class
),事件
(比如click
事件),都是放在_jsxRuntime.jsx
函数的第二参数上。表现为key:value
的形式,这里我们就会产生几个问题。
react
是怎么知道函数体(事件处理函数)是什么的呢?react
又是在什么阶段去处理这些事件的呢?
这里我们先卖个关子,我们先来看看一个完整的React
应用的完整的生命周期是怎么样的,我们都知道React
分为类组件
与函数组件
,两种组件的部分生命周期函数发生了一些变化
,在这里我会分别对两种组件的生命周期做讲解。
React组件的生命周期
组件挂载的时候的执行顺序
因为在_jsxRuntime.jsx
编译jsx
对象的时候,我们会去做处理defaultProps
和propType
静态类型检查。所以这也算是一个生命周期吧。Class
组件具有单独的constructor
,在mount
阶段会去执行这个构造函数,我曾经做了部分研究,这个constructor
是类组件独有的,还是class
独有的?后来发现这个constructor
是class
独有的,怎么理解这句话呢?
- 在《重学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
也被称作为性能优化的一种钩子,其作用在于比较两次更新的state
或props
是否发生变化,决定是否更新当前组件,比较的方式是浅比较
,以前讲过这里不再复述。 - 而
getSnapshotBeforeUpdate
函数在最近一次渲染输出(提交到DOM
节点)之前调用。它使得组件能在发生更改之前从DOM
中捕获一些信息。此生命周期方法的任何返回值将作为参数传递给componentDidUpdate()
。 componentDidUpdate()
会在更新后会被立即调用。首次渲染不会执行
此方法。
组件卸载的时候执行顺序
componentWillUnmount()
会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除timer
,取消网络请求等等。
组件在发生错误的时候执行顺序
getDerivedStateFromError
=> componentDidCatch
关于这两个钩子,同学们可自行移步官网。
当然上面的只是ClassComponent
的生命周期执行顺序,而在新版本的React中已经删除掉了componentDidMount
、componentDidUpdate
、componentWillUnMount
,取而代之的是useEffect
、useLayoutEffect
。那究竟是谁代替了他们三个呢?这个问题我已经在React源码解析系列(八) -- 深入hooks的原理 中阐述过了,这里不再复述。
现在来回答第一个问题:react是怎么知道函数体是什么的呢? 这个问题其实问的非常好,babel
解析后的jsx
本身只会去关注{事件名:函数名},但是每一个事件都是需要被注册、绑定的,然后通过事件触发,来执行绑定函数的函数体。解释这种问题还是得要去看一下源码里面的具体实现。
listenToAllSupportedEvents
我们在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
究竟干了些什么。
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
也就是对应的事件触发绑定的函数。dispatchDiscreteEvent
、dispatchUserBlockingUpdate
、dispatchEvent
三个函数都通过bind执行,我们都知道bind绑定的函数,会返回一个新函数,并不会立即执行。所以我们也必须看看他的入参是什么。
this
:null
argments
:domEventName
:事件名,eventSystemFlags
:事件类型标记,targetContainer
:目标容器。
dispatchEvent
因为不管是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 { ... } }
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
的时候就做了事件注册
、事件绑定
、事件调度
。那么他们的执行流程大致如下:
总结
这一章主要是介绍组件在mount
、update
、destroy
阶段的生命周期执行顺序与React
事件系统的注册,绑定,调度更新等
加载全部内容