A comprehensive understanding of Vuex, more understandable than official knowledge (PART 1)

Vuex advanced operation

Auxiliary function

mapState

We said earlier that we can use computed properties when a component accesses a value in a Store instance. It is fine if we access a value, but if we need to access multiple values, we need to write multiple computed properties in computed, which is neither easy nor elegant. In this case, Vuex provides us with auxiliary functions.

// In the separately built version, the auxiliary function is vuex.mapstate
import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    // Arrow functions make code more concise
    count: state= > state.count,

    // Pass the string argument 'count' equal to 'state => state.count'
    countAlias: 'count'.// In order to be able to use 'this' to get local state, you must use regular functions
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}
Copy the code

We can also pass mapState an array of strings when the name of the computed property of the map is the same as the name of the child node of State.

computed: mapState([
  // Map this.count to store.state.count
  'count'
])
Copy the code

The mapState function returns an object. How do we mix it with local computed properties? Typically, we need to use a utility function to merge multiple objects into one so that we can pass the final object to the computed property. But since we have the object expansion operator, we can greatly simplify writing:

computed: {
  localComputed () { / *... * / },
  // Use the object expansion operator to blend this object into an external object. mapState({// ...})}Copy the code

mapGetters

MapGetters is also computed and used in much the same way as mapState

import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
  // Mix getters into a computed object using the object expansion operator. mapGetters(['doneTodosCount'.'anotherGetter'.// ...])}}Copy the code

If you want to give a getter property another name, use object form:

. mapGetters({/ / the ` enclosing doneCount ` mapping for ` enclosing $store. Getters. DoneTodosCount `
  doneCount: 'doneTodosCount'
})
Copy the code

mapMutations

import { mapMutations } from 'vuex'

export default {
  // ...
  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

mapActions

export default {
  // ...
  methods: {
    ...mapActions([
      'increment'.// Map 'this.increment()' to 'this.$store.dispatch('increment')'

      // 'mapActions' also supports payloads:
      'incrementBy' // Map 'this.incrementBy(amount)' to 'this.$store.dispatch('incrementBy', amount)'
    ]),
    ...mapActions({
      add: 'increment' // Map 'this.add()' to 'this.$store.dispatch('increment')'}}})Copy the code

modules

When writing Vue program, if a project is too big, we will split into individual components was carried out on the project, Vuex, too, when a store instance store too much content, it will become very bloated, at this point, we can divide it into individual components, similar to the following.

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const moduleA = {
  state: () = > ({ count: 5 }),
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {},getters: {
    name: () = > { return 'moduleA'}}}const moduleB = {
  state: () = > ({ count: 10 }),
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {},getters: {
    name: () = > { return 'moduleB'}}}export default new Vuex.Store({
  state: {},mutations: {},actions: {},modules: {
    a: moduleA,
    b: moduleB
  }
})


Copy the code

Local state of a module

For mutation and getters inside a module, the first argument received is the module’s local state object.

Similarly, for actions within a module, you can use context.state to access the state inside the module, and context.rootState to access the state of the root node:

For getters inside the module, the root node state is passed in as the third argument (in the correct order)

getters: {
    sumWithRootCount (state, getters, rootState) {
      return state.count + rootState.count
    }
  }
Copy the code

How do we access status and mutation in the Module? In Module, state is local to the Module, so we can access it this way

this.$store.state.a.count / / - > 5
Copy the code

By default, actions, mutations, and getters inside a module are registered in the global namespace — enabling multiple modules to respond to the same mutation or action. Take a chestnut

  mounted () {
    setInterval(() = > {
      this.$store.commit('increment')},1000)}Copy the code

When we triggered a Mutation, the same Mutation inside the module was triggered at the same time, and the state in both modules changed. The same is true for actions, which I won’t demonstrate. Getters are also registered in the global namespace. If two Modules have getters of the same name, the module introduced first takes precedence.

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const moduleA = {
  state: () = > ({ count: 5 }),
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {},getters: {
    name: () = > { return 'moduleA'}}}const moduleB = {
  state: () = > ({ count: 10 }),
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {},getters: {
    name: () = > { return 'moduleB'}}}export default new Vuex.Store({
  state: {},mutations: {},actions: {},modules: {
    a: moduleA,
    b: moduleB
  }
})
Copy the code
this.$store.getters.name // -> 'moduleA'
Copy the code

The namespace

So, what if we just want to keep each module separate, not affect the global space, and maintain better encapsulation? Vuex gives us the option to enable the module’s namespace by simply adding namespaced: True inside the module.

When namespaces are enabled, getters and actions in the current module receive localized getters, dispatch, and commit, so our code doesn’t need to change at all. However, when we call getters, Actions and mutations in the module in the external vUE component, the module name needs to be added. Since state is the local state in the module, it makes no difference whether the namespace is added or not

const store = new Vuex.Store({
  modules: {
    // Module name: account
    account: {
      namespaced: true.// Module assets
      state: () = >({... }),// Module states are already nested, and using the 'namespaced' attribute doesn't affect them
      getters: {
        isAdmin () { ... } // -> this.$store.getters['account/isAdmin']
      },
      actions: {
        login () { ... } // -> this.$store.dispatch('account/login')
      },
      mutations: {
        login () { ... } // -> this.$store.commit('account/login')
      },

      // Nested modules
      modules: {
        // Inherits the parent module's namespace
        myPage: {
          state: () = >({... }),getters: {
            profile () { ... } // -> this.$store.getters['account/profile']}},// Further nested namespaces
        posts: {
          namespaced: true.state: () = >({... }),getters: {
            popular () { ... } // -> this.$store.getters['account/posts/popular']}}}}}})Copy the code

So what if we have namespace enabled and want to access global content inside the module?

In the getter, we can receive the third parameter rootState to access the global state and the fourth parameter rootGetters to access the global getter

// Inside the module
getters:{ someGetter (state, getters, rootState, rootGetters) { ... }},Copy the code

If we want to call a global action or Mutation from an action inside the module, we simply pass {root: true} as the third argument to Dispatch or COMMIT.

// Inside the module
actions: {
    someAction ({dispatch, commit}) {
        dispatch('someOtherAction'.null, { root: true })
        commit('someMutation'.null, { root: true}}})Copy the code

To register a global action in a namespaced module, add root: true and place the action definition in a handler. Just as we use watch when we need deep monitoring, for example:

  // Inside the module
  namespaced: true.actions: {
    someAction: {
      root: true.// This action will be registered in the global spacehandler (namespacedContext, payload) { ... }}}Copy the code

Remember our helper function above? How do we use helper functions if we have namespace enabled inside a module?

MapGetters and mapState are similar, mapActions and mapMutations are not so good, so I won’t repeat the demonstration here

There are three methods we can use

const moduleA = {
  namespaced: true.state: () = > ({ count: 5 }),
  mutations: {
    addMutation (state) {...}
  },
  actions: { addAction ({commit}) {... }}}export default new Vuex.Store({
  state: {
    count: 1
  },
  modules: {
    moduleA // moduleA:moduleA uses the ES6 syntax abbreviated as moduleA}})// => can be simplified to

computed: {
  ...mapState('moduleA', {
    count: state= > state.count
  })
}

Copy the code

The first one: take the path with you when you use it

import { mapState, mapMutations } from 'vuex'

computed: {
  ...mapState({
    count: state= > state.moduleA.count / / = > 5})},methods: {
  ...mapMutations([
    'moduleA/addMutation' // this['moduleA/addMutation']()
  ])
  ...mapMutations({
    addMutation: 'moduleA/addMutation' This.addmutation ()})}Copy the code

Second: pass the module name in the first argument

import { mapState, mapMutations } from 'vuex'

computed: {... MapState (moduleA, {count: state= > state.count / / = > 5})},methods: {... MapMutations (moduleA, ['addMutation' This.addmutation ()])... MapMutations (moduleA, {addMutation: 'addMutation' This.addmutation ()})}Copy the code

createNamespacedHelpers

import { createNamespacedHelpers } from 'vuex'
const { mapState, mapMutations } = createNamespacedHelpers('moduleA')

computed: {
  ...mapState({
    count: state= > state.count / / = > 5})},methods: {
  ...mapMutations([
    'addMutation' This.addmutation ()
  ])
  ...mapMutations({
    addMutation: 'addMutation' This.addmutation ()})}Copy the code

Module reuse

Sometimes we may need to create multiple instances of a module, for example:

  • Create multiple stores that share the same module (for example, to avoid stateful singletons in server rendering when runInNewContext option is false or ‘once’)
  • Register the same module multiple times in a store

If we use a pure object to declare a module’s state, the state object will be shared by reference, leading to contamination of store or module data when the state object is modified.

This is actually the same problem with data in Vue components. So the solution is the same — use a function to declare the module state (supported only in 2.3.0+) :

const MyReusableModule = {
  state: () = > ({
    foo: 'bar'
  }),
  // mutation, action and getters, etc...
}
Copy the code

Expand the knowledge

Value in the V-model bidirectional binding state

It is not recommended that we directly use v-Model to bind the value in state, and if we turn on strict mode, it will still give an error. If we solve this problem with vUE thinking, that is, we use V-Model to bind a value, and then monitor the change of the value. Using commit to change the value in state is too cumbersome, and the most elegant way to do this is to use the getter and setter properties of the calculated property

// ...
mutations: {
  updateMessage (state, message) {
    state.obj.message = message
  }
}
Copy the code
<input v-model="message">
Copy the code
// ...
computed: {
  message: {
    get () {
      return this.$store.state.obj.message
    },
    set (value) {
      this.$store.commit('updateMessage', value)
    }
  }
}
Copy the code

Well, the content of this article stops here, time is hasty, knowledge is various, study art is not fine, hard to avoid mistakes, if you big guy someone found in the wrong place, hope to give correction, thank you, hope can help you, I wish you work smoothly 😄