[Spring Boot + Vue]: VueX state management library

For componentized development, the state of a large application often spans multiple components. It is very troublesome to transfer state between multi-layer nested parent-child components, and Vue does not provide a method for sibling components to directly share data.

Based on this problem, many frameworks provide solutions: use a global state manager, and hand over all decentralized shared data to the state manager for safekeeping, and Vue is no exception.

VueX is a state management library specially developed for Vue.js applications, which uses centralized storage to manage the state of all components of the application.

VueX is used to manage data scattered in various components of Vue. Data transfer between sibling components.

There are two versions of vuex, vuex3 corresponds to vue2; vuex4 corresponds to vue3

Two versions are installed:

vuex3 installation: npm install vuex@3

vuex4 installation: npm install vuex

Official website:
https://v3.vuex.vuejs.org/zh/

About state management:

Generally, VueX is only used in complex applications, and it is not necessary.

The core of every VueX application is a store (warehouse) global object, which is different from ordinary global objects. Based on the characteristics of Vue data and view binding, when the state (data) in the store changes, the binding Views are also re-rendered.

The state in the store is not allowed to be modified directly. The only way to change the state in the store is to explicitly submit (commit) the mutation, which can easily track each state change.

In large and complex applications, if the state changes cannot be effectively tracked, it will cause great trouble to understand and maintain the code

There are 5 important concepts in VueX: State, Getter, Mutation, Action, Module

Demonstration below

Create a new project based on Vue2: see Note (9) for details on how to create it

vue create vuex-demo

Drag the newly created project vuex-demo into Visual Studio Code

vuex3 installation: npm install vuex@3

Create a new store folder under the src directory, create a new index.js file, and provide an initial state object and some mutations. The file code is as follows:

import Vue from 'vue'
import Vuex from 'vuex'
Vue. use(Vuex)

const store = new Vuex. Store({<!-- -->
 state: {<!-- -->
 count: 0
 },
 mutations: {<!-- -->
 increment (state) {<!-- -->
 state.count++
 }
 }
})

Explanation: A store (warehouse) is established, and the default count initial value is: 0; in the future, the state is the state, which is called for each component.

The state of the state cannot be changed directly. If you need to modify it, you need to call the mutations method (this method is used for + 1 operation).

Then export the store so that it can be used in main.js

export default store


Now I want to call the value of count in the component, the specific operation is as follows:

1. Since we created Vue in main.js, we can import the newly created index.js in main.js

import store from './store/index.js'

Since index.js is used as the file name, it can also be shortened and omitted

import store from './store'

Then use a mechanism provided by Vuex to “inject” the store from the root component to all child components as a store option:

new Vue({<!-- -->
 render: h => h(App),
 store: store
}).$mount('#app')
After doing this, you can use the store object to get the value in any component.
2. Create a new Test.vue component with the following code:
<template>
 <div>
 </div>
</template>
<script>
export default {<!-- -->
 name:'Test'
}
</script>

Register in the root component App.vue

Take out the value of count in this Test.vue.

{<!-- -->{<!-- --> this.$store.state.count }}


After running the project, the browser can display the initial value of count: “0”. At this time, the Test.vue component has successfully called the global state value.

How to modify the status code? Add a button to add 1.

<button @click="add"> + 1</button>

Define the add method:

methods:{<!-- -->
 add(){<!-- -->
 this.$store.commit("increment")
 }
 }


Again, we commit mutations rather than changing store.state.count directly because we want to track state changes more explicitly.

Here, the increment method in the store in index.js is called to modify the operation, instead of using this.

the s

t

o

r

e

.

the s

t

a

t

e

.

c

o

u

no

t

=

t

h

i

the s

.

store.state.count = this.

store.state.count = this.store.state.count + 1

Click the + 1 button in the browser to change the state

Optimization 1: By setting a method, you can directly call {{ count }}

computed: {<!-- -->
 count () {<!-- -->
 return this.$store.state.count
 }
 },


Optimization 2: mapState auxiliary function generates computed properties

When a component needs to get multiple states, declaring these states as computed properties is a bit repetitive and redundant. To solve this problem, we can use the mapState helper function to help us generate computed properties, saving you a few keystrokes.

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

// The helper function in the separately built version is Vuex.mapState
import {<!-- --> mapState } from 'vuex'
computed: mapState([
 // Map this.count to store.state.count
 'count'
])


