preface

Writing project for a long time, occasionally use Vuex is also made of some very simple functions, is simply store the user information, and use it every time you take a few deep, now settled down to want to write a program for oneself, in the process of writing, consolidate ever ignore the basic knowledge of learning: by the way, this article is to record the knowledge of learning Vuex, Since it is to consolidate knowledge, that those basic brushstrokes directly over.

This article is mainly object-oriented beginners, so the wording is more inclined to be easy to understand, so it is suggested that after understanding or go to see the official documents, familiar with the professional terms, and this article is mainly selected to speak, you can also go to see the official documents to fill gaps.

introduce

What is Vuex?

In daily project development, we often encounter some variables that need global storage and need to be used in multiple places, such as user information, shopping cart, etc. Before, the solution we adopted may be to set up public components or use cookies or localstorage and other localstorage methods for storage. However, this method will undoubtedly bring many disadvantages, such as:

  • Frequent references are required across multiple modules
  • The storage format is limited, and format conversion is required for the value
  • The storage structure is not clear
  • It’s not reactive
  • Unable to form a unified specification, accepting other people’s code takes time to understand
  • Unable to trace changes to values

Vuex can solve all these problems for us. Let’s take a look at the official explanation of Vuex

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

Vuex is a public state repository that can only be used in vuue.js. This repository can store global variables that you use frequently, and specify a specified way for you to modify variables in the repository, and if you modify variables in this way, then all changes can be saved.

Installation and use

Like Vue, we can also use Vuex through script tag introduction and module installation. Usually we create a new Vue project through VUUE – CLI. If you choose this, VUUE – CLI will automatically install and import Vuex for our new project. The rest of the lecture will expand in this context.

Vuex core concepts

The paper

Vuex’s core concepts are listed below, including State, Getters, Mutations, and Actions. Let’s take a closer look at each of them.

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {},mutations: {},actions: {
  },
  getters
})

Copy the code

state

State is where we store data in Vuex. Data in State, like data in Vue instances, must exist as key-value pairs.

We can define a data in state beforehand

export default new Vuex.Store({
  state: {
    count: 0}})Copy the code

Since we have previously “injected” the root component into each of the child components using the Store option in the Vue instance, we can access the contents of the Store instance through this.$store in all the child components

There is an official recommended use of Vuex, because Vuex’s state store is reactive, so Vuex encourages us to use the computed properties of Vue to read state from store instances

<template>
  <div id="app">
    <div class="message">{{ count }}</div>
  </div>
</template>

<script>
export default {
  name: 'App'.computed: {
    count () {
      return this.$store.state.count
    }
  }
}
</script>
Copy the code

Some students will choose to read the value of state in data

<template>
  <div id="app">
    <div class="message">{{ count }}</div>
  </div>
</template>

<script>
export default {
  name: 'App',
  data () {
    return {
      count: this.$store.state.count
    }
  }
}
</script>
Copy the code

This looks fine, but there is a danger that fetching a value from a Store instance in data is not reactive. The value in data only stores the literal of the corresponding value in the store instance at the time of creation. Any subsequent changes to the value will not change the value in data. Let’s look at an example.

<template>
  <div id="app">
    <div class="message">{{ count }}</div>
  </div>
</template>

<script>
export default {
  name: 'App',
  data () {
    return {
      count: this.$store.state.count
    }
  },
  mounted () {
    setTimeout(() = > {
      this.$store.commit('increment'.10)},2000)}}</script>
Copy the code

As you can see, although we changed the value in the Store instance two seconds after entering the page, our page did not follow.

Of course, there is no official recommendation to use it directly in templates, although doing so can work just as well. I don’t know why, but those of you who know the difference can leave a comment in the comments section.

<template>
  <div id="app">
    <div class="message">{{ this.$store.state.count }}</div>
  </div>
</template>
Copy the code

getters

Getters official said relatively simple, here directly moved to show.

Sometimes we need to evaluate and display the values, such as filtering and counting lists

computed: {
  doneTodosCount () {
    return this.$store.state.todos.filter(todo= > todo.done).length
  }
}
Copy the code

If more than one component needs this property, we can either copy the function or extract a shared function and import it in multiple places — neither approach is ideal.

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.

The Getter accepts state as its first argument:

