Learn Vuex from scratch
I. Introduction
When our application encounters multiple components sharing state, it will require multiple components to depend on the same state or behaviors from different views need to change the same state. Previous workaround:
a. Define the data and the behavior of operating the data in the parent component;
b. Pass the data and the behavior of manipulating the data to the various sub-components required (multi-level transmission may be required)
The method of passing parameters will be very cumbersome for multi-level nested components, and cannot do anything for state transfer between sibling components. When building the following pages, you may experience a breakdown in the communication between vue components, especially the communication between non-parent and child components. At this point, vuex should be used to easily solve the communication problem between components.
2. What is Vuex
Vuex is a state management pattern developed for Vue.js applications. It uses a centralized store to manage the state of all components of the application, and uses corresponding rules to ensure that the state changes in a predictable manner. The key here is centralized storage management. Simply put, centralized management (read/write) of the shared state of multiple components in a vue application .
3. What is the principle of Vuex
1. Briefly introduce the principle of Vuex
Vuex implements a one-way data flow, and has a State to store data globally. When a component wants to change the data in the State, it must be done through Mutation. Mutation also provides a subscriber mode for external plug-ins to call to obtain the update of State data. When all asynchronous operations (common in calling the back-end interface to asynchronously obtain updated data) or batch synchronous operations need to go through Action, but Action cannot directly modify the State, it is still necessary to modify the data of the State through Mutation. Finally, according to the change of State, it is rendered to the view.
2. Briefly introduce the main functions of each module in the process:
- Vue Components: Vue components. On the HTML page, it is responsible for receiving interactive behaviors such as user operations, and executing the dispatch method to trigger the corresponding action to respond.
- dispatch: Action trigger method, which is the only method that can execute action.
- actions: Action behavior processing module, which $store.dispatch('action name', data1)is triggered by the component in the component. Then the call of mutation is triggered by commit(), which indirectly updates the state . Responsible for handling all interactions received by Vue Components. Contains synchronous/asynchronous operations, supports multiple methods with the same name, and is triggered in the order of registration. The operations requested to the background API are performed in this module, including triggering other actions and submitting mutations. This module provides a Promise package to support chained triggering of actions.
- commit: state change commit operation method. Committing a mutation is the only way to execute a mutation.
- mutations: state change operation method, triggered by actionscommit('mutation name') . is the only recommended way for Vuex to modify state. This method can only perform synchronous operations, and the method name can only be globally unique. There will be some hooks exposed during the operation for state monitoring and so on.
- state: The page state management container object. Centrally store the scattered data of the data object in Vue components, which is globally unique for unified state management. The data required for page display is read from this object, and Vue's fine-grained data response mechanism is used to perform efficient state updates.
- getters: state object read method. This module is not listed separately in the figure, and should be included in the render. Vue Components reads the global state object through this method.
4. When to use Vuex
While Vuex helps us manage shared state, it also comes with more concepts and frameworks. This requires a trade-off between short-term and long-term benefits.
If your application is simple enough, you'd better not use Vuex, because using Vuex can be tedious and redundant. A simple global event bus is enough for what you need. However, if you need to build a medium-to-large single-page application, you will likely be thinking about how to better manage state outside of components, and Vuex will be the natural choice.
5. Vuex installation (limited development environment to vue-cli)
First, you need to install the vue-cli scaffolding. For mainland users, it is recommended to set the npm registry source to a domestic mirror (Taobao mirror), which can greatly improve the installation speed.
npm config set registry https://[registry.npm.taobao.org](http://registry.npm.taobao.org/)
npm config get registry//After configuration, you can verify the success in the following ways
npm install -g npm --registry=[https://registry](https://registry/).npm.taobao.org
//npm install scaffolding
npm install -g vue-cli
vue init webpack my-vue
cd my-vue
npm install
npm run dev
After the scaffolding is installed, install vuex
npm install vuex --save
6. How to use Vuex
1. How to achieve the following effects through Vue?
This small demo is easy to implement with Vue. The core code is as follows:
<div class="hello">
<p>click {{count}} times,count is {{evenOrOdd}}</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementIfOdd">increment if odd</button>
<button @click="incrementAsync">increment async</button>
</div>
export default {
name: "HelloWorld",
data() {
return {
count: 0
};
},
computed: {
evenOrOdd() {
return this.count % 2 === 0 ? "even" : "odd";
}
},
methods: {
increment() {
this.count = this.count + 1;
},
decrement() {
this.count = this.count - 1;
},
// add 1 only for odd numbers
incrementIfOdd() {
if (this.count % 2 === 1) {
this.count = this.count + 1;
}
},
// add 1 after two seconds
incrementAsync() {
setInterval(() => {
this.count = this.count + 1;
} }, 2000);
}
}
}
2. How to transform the above code through Vuex?
① Create a store.js file
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {// Contains multiple objects that directly update the state function
INCREMENT(state) {
state.count = state.count + 1;
} },
DECREMENT(state) {
state.count = state.count - 1;
}
},
getters: { // Automatically call and return the property value when the property value is read
evenOrOdd(state) {
return state.count % 2 === 0 ? "even" : "odd";
} }
},
actions: { // Objects containing multiple corresponding event callback functions
incrementIfOdd({ commit, state }) { // conditional action
if (state.count % 2 === 1) {
commit('INCREMENT')
}
} },
incrementAsync({ commit }) { //Asynchronous action
setInterval(() => {
commit('INCREMENT')
} }, 2000);
}
}
})
export default store //Encapsulate the code with export default so that it can be referenced externally
② Introduce the store.js file into the main.js file
import store from './store'
new Vue({
el: '#app',
router,
store,//register the store on vuex: all component objects have one more attribute $store
components: { App },
template: '<App/>'
})
③ Create a new template HelloWorld.vue
<template>
<div class="hello">
<p>click {{count}} times,count is {{evenOrOdd}}</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementIfOdd">increment if odd</button>
<button @click="incrementAsync">increment async</button>
</div>
</template>
<script>
export default {
name: "HelloWorld",
computed: {
count() {
return this.$store.state.count;
},
evenOrOdd() {
return this.$store.getters.evenOrOdd;
}
},
methods: {
increment() {
this.$store.commit("INCREMENT");
},
decrement() {
this.$store.commit("DECREMENT");
},
// add 1 only for odd numbers
incrementIfOdd() {
This.$store.dispatch("incrementIfOdd"); //Trigger the corresponding action call in the store
},
// add 1 after two seconds
incrementAsync() {
this.$store.dispatch("incrementAsync");
}
}
};
</script>
Since the state in the store is responsive, when a Vue component reads the state from the store, if the state in the store changes, the corresponding component will be updated efficiently accordingly. Calling the state in the store in a component is as simple as returning it in a computed property. The only way to change the state in the store is to explicitly commit mutations.
3. How to optimize the above code through auxiliary functions such as mapState?
import { mapActions, mapGetters, mapState, mapMutations } from "vuex";
...
computed: {
...mapState(["count"]),
...mapGetters(["evenOrOdd"])
}
methods: {
...mapActions(["incrementIfOdd", "incrementAsync"]),
...mapMutations(["increment", "decrement"])
}
It must be noted that the increment function name in the HelloWorld.vue file must be consistent with the mutation in the store.js file before it can be written as...mapMutations(["increment", "decrement"]), for the same reason, incrementIfOdd and incrementAsync are also To be consistent with store.js file actions.
7. the use of Vuex attention points
1. How to pass parameters in Mutations
First add a parameter n to the add method in the store.js file
mutations: {
INCREMENT(state,n) {
state.count+=n;
},
DECREMENT(state){
state.count--;
}
}
Then modify the parameters passed by the button's commit() method in HelloWorld.vue
increment() {
return this.$store.commit("INCREMENT",2);
},
decrement() {
return this.$store.commit("DECREMENT");
}
2. How to understand getters
Getters means to obtain from the surface, which can be regarded as a kind of re-editing before the data is obtained, which is equivalent to a filtering and processing of the data . Getters are like computed properties, the return value of a getter is cached according to its dependencies and only recalculated when its dependencies change.
For example: to operate on the count in the store.js file, add 100 to it before outputting it.
First, introduce getters in Vuex.Store() in store.js
getters:{
count:state=>state.count+=100
}
Then configure the computed in HelloWorld.vue. There can only be one computed property in the vue constructor. If you write more than one, only the last computed property is available, so use the spread operator "..." to write the previous section. The computed property does a makeover.
computed: {
...mapGetters(["count"])
}
3. The difference between actions and mutations
Actions are basically the same as the above Mutations, the difference is that actions change the state asynchronously, while Mutations change the state synchronously .
The meaning of synchronization is that each mutation can correspond to a new state after the execution is completed (same as reducer), so that devtools can take a snapshot and save it, and then it can be time-traveled at will. If you open the devtool to call an asynchronous action, you can clearly see when the mutation it calls was recorded, and you can immediately check their corresponding status.