이 장에서 논의하고 싶은 것은 예 와 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
- 책 "Re-learning ES6"에 언급 되어
ES6
있습니다 . 에 대해 말하자면 필수 기능은 이벤트 를 초기화 하고 바인딩하는 것입니다.또 다른 점은 선언 한 후에 호출해야 한다는 것 입니다 .우리는 일반적으로 그것을 수신하고 전달하는 데 사용합니다. 안쓰면 안되는 건 물론이고 에서 쓰려면 리셉션 을 이용 해야함 .constructor
constructor
ReactClassComponent
constructor
state
constructor
super
props
constructor
props
constructor
props
super
- 따라서 클래스 구성 요소
constructor
의 경우 수명 주기 후크로 간주할 수 있습니다.
getDerivedStateFromProps
render 메서드가 호출되기 전에 호출되며 초기 마운트 및 후속 업데이트에서 호출됩니다. 객체를 업데이트 상태로 반환해야 합니다. 아무것도 null
반환 하지 않으면 업데이트됩니다.
render
호출 this.props
되면 this.state
변경 사항을 확인하고 다음 유형 중 하나를 반환합니다.
- 반응 요소 . 일반적으로 JSX를 통해 생성됩니다. 예를 들어 React에 의해
<div />
DOM 노드<MyComponent />
로 렌더링되고 React 요소인지<div />
여부<MyComponent />
. - 배열 또는 단편 . render 메서드가 여러 요소를 반환하도록 합니다.
- 포털 . 자식 노드를 다른 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
이 2개의 Hook에 대해서는 학생들이 직접 공식 홈페이지로 이동할 수 있습니다.
물론 위의 내용은 ClassComponent
라이프 사이클의 실행 순서일 뿐이며, React 새 버전 componentDidMount
에서는 componentDidUpdate
, , , 가 삭제되고 , componentWillUnMount
로 대체되었습니다 . 그렇다면 정확히 누가 세 사람을 대체 했습니까? 이 문제는 이미 React 소스 코드 분석 시리즈(8) - 심층 후크의 원리에서 설명했으므로 여기서는 반복하지 않겠습니다.useEffect
useLayoutEffect
이제 첫 번째 질문에 답하기 위해 react는 함수의 본문이 무엇인지 어떻게 알 수 있습니까? 이 질문은 실제로 매우 좋은 질문입니다. babel
파싱 후 jsx
주의 만 기울 {事件名:函数名}
이면 각 이벤트가 등록되고 바인딩 된 다음 이벤트에 의해 트리거되어 바인딩 된 함수의 함수 본문이 실행됩니다. 이런 종류의 문제를 설명하려면 여전히 소스 코드의 특정 구현을 살펴봐야 합니다.
listenToAllSupportedEvents
rootFiber
React 소스코드 분석 시리즈(2)에서 언급 한 초기화 컴포넌트 생성 및 업데이트 과정 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, ) {// 创建具有优先级的事件监听函数,返回值为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,);
}
여기에서 우선 순위를 얻는 데 초점을 맞춥니다 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 => onClickconst 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
계속해서 등록 방법을 알아 보겠습니다.
TwoPhaseEvent 등록
export function registerTwoPhaseEvent( registrationName: string, // 注册事件reactNamedependencies: 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事件名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]);}
}
allNativeEvents
스토리지 이벤트 이름이라고 하는데 여기 Set
에 추가하면 事件名
끝 事件注册
입니다. 아직 안끝났어 위에서 이벤트 등록은 이벤트에 결속된다고 언급했는데 사용자가 클릭했을때 어떻게 촉발해야되나? 위의 코드에서 우선 순위를 얻은 후 각 이벤트는 해당 이벤트 트리거 바인딩 함수 listenerWrapper
인 현재 우선 순위에 따라 하나를 생성합니다. , , 세 가지 함수는 모두 bind를 통해 실행되는데, bind로 묶인 함수는 새로운 함수를 반환하고 즉시 실행되지 않는다는 것을 우리 모두 알고 있습니다. 그래서 우리는 또한 그의 항목이 무엇인지 확인해야합니다.listenerWrapper
dispatchDiscreteEvent
dispatchUserBlockingUpdate
dispatchEvent
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: 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();
}
그래서 이쯤에서 이벤트 React
시스템에 대한 분석은 끝났고, 위의 질문들은 여기서 쉽게 답을 얻을 수 있을 것 입니다 . 그런 다음 실행 프로세스는 대략 다음과 같습니다.React
创建rootFiber
事件注册
事件绑定
事件调度
요약하다
이 장에서는 주로 mount
, update
, 단계에서 구성 요소 destroy
의 수명 주기 실행 순서 와 React
이벤트 시스템의 등록, 바인딩, 예약 업데이트 등을 소개합니다.
마침내
HTML, CSS, JavaScript, HTTP, TCP 프로토콜, 브라우저, VUE, React, 데이터 구조 및 알고리즘, 총 201개의 인터뷰 질문을 포함하는 "프론트 엔드 제조업체 인터뷰 모음"을 구성하고 각 질문에 대한 답변 작성 질문 답변 및 분석.
도움이 필요한 친구들은 기사 끝에 있는 카드를 클릭하여 이 문서를 받고 무료로 공유할 수 있습니다.
문서의 일부는 다음을 보여줍니다.
기사의 길이가 제한되어 있으며 다음 내용은 하나씩 표시되지 않습니다.
도움이 필요한 친구는 아래 카드를 클릭하여 무료로 얻을 수 있습니다.