const store = new Vuex.Store({
  state: {
    todos: [{id: 1.text: '... '.done: true },
      { id: 2.text: '... '.done: false}},getters: {
    doneTodos: state= > {
      return state.todos.filter(todo= > todo.done)
    }
  }
})
Copy the code

Getters can also accept other getters as second arguments:

getters: {
  // ...
  doneTodosCount: (state, getters) = > {
    return getters.doneTodos.length
  }
}
Copy the code

Then we can use it directly in the component

computed: {
  doneTodos () {
    return this.$store.getters.doneTodos
  },
  doneTodosCount () {
    return this.$store.getters.doneTodosCount
  }
}
Copy the code

Sometimes we want to pass an argument to a getter so that it returns different data for our different needs.

getters: {
  // ...
  getTodoById: (state) = > (id) = > {
    return state.todos.find(todo= > todo.id === id)
  }
}
Copy the code
store.getters.getTodoById(2) // -> { id: 2, text: '... ', done: false }
Copy the code

Mutations

Basic use of Mutation

Note: To prevent misunderstanding, every function in Mutatios is called a Mutation, so when we say Mutation, we mean a function in Mutations

As mentioned above, Vuex gave us a specified way to modify the variables in the warehouse, namely the values in state, that is, to commit mutation. Because Vuex is also integrated into DevTools, the official debugging tool for Vue, it is very easy to see the values and changes in Vuex through DevTools.

Once vuE-DevTools is installed, open the console, go to the Vue TAB, and click the second icon to start debugging Vuex.

How do we change the state value using 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 accepts state as the first argument, as well as an optional second:

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    testMutation (state, obj) {
      state.count = obj.num
    }
  }
})
Copy the code

The commit method takes two arguments: the first is the name of the mutation to be triggered, and the second is the parameter we are passing (optional). If we are passing multiple arguments, You can pass multiple parameters into a single object

this.$store.commit('increment', {num: 10.remarks: 'test' })
Copy the code

And, of course, we have another way of writing it, which is exactly the same thing except for the style

this.$store.commit({
  type: 'increment'.num: 10.remarks: 'test'
})
Copy the code

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:

  1. It is best to initialize all required properties in your store in advance.
  2. When you need to add new properties to an object, you should
  • Use vue.set (obj, ‘newProp’, 123), or
  • Replace an old object with a new one. For example, with the object expansion operator we can write:
state.obj = { ... state.obj,newProp: 123 }
Copy the code

Mutation and devtools

In the example above, we already know how to view the value in the store instance through DevTools, and we also said that changing the value of state through the official specified operation will leave an operation record. So what does the operation record look like?

  state: {
    count: 5
  },
  mutations: {
    increment (state) {
      // Change the state
      state.count++
    }
  }
Copy the code
  mounted () {
    setInterval(() = > {
      this.$store.commit('increment', {
        remarks: 'Home page counter'})},5000)}Copy the code

As you can see, every time we call commit, we can leave a record of the operation. The record contains the Mutation we triggered, the time when we triggered it, and the state after it was triggered. We can even leave some ‘registration information’ in it, so that we can debug when we don’t know which page the value was modified by. Click on any record to view the details of the trigger.

When we click the small icon, the state of the store instance referenced on the debugging tool including the page will immediately change back to the state of the store instance after triggering the Mutation, and we can also click the latest record at any time to return to the latest state.

Do not perform assignments directly

See if calling commit to trigger Mutation would be convenient for debugging, but what if we used direct assignment instead?

  mounted () {
    setInterval(() = > {
      this.$store.state.count++
    }, 5000)}Copy the code

Although the value in the Store instance did change, there was no record of the operation, and we could not see the latest state value in the debug record, which would be bad for our debugging, and bad for our code semantics, which would be harder to read and understand.

Strict mode

If you want to change the value of state only with Mutation in your project, you can turn on strict mode. In strict mode, an error is thrown whenever a state change occurs that is not caused by a mutation function. This ensures that all state changes are tracked by the debugging tool.

To turn on strict mode, simply pass strict: true when creating a store

const store = new Vuex.Store({
  // ...
  strict: true
})
Copy the code

Do not enable strict mode in a release environment! Strict mode deeply monitors the status tree to detect non-compliant state changes — be sure to turn off Strict mode in a release environment to avoid performance losses.

Similar to plug-ins, we can have build tools handle this situation:

const store = new Vuex.Store({
  // ...
  strict: process.env.NODE_ENV ! = ='production'
})
Copy the code

When Vuex performs an asynchronous operation in Mutation, it will not know when the operation will be completed and will not be able to save the correct data in the operation record, which is contrary to the original intention of Mutation design

  mutations: {
    increment (state) {
      // Change the state
      setTimeout(() = > {
        state.count++
      })
    }
  }
Copy the code

As you can see, although the data store instance can change accordingly, but every time we modify the content of operating records but not in the record to the correct value, so we can only be done in Mutation synchronous operation, asynchronously that if we want to do, for example, I want to put the login operation Vuex in how to do? Vuex also provides us with the next core concept for asynchronous operation — Action

actions

Note: To prevent misunderstanding, each function in actions is called an action, so when we say an action we mean a function of the action

Here the official document content is relatively simple to understand, directly moved here.

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 = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      setTimeout(() = > {
        context.commit('increment')},2000)}}})Copy the code
this.$store.dispatch('increment')
Copy the code

The Mutation name can be the same as the Action name because the Mutation and Action are triggered differently

Since any asynchronous operation can take place in an action, we can call Mutation after an asynchronous operation (such as calling an API interface) in the action.

The first parameter of the action is state. The first parameter of the action is context. The first parameter of the action is context. The same thing is that they can both receive the user’s incoming parameter with the second parameter.

Consolidate the basic knowledge of Vue to have a comprehensive understanding of Vuex, more understandable than the official (ii)