Vuex version is ^2.3.1, I sort out vuex according to my own understanding.

About the mutation

This should make sense.

The only way to change the state in Vuex’s store is to commit mutation. Mutations in Vuex are very similar to events: each mutation has a string event type (type) and a callback function (handler). This callback is where we actually make the state change, and it takes state as the first argument:

const store = new Vuex.Store({
  state: { // 类似 vue 的 data
    count: 1
  },
  mutations: { // Similar to vue's methods
    increment (state) { // This is a callback function
      // Change the state
      state.count++
    }
  }
})
Copy the code

You cannot call a mutation handler directly. This option is more like event registration: “This function is called when a mutation of type INCREMENT is triggered.” To wake up a mutation handler, you need to call the store.mit method with the corresponding type:

// this is equivalent to a special call event
store.commit('increment')
Copy the code

Payload submission

You can pass an additional parameter to store.mit, which is the payload of mutation.

mutations: {
 // The first argument is state, and the second argument is called extra, which is n
  increment (state, n) {
    state.count += n
  }
}
Increment is passed as an additional argument, and n is 10
store.commit('increment'.10)
Copy the code

In most cases, the payload should be an object, which can contain multiple fields and the mutation recorded will be more readable:

mutations: {
  increment (state, payload) {
	  // Payload is a more readable object
    state.count += payload.amount
  }
}
// The additional mutation parameter is passed in as an object.
store.commit('increment', {
  amount: 10
})
Copy the code

In general, mutations can transfer parameters, and the best way to transfer parameters is as an object.

Object style submission

Another way to submit mutation is to use an object containing the type attribute directly:

// An object is also passed in, but this object contains the type attribute
store.commit({
  type: 'increment'.amount: 10
})
Copy the code

This is just a way to submit mutations, so don’t go into it.

When using this object-style commit, the entire object is passed as a payload to the mutation function, so the handler remains unchanged:

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
// Vuex will split this object, except for type, which will still be passed as additional arguments
store.commit({
  type: 'increment'.amount: 10
})
Copy the code

After passing the entire object to mutation, VUEX recognizes that it is a mutation payload parameter based on the type parameter, and then automatically populates the state parameter as the first and the second parameter as the second parameter of the object passed in.

Here’s a demo of JsRun: jsrun.net/VvqKp

In this case it’s going to be plus 11!

The Mutation complies with the Vue response rules

Since the state in Vuex’s Store is responsive, the Vue component that monitors the state updates automatically when we change the state. This also means that mutation in Vuex requires the same precautions as mutation in Vue:

  • It is best to initialize all required properties in your store in advance.
  • When you need to add new properties to an object, you should
    • useVue.set(obj, 'newProp', 123)(Follow the vUE approach)
    • Replace an old object with a new one. For example, using the stage-3 object expansion operator we could write:state.obj = { ... state.obj, newProp: 123 }(Destruct the object with extension notation, and then assign to the new object, because objects are reference types in JS.)

Replace Mutation event types with constants

Substituting constants for mutation event types is a common pattern in various Flux implementations. This allows tools like Linter to work, and keeping these constants in a separate file allows your code collaborators to see what mutations are included in the entire app:

Whether or not you use constants is up to you — this can be very helpful on large projects that require multiple people to work together. But if you don’t like it, you don’t have to.

// mutation-types.js File where constants are placed
export const SOME_MUTATION = 'SOME_MUTATION'

// store.js
import Vuex from 'vuex'
// A separate constant is imported to test this usage
import { SOME_MUTATION } from './mutation-types'

