preface
📢 article first blog: Hiro’s blog
💕 warm tips: below is the React synthesis event source to read, the full text is a little long, but! If you really want to know what’s going on behind the scenes, be patient!
Recently, I was working on a feature, and I accidentally stepped in the pit of the React synthesis event. Driven by curiosity, I went to read the explanation of the React synthesis event on the React website.
What the hell is SyntheticEvent? Why is there an event pool?
I just have a simple requirement function, why can pull out all this shit?
Let’s take a quick look at what my required function is.
The spark
You need to make a popover open/close function, which opens when you click the button, and closes when you click outside the popover area while it is open.
Register a click event in the Button, register a click event in the Document. body, and then block bubbles in the popover Container.
class FuckEvent extends React.PureComponent {
state = {
showBox: false
}
componentDidMount() {
document.body.addEventListener('click'.this.handleClickBody, false)
}
componentWillUnmount() {
document.body.removeEventListener('click'.this.handleClickBody, false)
}
handleClickBody = (a)= > {
this.setState({
showBox: false
})
}
handleClickButton = (a)= > {
this.setState({
showBox: true
})
}
render() {
return (
<div>
<button onClick={this.handleClickButton}>Click on me to show the popover</button>
{this.state.showBox && (
<div onClick={e= >Propagation()}> < span style = "color: RGB (0, 0, 0)</div>
)}
</div>)}}Copy the code
Very simple, very happy to click on the popover area….
So… I didn’t… Click on the popover area, the popover is also closed… what the f**k ?????? Is bubbling useless?
With this question, I went down the road of no return…
Event delegation
We all know, what is the event entrust, (do not know of go out to turn left 👈) in front of slash-and-burn period, the event entrust but father
Event delegates resolve large lists of data without binding event listeners to each list item. Elements can also be mounted dynamically without additional event listening.
Look, event delegate so cow 13, you think React won’t use it? Neil: Well, it’s not only used, it’s used very well
How to say, React takes over the browser event optimization strategy and implements its own event mechanism, and it’s very sweet, just like your boyfriend, it eliminates all the differences in the browser for you
React implements a composite event layer that eliminates compatibility issues between IE and W3C standards.
📌 Then the question arises, what are synthetic events versus native events????
-
Native events: Events that are bound to addEventListener within the componentDidMount lifecycle
-
Composite event: an event bound by JSX, such as onClick={() => this.handle()}
Remember the example above? We bind an event to the DOM element of the popover to prevent bubbling
{
this.state.showBox && <div onClick={e= >Propagation()}> < span style = "color: RGB (0, 0, 0)</div>
}
Copy the code
The body is then bound by click in the componentDidMount lifecycle
componentDidMount() {
document.body.addEventListener('click'.this.handleClickBody, false)
}
componentWillUnmount() {
document.body.removeEventListener('click'.this.handleClickBody, false)}Copy the code
Let’s take a look, because synthetic events are triggered by a browser-based event mechanism, bubbling up to the top element and then being processed by dispatchEvent
Review the browser event mechanism
At the top of Document is Window, and here is an image from the book of Advanced Programming in JavaScript
The execution of browser events goes through three phases: capture phase – target element phase – bubble phase.
🙋 Question: If synthetic events are blocked, will native events be executed? The answer is yes!
📢 Answer: Because the native event is executed before the synthesized event (personal understanding: the registered native event has been executed, while the synthesized event is in the target phase, it prevents bubbling only prevents the synthesized event from bubbling, but the native event has been executed in the capture phase)
Composite event characteristics
React itself implements such a set of event mechanism, which is improved on the basis of DOM event system, reduces memory consumption, and solves the incompatibility problem of Internet Explorer and other browsers to the greatest extent
What are its characteristics?
-
Events registered on React will eventually be bound to the Document DOM instead of the React component’s DOM(which reduces memory overhead because all events are bound to document and no other nodes are bound to events).
-
React implements an event bubble mechanism, so that’s why event.stopPropagation() is not working.
-
React queues back from the triggered component to the parent component and calls their JSX defined callback
-
React has its own SyntheticEvent, which is not native. You can check out the official website for this
-
React manages the creation and destruction of synthetic event objects in the form of object pools, reducing garbage generation and memory allocation for new objects, improving performance
React event system
After reading this, we should have a simple understanding of React synthesis events. Let’s take a look at the source code
👉 source ReactBrowserEventEmitter
We’re ReactBrowserEventEmitter. Js file you can see, the React synthesis system frame
/** * React and event system overview: * * +------------+ . * | DOM | . * +------------+ . * | . * v . * +------------+ . * | ReactEvent | . * | Listener | . * +------------+ . +-----------+ * | . +--------+|SimpleEvent| * | . | |Plugin | * +-----|------+ . v +-----------+ * | | | . +--------------+ +------------+ * | +-----------.--->|EventPluginHub| | Event | * | | . | | +-----------+ | Propagators| * | ReactEvent | . | | |TapEvent | |------------| * | Emitter | . | |<---+|Plugin | |other plugin| * | | . | | +-----------+ | utilities | * | +-----------.--->| | +------------+ * | | | . +--------------+ * +-----|------+ . ^ +-----------+ * | . | |Enter/Leave| * + . +-------+|Plugin | * +-------------+ . +-----------+ * | application | . * | -- -- -- -- -- -- -- -- -- -- -- -- - |. * | |. * | |. * + -- -- -- -- -- -- -- -- -- -- -- -- -- +. *. * /Copy the code
Source code inside a string of English explanation, I help you Google translation, simply say:
-
Top-level Delegation is used to capture the original browser events, and is primarily the responsibility of the ReactEventListener, which is injected to support plug-in event sources, and this happens on the main thread.
-
React normalizes and deduplicates events to address browser quirks. This can be done in the worker thread.
-
These local events (with an associated top-level type to capture it) are forwarded to EventPluginHub, which asks the plug-in if it wants to extract any composite events.
-
EventPluginHub then processes each event by annotating it by adding “Dispatches” (a sequence of listeners and ids concerned with that event).
-
EventPluginHub then dispatches the event.
❗ suggest going directly to the English notes, the translation may not be very standard.
Look at the frame diagram on the side, we need to know what these things are first, just look at the name, you can also know:
- ReactEventListener: Is responsible for registering events.
- ReactEventEmitter: Responsible for event distribution.
- EventPluginHub: Stores and distributes events.
- Plugin: Construct different composite events based on different event types.
👇 Let’s see how it works step by step
Event registration
Registering an event thief in React is easy, like this:
class TaskEvent extends Reac.PureComponent {
render() {
return (
<div
onClick={()= >{console.log(' I am registering events ')}} > Hehehehe</div>)}}Copy the code
How is it registered in the React event system?
enqueuePutListener()
The _updateDOMProperties() method is called by the component when it creates the mountComponent and when it updates updateComponent
📢 Pages Not Found on myMypages, which is Not Found on myMypages. It’s Not Found on myMypages, which is Not Found on myMypages. Here is the source code explanation of the registration event in the article I read the information
mountComponent: function(transaction, hostParent, hostContainerInfo, context) {
// ...
var props = this._currentElement.props;
// ...
this._updateDOMProperties(null, props, transaction);
// ...
}
Copy the code
_updateDOMProperties: function (lastProps, nextProps, transaction) {
// ...
for (propKey in nextProps) {
var nextProp = nextProps[propKey];
var lastProp = propKey === STYLE ? this._previousStyleCopy : lastProps ! =null ? lastProps[propKey] : undefined;
if(! nextProps.hasOwnProperty(propKey) || nextProp === lastProp || nextProp ==null && lastProp == null) {
continue;
}
if (propKey === STYLE) {
// ...
} else if (registrationNameModules.hasOwnProperty(propKey)) {
// Processes props if it is a property declared directly by the object, instead of inheriting it from the prototype chain
// For mountComponent, lastProp is null. UpdateComponent neither is null. UnmountComponent nextProp is null
if (nextProp) {
// In the mountComponent and updateComponent, enqueuePutListener registers events
enqueuePutListener(this, propKey, nextProp, transaction);
} else if (lastProp) {
// In unmountComponent, delete the registered listener to prevent memory leaks
deleteListener(this, propKey); }}}}Copy the code
The code above clearly tells you to register an event with the enqueuePutListener() method, so let’s see what that means
function enqueuePutListener(inst, registrationName, listener, transaction) {
if (transaction instanceof ReactServerRenderingTransaction) {
return
}
var containerInfo = inst._hostContainerInfo
var isDocumentFragment =
containerInfo._node && containerInfo._node.nodeType === DOC_FRAGMENT_TYPE
/ / find the document
var doc = isDocumentFragment
? containerInfo._node
: containerInfo._ownerDocument
// Register the event to the document
listenTo(registrationName, doc)
// Store the event and put it in the transaction queue
transaction.getReactMountReady().enqueue(putListener, {
inst: inst,
registrationName: registrationName,
listener: listener
})
}
Copy the code
💢 See, this enqueuePutListener() only does two things:
-
ListenTo registers events on the Document by calling a listenTo.
-
Use putListener to store events (i.e., store all events in the React component into an object and cache it, so that when the event is triggered, it can find the corresponding method to execute it).
listenTo()
Don’t post code, but! Look directly at the source code is really simple ah, 👉 listenTo source
📢 Note that the React version is currently the Github Master branch code
Let’s look at the code
export function listenTo(registrationName: string, mountAt: Document | Element | Node) :void {
const listeningSet = getListeningSetForElement(mountAt)
const dependencies = registrationNameDependencies[registrationName]
for (let i = 0; i < dependencies.length; i++) {
const dependency = dependencies[i]
// Call this method to register
listenToTopLevel(dependency, mountAt, listeningSet)
}
}
Copy the code
RegistrationNameDependencies onClick registrationName is coming, and variable is a storage for the React event name and native browser event name corresponds to a Map, You can use this map to get the corresponding browser native event name
export function listenToTopLevel(topLevelType: DOMTopLevelEventType, mountAt: Document | Element | Node, listeningSet: Set
) :void {
if(! listeningSet.has(topLevelType)) {switch (topLevelType) {
/ /...
case TOP_CANCEL:
case TOP_CLOSE:
if (isEventSupported(getRawEventName(topLevelType))) {
trapCapturedEvent(topLevelType, mountAt) // Capture phase
}
break
default:
constisMediaEvent = mediaEventTypes.indexOf(topLevelType) ! = =- 1
if(! isMediaEvent) { trapBubbledEvent(topLevelType, mountAt)// The bubble stage
}
break
}
listeningSet.add(topLevelType)
}
}
Copy the code
By calling listenToTopLevel() on the dependencies loop, you can see that the listenToTopLevel() method is used to register events. TrapCapturedEvent and trapBubbledEvent are called in this method to register the capture and bubble events.
TrapCapturedEvent and trapBubbledEvent
The following describes only trapCapturedEvent: 👉 trapCapturedEvent source address, trapBubbledEvent source address
// Capture phase
export function trapCapturedEvent(topLevelType: DOMTopLevelEventType, element: Document | Element | Node) :void {
trapEventForPluginEventSystem(element, topLevelType, true)}// The bubble stage
export function trapBubbledEvent(topLevelType: DOMTopLevelEventType, element: Document | Element | Node) :void {
trapEventForPluginEventSystem(element, topLevelType, false)}Copy the code
function trapEventForPluginEventSystem(
element: Document | Element | Node,
topLevelType: DOMTopLevelEventType,
capture: boolean //Determine the capture or bubble stage) :void {
let listener
switch (getEventPriority(topLevelType)) {
}
const rawEventName = getRawEventName(topLevelType)
if (capture) {
addEventCaptureListener(element, rawEventName, listener)
} else {
addEventBubbleListener(element, rawEventName, listener)
}
}
Copy the code
😝 Here we can see that the capture event is via addEventCaptureListener() and the bubbling event is via addEventBubbleListener().
/ / capture
export function addEventCaptureListener(element: Document | Element | Node, eventType: string, listener: Function) :void {
element.addEventListener(eventType, listener, true)}/ / the bubbling
export function addEventBubbleListener(element: Document | Element | Node, eventType: string, listener: Function) :void {
element.addEventListener(eventType, listener, false)}Copy the code
The event store
Remember enqueuePutListener() above, where we put events into the transaction queue?
function enqueuePutListener(inst, registrationName, listener, transaction) {
/ /...
// Register the event to the document
listenTo(registrationName, doc)
// Store the event and put it in the transaction queue
transaction.getReactMountReady().enqueue(putListener, {
inst: inst,
registrationName: registrationName,
listener: listener
})
}
Copy the code
Yeah, putListener, this thing, we can look at the code
putListener: function (inst, registrationName, listener) {
// The React object used to identify registered events, such as onClick. The key is in the '.nodeid 'format, so you just need to know which React object it can flag
// step1: get the unique identifier of the component
var key = getDictionaryKey(inst);
// Step2: Get the object of the specified event type in the listenerBank object
var bankForRegistrationName = listenerBank[registrationName] || (listenerBank[registrationName] = {});
// Step3: Store the listener event callback method in listenerBank[registrationName][key], for example, listenerBank['onclick'][nodeId]
// All React events defined by all React component objects are stored in listenerBank
bankForRegistrationName[key] = listener;
// ...
}
// Get the component unique identifier
var getDictionaryKey = function (inst) {
return '. ' + inst._rootNodeID;
};
Copy the code
Dispatching events
Since the event delegate is registered with the Document, the process of event distribution is very simple. Since the event is stored in listenrBank, I just need to find the corresponding event type and perform the event callback
📢 Note: Since the element itself does not register any events, but delegates to the document, the event that will be triggered is a React synthetic event, not a browser native event
First, find the event triggered DOM and React Component. It is easy to find the actual DOM. See the getEventTarget source code:
/ / the source code to see here: https://github.com/facebook/react/blob/master/packages/react-dom/src/events/ReactDOMEventListener.js#L419
const nativeEventTarget = getEventTarget(nativeEvent)
let targetInst = getClosestInstanceFromNode(nativeEventTarget)
Copy the code
function getEventTarget(nativeEvent) {
let target = nativeEvent.target || nativeEvent.srcElement || window
if (target.correspondingUseElement) {
target = target.correspondingUseElement
}
return target.nodeType === TEXT_NODE ? target.parentNode : target
}
Copy the code
This nativeEventTarget object hangs on a property that starts with __reactInternalInstance, and that property is internalInstanceKey, The value is the React Component corresponding to the current React instance
Continue to see the source code: dispatchEventForPluginEventSystem ()
function dispatchEventForPluginEventSystem(topLevelType: DOMTopLevelEventType, eventSystemFlags: EventSystemFlags, nativeEvent: AnyNativeEvent, targetInst: null | Fiber) :void {
const bookKeeping = getTopLevelCallbackBookKeeping(
topLevelType,
nativeEvent,
targetInst,
eventSystemFlags
)
try {
// Event queue being processed in the same cycle allows
// `preventDefault`.
batchedEventUpdates(handleTopLevel, bookKeeping)
} finally {
releaseTopLevelCallbackBookKeeping(bookKeeping)
}
}
Copy the code
See, batchedEventUpdates(). Its job is to put the currently triggered event into the batch queue. HandleTopLevel is at the heart of event distribution
👉 source code here: handleTopLevel
function handleTopLevel(bookKeeping: BookKeepingInstance) {
let targetInst = bookKeeping.targetInst
// Loop through the hierarchy, in case there's any nested components.
// It's important that we build the array of ancestors before calling any
// event handlers, because event handlers can modify the DOM, leading to
// inconsistencies with ReactMount's node cache. See #1105.
let ancestor = targetInst
do {
if(! ancestor) {constancestors = bookKeeping.ancestors ; ((ancestors: any):Array<Fiber | null>).push(ancestor)
break
}
const root = findRootContainerNode(ancestor)
if(! root) {break
}
const tag = ancestor.tag
if (tag === HostComponent || tag === HostText) {
bookKeeping.ancestors.push(ancestor)
}
ancestor = getClosestInstanceFromNode(root)
} while (ancestor)
}
Copy the code
The main point is that event callbacks can change the DOM structure, so you should first iterate through the hierarchy in case there are any nested components, and then cache them.
And then continue with this method
for (let i = 0; i < bookKeeping.ancestors.length; i++) {
targetInst = bookKeeping.ancestors[i]
// getEventTarget mentioned above
const eventTarget = getEventTarget(bookKeeping.nativeEvent)
const topLevelType = ((bookKeeping.topLevelType: any): DOMTopLevelEventType)
const nativeEvent = ((bookKeeping.nativeEvent: any): AnyNativeEvent)
runExtractedPluginEventsInBatch(
topLevelType,
targetInst,
nativeEvent,
eventTarget,
bookKeeping.eventSystemFlags
)
}
Copy the code
Here is a for loop to iterate through the React Component and the father of all components, and then execute runExtractedPluginEventsInBatch () method
As you can see from the event dispatch above, React implements a bubbling mechanism itself. Starting with the object that triggered the event, and tracing back to the parent element, the event callbacks registered by each element in turn are called.
Event to perform
As above runExtractedPluginEventsInBatch () method is the entrance to the event execution, through the source code, we can know that it did two things
👉 runExtractedPluginEventsInBatch source
- Tectonic synthesis event
- Composite events constructed by batch processing
export function runExtractedPluginEventsInBatch(topLevelType: TopLevelType, targetInst: null | Fiber, nativeEvent: AnyNativeEvent, nativeEventTarget: EventTarget, eventSystemFlags: EventSystemFlags) {
// step1: construct the synthetic event
const events = extractPluginEvents(
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags
)
// step2: batch processing
runEventsInBatch(events)
}
Copy the code
Tectonic synthesis event
Let’s look at the related code extractPluginEvents() and runEventsInBatch()
function extractPluginEvents(topLevelType: TopLevelType, targetInst: null | Fiber, nativeEvent: AnyNativeEvent, nativeEventTarget: EventTarget, eventSystemFlags: EventSystemFlags) :Array<ReactSyntheticEvent> | ReactSyntheticEvent | null {
let events = null
for (let i = 0; i < plugins.length; i++) {
// Not every plugin in the ordering may be loaded at runtime.
const possiblePlugin: PluginModule<AnyNativeEvent> = plugins[i]
if (possiblePlugin) {
const extractedEvents = possiblePlugin.extractEvents(
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags
)
if (extractedEvents) {
events = accumulateInto(events, extractedEvents)
}
}
}
return events
}
Copy the code
The first step is to loop through the plugins. The relevant code is in the source code: Plugins, which is an array of all the events that are synthesized into the plugins that are injected when the EventPluginHub is initialized
/ / 📢 source address: https://github.com/facebook/react/blob/master/packages/legacy-events/EventPluginHub.js#L80
export const injection = {
injectEventPluginOrder,
injectEventPluginsByName
}
Copy the code
/ / 📢 source address: https://github.com/facebook/react/blob/master/packages/react-dom/src/client/ReactDOMClientInjection.js#L26
EventPluginHubInjection.injectEventPluginOrder(DOMEventPluginOrder)
EventPluginHubInjection.injectEventPluginsByName({
SimpleEventPlugin: SimpleEventPlugin,
EnterLeaveEventPlugin: EnterLeaveEventPlugin,
ChangeEventPlugin: ChangeEventPlugin,
SelectEventPlugin: SelectEventPlugin,
BeforeInputEventPlugin: BeforeInputEventPlugin
})
Copy the code
Stop, let’s go ahead and look at the extractEvents logic code without expanding the analysis
const extractedEvents = possiblePlugin.extractEvents(
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags
)
if (extractedEvents) {
events = accumulateInto(events, extractedEvents)
}
Copy the code
Because the const possiblePlugin: PluginModule = plugins[I], type is PluginModule, we can go to 👉SimpleEventPlugin source code to see extractEvents exactly what did
extractEvents: function() {
const dispatchConfig = topLevelEventsToDispatchConfig[topLevelType]
if(! dispatchConfig) {return null
}
/ /...
}
Copy the code
First, look at the topLevelEventsToDispatchConfig there any topLevelType this property in the object, as long as there is, it means that the current events can use SimpleEventPlugin structure synthesis
EventConstructor is defined inside the function, and then switch… The case statement does the assignment
extractEvents: function() {
/ /...
let EventConstructor
switch (topLevelType) {
// ...
case DOMTopLevelEventTypes.TOP_POINTER_UP:
EventConstructor = SyntheticPointerEvent
break
default:
EventConstructor = SyntheticEvent
break}}Copy the code
Anyway, it’s assigned to EventConstructor. If you want to learn more about SyntheticEvents, click here
Once EventConstructor is set up, the method continues execution
extractEvents: function() {
/ /...
const event = EventConstructor.getPooled(
dispatchConfig,
targetInst,
nativeEvent,
nativeEventTarget
)
accumulateTwoPhaseDispatches(event)
return event
}
Copy the code
So what this code means is that we get the synthesized event from the event object pool, and the getPooled() method here is actually set when SyntheticEvent is initialized, so let’s take a look at the code
function addEventPoolingTo(EventConstructor) {
EventConstructor.eventPool = []
// This is where getPooled is set
EventConstructor.getPooled = getPooledEvent
EventConstructor.release = releasePooledEvent
}
SyntheticEvent.extend = function(Interface) {
/ /...
addEventPoolingTo(Class)
return Class
}
addEventPoolingTo(SyntheticEvent)
Copy the code
So we see here that getPooled is getPooledEvent, so let’s see what getPooledEvent does
function getPooledEvent(dispatchConfig, targetInst, nativeEvent, nativeInst) {
const EventConstructor = this
if (EventConstructor.eventPool.length) {
const instance = EventConstructor.eventPool.pop()
EventConstructor.call(
instance,
dispatchConfig,
targetInst,
nativeEvent,
nativeInst
)
return instance
}
return new EventConstructor(
dispatchConfig,
targetInst,
nativeEvent,
nativeInst
)
}
Copy the code
So the first thing you’re going to do is you’re going to go into the object pool and see if the length is zero, so if this is the first event that’s triggered, sorry, you’re going to need new EventConstructor, so if you’re going to do it again, you’re going to take it directly from the object pool, Also is the instance directly = EventConstructor. EventPool. Pop () out afterward
Ok, with that out of the way, let’s move on to another important action that events do: batch (events).
The batch
Batch processing is mainly operated by runEventQueueInBatch(Events). Let’s take a look at the source code: 👉 runEventQueueInBatch source code
export function runEventsInBatch(
events: Array<ReactSyntheticEvent> | ReactSyntheticEvent | null
) {
if(events ! = =null) {
eventQueue = accumulateInto(eventQueue, events)
}
// Set `eventQueue` to null before processing it so that we can tell if more
// events get enqueued while processing.
const processingEventQueue = eventQueue
eventQueue = null
if(! processingEventQueue) {return} forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel) invariant( ! eventQueue,'processEventQueue(): Additional events were enqueued while processing ' +
'an event queue. Support for this has not yet been implemented.'
)
// This would be a good time to rethrow if any of the event handlers threw.
rethrowCaughtError()
}
Copy the code
This method first merges the events that need to be processed and the queue that has not been processed before with the accumulateInto method in order to form a new queue
If processingEventQueue is empty, gg, and no event is processed, exit, otherwise call forEachAccumulated(), see the source code here: forEachAccumulated source code
function forEachAccumulated<T> (arr: ? (Array
| T
),
cb: (elem: T) = >void.scope:?any
) {
if (Array.isArray(arr)) {
arr.forEach(cb, scope)
} else if (arr) {
cb.call(scope, arr)
}
}
Copy the code
This method is to look at the event queue processingEventQueue is an array, if it is an array, shows that more than one event in the queue, then traverse the queue, call executeDispatchesAndReleaseTopLevel, otherwise only one event in the queue, Call directly without traversal
📢 executeDispatchesAndReleaseTopLevel source
const executeDispatchesAndRelease = function(event: ReactSyntheticEvent) {
if (event) {
executeDispatchesInOrder(event)
if(! event.isPersistent()) { event.constructor.release(event) } } }const executeDispatchesAndReleaseTopLevel = function(e) {
return executeDispatchesAndRelease(e)
}
Copy the code
export function executeDispatchesInOrder(event) {
const dispatchListeners = event._dispatchListeners
const dispatchInstances = event._dispatchInstances
if (__DEV__) {
validateEventDispatches(event)
}
if (Array.isArray(dispatchListeners)) {
for (let i = 0; i < dispatchListeners.length; i++) {
if (event.isPropagationStopped()) {
break
}
// Listeners and Instances are two parallel arrays that are always in sync.
executeDispatch(event, dispatchListeners[i], dispatchInstances[i])
}
} else if (dispatchListeners) {
executeDispatch(event, dispatchListeners, dispatchInstances)
}
event._dispatchListeners = null
event._dispatchInstances = null
}
Copy the code
First of all get the events on mount dispatchListeners, is a collection of all registered event callback function, through the collection, if the event isPropagationStopped () = true, ok, it is good to break, Since the preceding event has called event.stopPropagation(), the value of isPropagationStopped is set to true, the current event and subsequent events as parent events should no longer be executed
When the event here. IsPropagationStopped () is true, interrupt synthetic upward through execution of events, also will have the same effect and native events call stopPropagation If the loop is not interrupt, The executeDispatch method continues, and for this method, the source address is given: executeDispatch source address
And…
subsequent
No follow-up, to write, then everyone should go to see the source code from noon on hole, and then through the event. The nativeEvent. StopImmediatePropagation problem solving after, began to read related blog posts, to see the source code, I blow up, From 2:00 p.m. to 10:00 p.m., I’m already puking. OMG
Tips: When the binding onClick is triggered, the composite event is triggered, and the composite event is later than the native event, so when you click, you actually bubble up to the native event, that is, when the document binding event is triggered and then the composite event is triggered, it is too late to organize in the composite event.
. So will the document body. AddEventListener into the window. The addEventListener
Other articles
- Today’s quiz question: Do you know the difference between React and Vue? SKR, SKR
- Front-end slags are constantly refactoring the requestAPI
- Double 11 encore -JS constructor pattern and factory pattern
- Come and feel your first NPM package
- Understand Vue’s nextTick, Watcher and Dep’s blue life and death love?
Related links
-
Hiro’s blog
-
Hiro’s reading list
-
React source analysis 6 – React synthesis event system