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