series

  • Vue 2 basis
  • Vue 2 components
  • Vue 2 dynamic effect
  • Vue 2 plug-in
  • Vue Router
  • Vuex

reference

  • The Re Vue Start from Alex’s Youtube channel
  • Vuex official document

The 🔗 article has an embedded Youtube player that can play the clip directly, while the article can be accessed by clicking the 🎬 icon to open the corresponding Youtube clip url.


Vuex is a state management model developed specifically for vue.js applications. It uses centralized storage to manage the state of all components of the application, and corresponding rules to ensure that the state changes in a predictable manner.

🎬 The core concepts and how Vuex works are shown in the following figure

Vuex is to extract the state (data) shared by multiple components for management; The state that is exclusive to the component (typically the underlying component) should be managed using the component’s data. In order to be able to track state changes, one principle must be followed: the only way to change state in Vuex is to commit mutation.

⚠️ This pattern causes components to rely on global state singletons.

Install the introduction

Can be imported using CDN

<script src="https://unpkg.com/vuex/dist/vuex.js"></script>
Copy the code

You can also install the plug-in via NPM and then install it via vue.use ()

npm install vuex --save
Copy the code
import Vuex from 'vuex'

Vue.use(Vuex)
Copy the code

At the heart of Vuex is the repository. The store is basically a container that uses a single state tree. That is, each Vue application has only one store instance (object), which contains the state states shared by all application levels and the related methods to modify the state.

💡 Single-state trees and modularity do not conflict

Instantiate a repository of shared state for components and methods to modify that state

const store = new Vuex.Store({
  state: {},
  getters: {},
  mutations: {},
  actions: {}})Copy the code

🎬 In order to access this.$store property in the Vue component, you need to provide the created store for the Vue instance. 🎬 Vuex provides a mechanism to “inject” the store from the root component to all child components with the store option:

new Vue({
  el: '#app'.store: store,
})
Copy the code

State

State is a 🎬 single state tree shared by components, following the principle of single Source of truth.

This is defined in the State option of the store, similar to the data option of the component.

const store = new Vuex.Store({
  / / state
  state: {
    numbers: [0.1.2.3.4.5.6]}});Copy the code

🎬 Use store.state.stateName or this.$store.state.stateName to access the specific state (property) in the component. Since the state store of Vuex is reactive, the state states read from the Store are generally taken as component computed properties, so that whenever the corresponding state changes, the corresponding computed properties are also recalculated, triggering an update to the associated DOM.

In a project system built through modularity, various state properties need to be imported in multiple components. 🎬 Vuex provides a helper function called mapState to make it easier to generate computed properties. This function can pass objects or arrays as arguments:

import { mapState } from 'vuex'

// ...
// Pass objects for the helper function 'mapState' (key-value pairs can be used in several ways)
computed: mapState({
  // Rename the property of state directly by taking the string value of the property of state. The key can be any name
  countAlias: 'count'.// Use the arrow function to return one of the attributes in the required state, passing state as an argument
  count: state= > state.count,
  // Return a new mixed state by using this to get the local state (data property) of the current component
  countPlusLocalState(state) {
    return state.count + this.localCount
  }
})
Copy the code
import { mapState } from 'vuex'

// ...
// Pass the array to the helper function 'mapState' (the name of the mapped computed property is the same as the name of the state property)
// The computed attributes contain only the attributes mapped from state
computed: mapState([
  'count'
])
// The computed properties contain the properties mapped from state, as well as the locally computed properties in the component.
// The mapState function returns an object. You can use the object expansion operator to deconstruct the objects returned by mapState, mixing them with local computed properties in the component
computed: {
  localComputed(){... },... mapState({... })}Copy the code

💡 The data state stored in Vuex follows the same rules as the data in the Vue instance. The object must be plain

Getter

🎬 Getter is derived from state and is defined in the store option getters, similar to the action of the component’s computed option.

🎬 The Getter function takes a state object as its first argument, and (optionally) getters as its second argument, which is an object containing other getters, and returns a value or a function.

const store = new Vuex.Store({
  / / state
  state: {
    numbers: [0.1.2.3.4.5.6]},// Getter
  getters: {
    oddNumbers(state) {
      return state.numbers.filter((num) = > {
        return num % 2})},evenNumbers(state) {
      return state.numbers.filter((num) = > {
        return (num % 2) - 1})},numbersLen: (state, getters) = > {
      return getters.oddNumbers.length + getters.evenNumbers.length
    }
  }
});
Copy the code

🎬 There are two ways to use getters in components:

  • Can be achieved bystore.getters.getterNameThe concrete Getter is accessed as a property
  • You can also go throughstore.getters.getterName(params)Access to a concrete Getter in the form of a method call (and, correspondingly, when the Getter is defined,The return value should be a function)