Getter
Sometimes we need to derive some states from the state in the store, such as filtering and counting state data.

Let’s demonstrate below, adding another todos to-do list in the state. Among them, eating has been completed; sleeping has not been completed.

todos: [
 {<!-- --> id: 1, text: 'eating', done: true },
 {<!-- --> id: 2, text: '.sleep', done: false }
 ]


Suppose it is to be displayed on the Test.vue component, now map the todos over, and then it can be used.

Now suppose you just want to display the actions that have been completed, how to do it?

This requires filtering on todos. Add a getters method in index.js (note: add a comma “, ” between the two methods)

getters: {<!-- -->
 doneTodos: state => {<!-- -->
 return state.todos.filter(todo => todo.done)
 }
 }


Then you can use the mapGetters helper function in the Test.vue component to map.

Since we have already used mapState, and now we need to use another method mapGetters at the same time, we need to make some changes to the code:

computed:{<!-- -->
 ...mapState([
 'count','todos'
 ]),
 ...mapGetters([
 'doneTodos'
 ])
 },


At this time, the browser filters out the data whose done status is true

The Mutation, Action, and Module below can be better understood in the example explanations behind, and this is only for understanding.

  • Mutation
    The only way to change the state in Vuex’s store is to submit a mutation.

The method of submitting Mutation in the component, the code is as follows. No more demonstrations here, you can try it yourself if you are interested.

import {<!-- --> mapMutations } from 'vuex'

export default {<!-- -->
 //...
 methods: {<!-- -->
 ...mapMutations([
 'increment', // maps `this.increment()` to `this.$store.commit('increment')`


 // `mapMutations` also supports payloads:
 'incrementBy' // maps `this.incrementBy(amount)` to `this.$store.commit('incrementBy', amount)`
 ]),
 ...mapMutations({<!-- -->
 add: 'increment' // maps `this.add()` to `this.$store.commit('increment')`
 })
 }}
  • action
    Action is used when handling mixed asynchronous calls in mutation. (In Vuex, mutations are all synchronous transactions 🙂

First register a simple action:

const store = new Vuex. Store({<!-- -->
 state: {<!-- -->
 count: 0
 },
 mutations: {<!-- -->
 increment (state) {<!-- -->
 state.count++
 }
 },
 actions: {<!-- -->
 increment (context) {<!-- -->
 context.commit('increment')
 }
 }})

Distribute Action in components, similar to before: use mapActions in methods

import {<!-- --> mapActions } from 'vuex'

export default {<!-- -->
 //...
 methods: {<!-- -->
 ...mapActions([
 'increment', // maps `this.increment()` to `this.$store.dispatch('increment')`

 // `mapActions` also supports payloads:
 'incrementBy' // maps `this.incrementBy(amount)` to `this.$store.dispatch('incrementBy', amount)`
 ]),
 ...mapActions({<!-- -->
 add: 'increment' // maps `this.add()` to `this.$store.dispatch('increment')`
 })
 }}
  • Modules
    Due to the use of a single state tree, all the state of the application will be concentrated into a relatively large object. When the application becomes very complex, the store object can become quite bloated.

To solve the above problems, Vuex allows us to split the store into modules. Each module has its own state, mutations, actions, getters, and even nested submodules – split the same way from top to bottom.

const moduleA = {<!-- -->
 state: () => ({<!-- --> ... }),
 mutations: {<!-- --> ... },
 actions: {<!-- --> ... },
 getters: {<!-- --> ... }}

const moduleB = {<!-- -->
 state: () => ({<!-- --> ... }),
 mutations: {<!-- --> ... },
 actions: {<!-- --> ... }}

const store = new Vuex. Store({<!-- -->
 modules: {<!-- -->
 a: moduleA,
 b: moduleB
 }})

store.state.a // -> state of moduleA
store.state.b // -> state of moduleB

Eventually all the modules have to be put together. When we create Vuex.Store, we name the module through the modules parameter, for example: name moduleA a .

Add a when you need to access the module. store.state.a // -> state of moduleA.

In fact, it is to divide and manage the modules, such as: the status of the user module, the status of the order module, etc. are set separately.

The above is only the basic concept of Vuex, and it will be demonstrated in detail in the following comprehensive cases.