preface

Before reading the COMMIT source code, take a few minutes to review how to use commit.

The source code interpretation

Relevant methods

  • UnifyObjectStyle Checks the parameter type and adjusts it accordingly. Detailed introduction

  • _withCommit to avoid errors when changing the status. Detailed introduction

Commit the source code

  commit (_type, _payload, _options) {
    const {
      type, // Trigger type, e.g. Store.com MIT ('increment')
      payload, // Commit payloads as parameters, e.g. Store.com MIT ('increment', 10)
      options / / configuration items
    } = unifyObjectStyle(_type, _payload, _options)

    const mutation = { type, payload } // Define the mutation variable
    
    // this._mutations, store specifies an object that stores mutation
    const entry = this._mutations[type] // Get the corresponding mutation type
    
    // Block entry if it does not exist
    if(! entry) {if (__DEV__) {
        console.error(`[vuex] unknown mutation type: ${type}`)}return
    }
    
    this._withCommit(() = > {
      entry.forEach(function commitIterator (handler) {
        handler(payload) // Call mutation and pass in the argument})})// This. _subscribers () subscribed to the store. Subscribe method
    // handler (a function, which is stored below).
    
    // Iterate over the copy of this._subscribers (calling the new array returned by Slice), call
    // handler and pass in mutation and this.state as arguments. That's why
    // The handler in store. Subscribe accepts these two arguments.
   
    // Shallow replication with slice is created to prevent subscribers from synchronizing calls to unsubscribe
    // The proxy fails.
    this._subscribers
        .slice()
        .forEach(sub= > sub(mutation, this.state)) 
      
    if (__DEV__ && options && options.silent) {
      console.warn(
        `[vuex] mutation type: ${type}. Silent option has been removed. ` +
        'Use the filter functionality in the vue-devtools')}}Copy the code

To better understand the source code, we also need to understand the vuex. Store instance method: subscribe

Learn subscribe from the official document, you must have noticed the following sentence:

Subscribe to store mutation. Handler is called after each mutation is complete, receiving the mutation and the state after mutation as parameters

This is the first time I have ever seen a woman.

  1. Subscribe to store mutation

  2. After store.mit completes the mutation commit, it calls handler (the function passed in when store.subscribe is called). In the above code, you must have noticed that mutation was called in this._withCommit and then iteratingthis. _subscribers to call handler, Mutation and the state after mutation (that is, the modified state) were passed in as parameters.

Words alone may not be clear enough. Below, we take a look at the case and its source code.

The subscribe case

For convenience, the counter test case provided by Vuex (directory location: examples/counter) was used.

Call the store instance methods commit and subscribe:

Print parameters:

Next, we also need to understand the source of SUBSCRIBE.

The subscribe source

  subscribe (fn, options) {
    return genericSubscribe(fn, this._subscribers, options)
  }
Copy the code
  • GenericSubscribe parameter

    1. Fn: a function that accepts mutation and the state after mutation as arguments. Fn is the function we pass in when we call store.subscribe.
    store.subscribe((mutation, state) = > {
      console.log(mutation.type)
      console.log(mutation.payload)
    })
    Copy the code
    1. Subs: (this._subscribers) Stores handler (FN)

    2. Options: prepend: true to add a handler to the beginning of its chain.

function genericSubscribe (fn, subs, options) {
  First check if FN already exists in subs (this._subscribers).
  // If not, check the configuration items options and options.prepend. If both are true,
  // Insert fn at the beginning of subs, otherwise at the end of subs
  if (subs.indexOf(fn) < 0) {
    options && options.prepend
      ? subs.unshift(fn)
      : subs.push(fn)
  }
  Subscribe returns the current fn function, which is the key to unsubscribe
  return () = > {
    const i = subs.indexOf(fn)
    if (i > -1) {
      subs.splice(i, 1)}}}Copy the code

You already know a lot about commit and subscribe. Now, one more thing to understand: why does iterator invalidation occur when subscribers call unsubscribe in sync after iterating through the handler after slice returns its new array on this._subscribers?

If you’re careful, you may have noticed that the genericSubscribe function does two things:

  1. Subscribers add fn to this._subscribers

  2. Returns the current FN (implemented by calling subs.splice(I, 1)), which is called when unsubscribing.

Calling store.subscribe adds fn to this._subscribers, and calling the function returned by store.subscribe (unsubscribe) removes FN from this._subscribers.

These two operations, one plus one subtraction, are both changing this._subscribers, so just to be safe, slice returns a new array and then does the corresponding operation instead of operating on this._subscribers directly.