💡 When the Getter is accessed as a property, it is considered to be a computed store. The dependent cache function is enabled. That is, the Getter is recalculated only when its dependent value changes. The Getter, when called as a method, can be thought of as a methods store, and is called every time without caching the result.

🎬 Vuex provides the helper function mapGetters to mapGetters in stores to locally computed properties of components more easily. This function can pass an object or an array of strings as an argument:

import { mapGetters } from 'vuex'

// ...
// Pass the array to the helper function 'mapGetters' (the name of the mapped computed property is the same as the name of the getters property)
computed: {
  // Use the object expansion operator to mix getters into a computed object. mapGetters(['doneTodosCount'.'anotherGetter',])}Copy the code
import { mapGetters } from 'vuex'

// ...
// If you want to rename the getter property, pass the object to the helper function 'mapGetters'
computed: {
  ...mapGetters({
    / / the ` enclosing doneCount ` mapping for ` enclosing $store. Getters. DoneTodosCount `
    doneCount: 'doneTodosCount'})}Copy the code

Mutation

In order to be able to track state changes, 🎬 follows the rule that the only way to change state in Vuex is to commit mutation.

🎬, in store’s option mutations, defines a mutation type (similar to event types) and its mutation Handler (callback functions) as functions in which the state is modified.

The callback function takes state as the first argument and (optionally) the second argument, called payload, to receive the data being passed.

const store = new Vuex.Store({
  / / state
  state: {
    count: 1
  },
  mutations: {
    INCREMENT(state) {
      // Change the statusThe state count++},SET_COUNT(state, n) {
      // Change the status
      state.count = n
    }
  } 
});
Copy the code

💡 recommends that mutationType use constants (uppercase) and assign values to variables, and that mutation handlers be defined with computational attribute names, 🎬 to allow tools such as linter to come into play while inputting variables and to avoid duplicate code

🎬 Operations in the mutation Handler must be synchronous so that DevTools accurately captures each snapshot of the previous and later state of the mutation.

🎬 In the component, similar to triggering events, to commit a specific Mutation event type, the corresponding Mutation handler is executed to modify the state attribute, passing (optional) the second parameter payload, Passed as data to the mutation Handler

store.commit('SET_COUNT', { count: 10 })
Copy the code

💡 can also use the object style to submit Mutation, passing an object that contains the type attribute, which specifies the muationType, and passing the other attributes as payload to the handler

store.commit({
  type: 'SET_COUNT'.count: 10
})
Copy the code

MapMutations, the auxiliary function provided by 🎬 Vuex, more conveniently maps mutations in store to the local methods of the component, which facilitates the subsequent implementation of mutation submitting by calling the corresponding methods in the component directly. This function can pass an object or an array of strings as an argument:

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    // Array. mapMutations([// map this.increment() to this.store.com MIT ('increment')
      This.increment (amount) is mapped to this.store.com ('increment', amount).
      'increment',]),// Object form, renaming method. mapMutations({add: 'increment' // Map this.add() to this.store.com MIT ('increment')}}})Copy the code

Action

🎬 Action is a function similar to mutation, except that: Action is generally used to commit mutation, not to change state directly, and Action can contain asynchronous operations; In Mutation, there are only synchronous operations.

🎬 Defines an action type (similar to an event type) and its action Handler (callback function) as functions in the store’s option Actions, where asynchronous operations are performed and mutation is submitted

The callback function takes context as the first argument, which is an object with the same methods and attributes as the Store instance, so you can submit a mutation by calling context.com MIT, Or get state and getters from context.state and context.getters. (Optional) The second parameter, called payload, receives the data as it is passed.

const store = new Vuex.Store({
  // ...
  actions: {
    increment (context, payload) {
      context.commit('INCREMENT', payload)
    }
  }
});
Copy the code

💡 There are often asynchronous actions in actions, 🎬 so Vuex returns a Promise by default when an Action is distributed in a component, If the action Handler callback also returns a Promise return new Promiser((resolve, reject) => {}), then the component can listen for the Promise execution in the callback. Perform the following operations:

const store = new Vuex.Store({
  // ...
  actionA ({ commit }) {
    // Define an action that returns a Promise
    return new Promise((resolve, reject) = > {
      setTimeout(() = > {
        commit('someMutation')
        resolve()
      }, 1000)})}});Copy the code
// Use in components
store.dispatch('actionA').then(() = > {
  // ...
})
Copy the code

🎬 Dispatches a specific Action event type in a component similar to dispatches. This dispatches the corresponding Action handler, passing an (optional) second parameter as payload (usually an object). To pass data to the Action Handler

store.dispatch('actionType')
Copy the code

Vuex provides a helper function called mapActions that maps the actions in the store to local methods of the component. This makes it easier to distribute the actions directly in the component by calling the corresponding method. This function can pass objects or arrays of strings as arguments:

import { mapActions } from 'vuex'

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

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

Module

Vuex allows us to divide the Store into modules, each of which has its own state, mutations, actions and getters, and also supports nested submodules.

Register modules in the store option modules

const moduleA = {
  state: () = >({... }),mutations: {... },actions: {... },getters: {... }}const store = new Vuex.Store({
    modules: {
      a: moduleA,
    }
})
Copy the code

The component can access the local state of the corresponding module A with the module name store.state.a

The state in the ⚠️ module should be in functional form and return an object containing the properties of the local state.

Local state

In a module, the first parameter that is received by the Mutation and Getter is an object of local state in the module.

const moduleA = {
  state: () = > ({
    count: 0
  }),
  mutations: {
    increment (state) {
      // `state` is the local module state
      state.count++
    }
  },

  getters: {
    doubleCount (state) {
      return state.count * 2}}}Copy the code

If you need to access the root state of the root node in the Gutter, it’s exposed in the third argument

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

For an Action in a module, the local state is exposed by context.state, and the root node state is exposed by context.rootState:

const moduleA = {
  // ...
  actions: {
    incrementIfOddOnRootSum ({ state, commit, rootState }) {
      if ((state.count + rootState.count) % 2= = =1) {
        commit('increment')}}}}Copy the code

💡 rootState rootState actually contains the local state of the loaded module (so modules can 🎬 “with” rootState as an intermediary to access the local state of each other registered module)

The namespace

The state of a module is local, and store.state.a is used to access the state of module A in the component (or parent module). 🎬 But the actions, mutations, and getters inside the module are registered in the global namespace, Enables multiple modules to respond to the same store.com MIT (‘mutationType’) or store.Dispatch (‘actionName’).

🎬 If you want a module to be more encapsulated and reusable (its getters, actions, and mutations do not conflict with global registration), you can make it a namespaced module by adding the option namespaced: true to the module.

const moduleA = {
  namespaced: true.// ...
}
Copy the code

After opening the namespace, getters, Actions and mutations of the module all become local. Therefore, in order to access the Getter, submit Mutation and distribute Action for the module in the component, corresponding calls should be made according to the registration path of the module:

  • getters['moduleName/stateName']
  • dispatch('moduleName/actionType')
  • commit('moduleName/mutationType')

When the Getter, Mutation, and Action are accessed inside the module, there is no need to add the path “prefix”, which makes it easier to migrate and reuse the module.

If, on the other hand, action or mutation for a global space is to be distributed and committed within a module with a namespace, {root: true} is correspondingly passed to Dispatch or COMMIT as the third argument

modules: {
  foo: {
    namespaced: true.actions: {
      // Actions are localized
      // Getters of global space can be accessed by 'rootGetters'
      someAction ({ dispatch, commit, getters, rootGetters }) {
        dispatch('someOtherAction') // -> 'foo/someOtherAction'
        dispatch('someOtherAction'.null, { root: true }) // -> 'someOtherAction'

        commit('someMutation') // -> 'foo/someMutation'
        commit('someMutation'.null, { root: true }) // -> 'someMutation'}}}},Copy the code

If you want to register global actions or mutations in a namespace-enabled module, you can add the option root for them and set the value to true

modules: {
  foo: {
    namespaced: true.actions: {
      someAction: {
        root: true,
        handler (namespacedContext, payload) { ... } // -> 'someAction'}}}}Copy the code

💡 In components, when mapping using helper functions such as mapState or mapActions, you can add the path of the namespace module as the first argument to simplify the calling code

/ / component
computed: {
  ...mapState('some/nested/module', {
    a: state= > state.a,
    b: state= > state.b
  })
},
methods: {
  ...mapActions('some/nested/module'['foo'.'bar'])}Copy the code

Dynamic registration module

🎬 After the store is created, you can still register the module using the store.registerModule method; You can use store.unregisterModule(moduleName) to dynamically uninstall modules, 🎬 but you cannot use this method to uninstall static modules (that is, modules declared in the configuration object when the store is created)

const store = new Vuex.Store({ / * option * / })

// Register the module 'myModule'
store.registerModule('myModule', {
  // ...
})
// Register a nested/myModule
store.registerModule(['nested'.'myModule'] and {// ...
})
Copy the code

💡 You can use the store.hasModule(moduleName) method to check whether the module has been registered with the store

🎬 When uninstalling a Module, you might want to keep the old state so that you can use the old local state when registering it again, such as from a server-side rendering application.

Local state can be archived when the module is registered by setting the option preserveState; that is, when the module is registered, its actions, mutations, and getters are added to the Store, but state is not. This assumes that the original state in the store already contains the module’s state and you don’t want to overwrite it

store.registerModule('a'.module, {
    preserveState: true 
});
Copy the code