const store = new Vuex.Store({
  state: {... },mutations: {
    // We can use ES2015 style computed attribute naming to use a constant as the function name
    [SOME_MUTATION] (state) {
      // mutate state}}})Copy the code

Note: The calculated attribute names of ES2015 are named using brackets, which allow us to define attributes using variables or strings that would cause syntax errors when using identifiers, such as person[“first name”], and nothing more.

I think, as soon as possible to adapt to this writing method is better, can pretend force and can learn other people’s advanced skills.

Mutation must be a synchronization function

An important rule to remember is that mutation must be a synchronization function.

Essentially any state change made in a callback function is untraceable. So you need to do asynchronous mutation encapsulation in actions to implement async.

Commit Mutation in the component

You can use this. codeStore.mit (‘ XXX ‘) to commit mutation in the component, or use the mapMutations helper function to map methods in the component to a store.mit call (requiring store injection at the root node).

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    The mapMutations tool function maps the commit method in the store to the component's methods. mapMutations(['increment'.// Map 'this.increment()' to 'this.store.com MIT ('increment')'
      // 'mapMutations' also supports payloads:
      'incrementBy' // Map 'this.incrementBy(amount)' to 'this. codestore.com MIT ('incrementBy', amount)'
    ]),
    ...mapMutations({
      add: 'increment' // Map 'this.add()' to 'this.store.mit ('increment')'}}})Copy the code
  • .Es2015 extended operator that can deconstruct arrays or objects, here is the Deconstruction mapMutations object.
  • Writing method and parameters of mapMutations:mapMutations(namespace? : string, map: Array<string> | Object): Object(Format of API document on official website)
    • The first argument is the module space name string, which can be left blank
    • The second argument is a map-structured object, which can also be an array of strings
    • It returns an object
    • For more information about jSdoc formatting, see: Use Jsdoc: @type

About… mapMutations

First: normalizeMap formats mutations as an array:

function normalizeMap (map) {
  // Check if it is an array and return an array
  return Array.isArray(map)
    // If the array is the map loop
    ? map.map(key= > ({ key, val: key }))
    // Take the key out of the map loop
    : Object.keys(map).map(key= > ({ key, val: map[key] }))
}
Copy the code

For example, mutations passed in is an array, as follows:

/ / before the conversion
[
      // This is no extra parameter (no load)
      'increment'.// This has extra parameters (payload)
      'incrementBy' 
]
// Then normalizeMap transforms:
{key, val: key})
[
    { 
     key, / / key is increment
     val: key / / val is increment
    },
    // An extra argument is passed in, but it is not processed in the conversion
    { 
     key, / / key is incrementBy
     val: key / / val is incrementBy
    },    
    / /...
]
Copy the code

For example, mutations, which is passed in, is an object, as follows:

/ / before the conversion
{
      addAlias: function(commit, playload) {
           commit('increment') 
           commit('increment', playload)
      } 
}
// Then normalizeMap transforms:
{key, val: key})
{ 
    key, / / key is addAlias
    val: map[key] // val is the value of the object's key property, which is function().....
}
Copy the code

Then go back to the vuex source code for mapMutations:

// See the vuex source code
var mapMutations = normalizeNamespace(function (namespace, mutations) {
  var res = {};
  // mutations formatted by normalizeMap is looped by foreach
  normalizeMap(mutations).forEach(function (ref) {
    var key = ref.key;
    var val = ref.val;

    res[key] = function mappedMutation () {
      // Copy payload: Copies additional parameters to args array
      var args = [], len = arguments.length;
      while ( len-- ) args[ len ] = arguments[ len ];
        
      var commit = this.$store.commit;
      // Forget about namespaces
      / /...
      return typeof val === 'function'
        // is a function, the function is executed directly, with comit as its first argument and arg as its subsequent argument.
        ? val.apply(this, [commit].concat(args))
        // If it is not a function, commit is executed with an array of values and payloads.
        : commit.apply(this.$store, [val].concat(args))
    };
  });
  return res
});
Copy the code
  • The implementation of mapState is almost identical, with only two differences:
    • The payload may or may not be passed when submitting mutaion, and copy payloads are carried out regardless of whether the payload is passed or not.
    • This is done for functions, which are actually just some commit, and for non-functions, the commit is done directly, but the current one is boundthis.$storeAs a scope, the payload parameters are also passed.

Then return to the actual conversion effect as follows:

// It requires the introduction of mapMutations to be available
import { mapMutations } from 'vuex' 

export default {
  // ...methods: { ... mapMutations('moduleName'[// Map 'this.increment()' to 'this.store.com MIT ('increment')'
      'increment'.// 'mapMutations' also supports payloads:
      // Map 'this.incrementBy(amount)' to 'this. codestore.com MIT ('incrementBy', amount)'
      'incrementBy' 
    ]),
    
    ...mapMutations('moduleName', {
      // Map 'this.add()' to 'this.store.mit ('increment')'
      add: 'increment' 
    }),
    
    ...mapMutations('moduleName', {
      addAlias: function(commit) {
          // Map 'this.addAlias()' to 'this.code.store.mit ('increment')'
          commit('increment')}})}}Copy the code
  • Increment is the same thing as incrementBy, but it’s the same thing as incrementBythis.$store.commit('increment')If there are parameters, it will automatically add parameters.
  • Others are easier to understand, combined with the source code seen before, we know that his conversion is handled separately

Here’s an example of jsrun: jsrun.net/U6qKp


Reference:

  • Vuex 2.0 source code analysis (ii) _ MOOCs notes
  • This article describe more clearly, can see this article: first segmentfault.com/a/119000001…