Vuex is for vue.js applicationsState management mode + library. It acts as a centralized store for all the components in your application, and its rules ensure that state can only change in predictable ways.

Using Vuex doesn’t mean you shouldallAll of the states are placed in Vuex. While putting more state into Vuex makes your state changes more unambiguous and debugable, it can sometimes make your code more verbose and indirect. If a piece of state belongs strictly to a single component, it is ok to keep it as local state. You should weigh the pros and cons and make a decision that suits your application development needs.

The official website

Vuex video teaching interested can have a look

The installation

npm

npm install vuex@next --save
Copy the code

yarn

yarn add vuex@next --save
Copy the code

State

Data stored in Vuex is similar to a data warehouse

Start by creating a simple Vuex repository

Import {createApp} from 'vue' import {createStore} from 'vuex' // Creates a new storage instance. const store = createStore({ state () { return { count: 0 } }, mutations: {increment (state) {state.count++}}}) const app = createApp({/* Your root component */})Copy the code

Use in pages

Computed: {count () {return store.state.count // Every time it changes, it causes the calculated properties to be reevaluated and triggers an associated DOM update. },}, methods: {increment() {this. use code.store.com ('increment')Copy the code
The above approach is useful, but when a component needs to use multiple stored state properties or getters, declaring all of these computed properties can become repetitive and tedious. To solve this problem, we can usemapStateThe helper generates computed getters for us to avoid these problems:
import { mapState } from 'vuex' export default { computed: mapState({ count: State =>state.count, // Pass string value "count" as "state=>state.count" same as countAlias: CountPlusLocalState (state) {return state.count + this.localcount}})}Copy the code

Use the object expander in the page to blend properties into external objects

computed: {
  ...mapState({
    // ...
  })
}

Copy the code

Getter

Vuex allows us to define “getters” (you can think of them as computed properties of the store) in the store. Just like evaluating properties, the return value of a getter is cached based on its dependency and is recalculated only if its dependency value changes.

Sometimes the data in the warehouse is not exactly what we need, so we will filter it

DoneTodosCount (){this.$store.state.todos = this.$store.state.todos.map(e=> e.id); } // Use computed correctly: {doneTodosCount () {return this.$store.state.todo.filter (todo => todo.done).length}}Copy the code
If more than one component needs to use it, we can either copy the function or extract it into a shared helper and import it in multiple locations — neither is ideal. Vuex allows us to define “getters” in stores. You can think of them as computed properties of the repository.

Attribute access

const store = createStore({ state: { todos: [ { id: 1, text: '...', done: true }, { id: 2, text: '...', done: False}]}, getters: {doneTodos (state) {return state.todos.filter(todo => todo.done)}}}) // Computed attributes in pages: { doneTodosCount () { return this.$store.getters.doneTodosCount } }Copy the code

Method access

getters: { // ... getTodoById: (state) = > (id) = > {return state. Todos. Find (todo = > todo. Id = = = id)}} / / accept parameters to filter the store. The getters. GetTodoById (2) / / return - > {  id: 2, text: '... ', done: false }Copy the code

The mapGetters helper simply maps the stored getters to local computed properties:

import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])
  }
}
Copy the code

If you want to map getters to other names, use the object:

. MapGetters ({/ / will enclosing doneCount "pointing ` enclosing store. The getters. DoneTodosCount" doneCount: 'doneTodosCount})Copy the code

Mutations(synchronous)

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 stringEvent type (Type)And aCallback function (handler). This callback is where we actually make the state change, and it accepts state as the first argument, and in Vuex,Mutation are all synchronous transactions:
const store = createStore({ state: { count: 1 }, mutations: Increment (state) {// mutate state state.count++}}}) // Use store.mit ('increment') // similar to passing increment to trigger increment This functionCopy the code
Additional parameters can also be passed to store.mit
//state is the vuex repository object state. //n is the argument passed when the method is triggered to do some operations; Mutations: {increment (state, n) {state.count += n}}Copy the code
You can also submit it as an object and of course it has the same effect
store.commit({
  type: 'increment',
  amount: 10
})
Copy the code
Replace Mutation event types with constants
Whether or not to use constants is largely a matter of preference — it helps in large projects with lots of developers, but it’s entirely optional if you don’t like them.
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
Copy the code
// store.js
import { createStore } from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = createStore({
  state: { ... },
  mutations: {
    // we can use the ES2015 computed property name feature
    // to use a constant as the function name
    [SOME_MUTATION] (state) {
      // mutate state
    }
  }
})
Copy the code

Action(asynchronous)

Action is similar to mutation, except that:
  • The Action commits mutation rather than a direct state change.
  • Actions can contain any asynchronous operation.

Let’s register a simple action:

const store = createStore({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment ({commit}) {
      commit('increment')
    }
  }
})
Copy the code
The Action bystore.dispatchMethod trigger:
store.dispatch('increment')
Copy the code

At first glance, it seems unnecessary. Wouldn’t it be easier to just distribute mutation? In fact, this is not the case. Remember that mutation must implement this limitation synchronously? Action is not bound! We can perform asynchronous operations within an action:

actions: {
  increment ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}
Copy the code

Actions support the same payloads and objects for distribution:

// Distribute store.dispatch('incrementAsync', {amount: 10}) // distribute store.dispatch({type: 'incrementAsync', amount: 10})Copy the code

Let’s look at a shopping cart example

actions: {checkout ({commit, state}, products) {const savedCartItems = [...state.cart.added] Commit (types.checkout_request) // The shopping API returns a successful callback and a failed callback shop.buyProducts(products, CHECKOUT_SUCCESS () => commit(types.checkout_failure, savedCartItems))}}Copy the code

Combination of the Action

Actions are usually asynchronous, so how do you know when an Action ends? More importantly, how can we combine multiple actions to handle more complex asynchronous processes?

First, you need to understand that store.dispatch can handle the Promise returned by the handler of the triggered action, and that store.dispatch still returns promises:

actions: { actionA ({ commit }) { return new Promise((resolve, reject) => { setTimeout(() => { commit('someMutation') resolve() }, 1000)})}} / / page trigger store. Dispatch (' actionA). Then (() = > {/ /... })Copy the code

If we use async/await we can combine actions as follows:

actions: { async actionA ({ commit }) { commit('gotData', await getData()) }, async actionB ({ dispatch, Commit}) {await dispatch('actionA') // await actionA to complete commit('gotOtherData', await getOtherData())}}Copy the code