[toc]
Before writing this article, WHEN using Vuex for development, I always had a vague understanding of it. If NOT, I would just copy and paste it and run it. I didn’t know much about the details inside.
Today, I checked a lot of materials by taking advantage of the project, so I want to make Vuex clear from beginning to end.
Before reading this article, I hope you have some basic knowledge of Vue and Vuex. If not, please go to Vue official website
It is inevitable that there are mistakes in the writing, you don’t mind, I hope to point out mistakes ~~
A, the state
Take a look at the standard Store directory structure
Once in VUEX, we need to define variables in state, similar to data in VUE, to store the shared 1 state through state
-store
--actions
--mutations
--getters
--mutations-type
--index.js
Copy the code
Index.js is the general entry point
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import state from './state.js'
import actions from './actions.js'
import mutations from './mutations.js'
import getters from './getters.js'
export default new Vuex.Store({
state,
actions,
mutations,
getters
})
Copy the code
We define variables in state.js
export default {
userInfo: {userName:"Fried Pikachu.".age:"22".sex:"Female"
},
token:"aerfrftregtyrb.rytyuhjyi".friends: ["Tom"."Herry"."Lucy"].likes: ["Eat".'sleep'.'Go to the movies'].money:250
}
Copy the code
To use in components, customize two components in app.vue
<div id="app">
<about> </about>
<home> </home>
</div>
Copy the code
About. Vue
<h1>{{$store.state.userInfo.userName}}</h1> // Deep-fried pikachuCopy the code
The contents of home.vue
<h1>{{$store.state.userInfo.age}}</h1> // 22Copy the code
With Vuex, we don’t have to worry about passing values between components. We can get different data directly from $store
But if we need more than one data in VUEX, and this is too verbose, we can define it in computed.
Initialization of props, Methods,data, and computed is done between beforeCreated and created.
For example,
<template>
<div class="home">
{{userName}}
</div>
</template>
<script>
export default {
name: 'home'.computed: {userName(){
return this.$store.state.userInfo.userName
}
}
}
</script>
Copy the code
That’s a lot easier to introduce.
1.2 mapState auxiliary function
Although it is very convenient to incorporate values in state into a computed object using this.$store.state, the following happens if you take multiple values
computed:{
userInfo(){
return this.$store.state.userInfo
},
token(){
return this.$store.state.token
},
friends(){
return this.$store.state.friends
},
likes(){
return this. $store. State. Likes},... }Copy the code
If there are too many, it can be very troublesome, and vuex provides us with a much simpler method called mapState,
This method automatically maps the required state value to the calculated properties of the instance
We could write it like this
import {mapState} from 'vuex'
export default {
name: 'home'.computed: mapState(['likes'.'friends'.'token'.'userInfo'])}Copy the code
mapState(['likes'.'friends'.'token'.'userInfo']) // Which fields you fill in will automatically map those fields to a calculated property
Copy the code
so
mapState(['likes'.'friends'.'token'.'userInfo'])
Copy the code
Equivalent to the following code
userInfo(){
return this.$store.state.userInfo
},
token(){
return this.$store.state.token
},
friends(){
return this.$store.state.friends
},
likes(){
return this. $store. State. "likes"},Copy the code
Remember: when using helper functions such as mapState, the preceding method name is the same as the name of the fetched property.
What if we need to customize a calculated property? How do I add it?
After all, computed: mapState([‘likes’,’friends’,’token’,’userInfo’])
This is where we need the es6 expansion operator:… . Append the computed properties of the mapState map instead of overwriting the existing ones
computed: {
value(){
return this.val++ }, ... mapState(['likes'.'friends'.'token'.'userInfo'])}Copy the code
Used in templates
<h2>{{userInfo.userName}}</h2> // Deep-fried pikachuCopy the code
About the alias
Sometimes, when we are mapping, we want to give a new name to the computed property instead of the original property name. We can alias the property as an object
computed: {
value(){
return this.val++ }, ... mapState({myLikes:"likes".myFriends:"friends".theUserInfo:"userInfo"})}Copy the code
So we can use it in the template
<h2>{{theUserInfo.userName}}</h2> // Deep-fried pikachuCopy the code
Second, the getters
Getters is the equivalent of a calculated property in vUE that gets the latest value in state
And getters allows arguments, the first of which is state
So, with getters, we get the value we want,
getters.js
export default{
realName:(state) = >{
return state.userInfo.userName+' '
},
myMoney:(state) = >{
return (state.money/7).toFixed(2)}}Copy the code
We can use this in our example
computed: {
valued(){
return this.value++
},
realName:this.$store.getters.realName,
myMoney:this.$store.getters.myMoney,
}
Copy the code
2.2 mapGetters auxiliary function
The mapGetters function has the role of mapState, and its main usage is the same, mapping the properties of getters to the calculated properties of the instance
computed: {
valued(){
return this.value++ }, ... mapGetters(['realName'.'myMoney'])}Copy the code
You can also take an alias
computed: {
valued(){
return this.value++ }, ... mapGetters({myRealName:"realName".myFormatMoney:"myMoney"})}Copy the code
But one has to ask, they’re both really the same thing, right? Which one? Or is there any real difference?
As mentioned above, getters can pass parameters, which is the difference between getters and mapState
So how do you pass in the parameters?
The first argument to getters is state
Here’s a scenario where we don’t want to take all the friends values in the state, but rather we want to filter them based on the values we pass in
So all we have to do is put a layer of function on the value that’s returned from the getter and then we can do that
export default{
realName:(state) = >{
return state.userInfo.userName+' '
},
myMoney:(state) = >{
return (state.money/7).toFixed(2)},myFriend:(state) = >{
// Returns a function to receive the filter value we pass in
return function (friendName) {
return state.friends.find(item= > item === friendName)
}
}
}
Copy the code
Obviously, getters can not only get the value in state like mapState, but also do what we want to do with the value before we get it
To “derive” new values to meet our different business needs
Not surprisingly, if you don’t need to derive new values,
thisThe.$store.getters. Value is equal tothis. $store. State. The valueCopy the code
Third, mutation
We need some mutations for the definition in the code, which is similar to methods in VUE,
Mutations require a commit to call its methods, and it can also pass in parameters, the first of which is state, and the second is payLoad, which is an additional parameter
We can only change the value of state using mutation
mutations.js
export default {
addAge:(state,payLoad) = >{
state.userInfo.age+=payLoad.number
},
changeName:(state,payLoad) = >{
state.userInfo.userName+=payLoad.userName
},
addFriend:(state,payLoad) = >{
if(state.friends.indexOf(payLoad.newFriend) < 0){
state.friends.push(payLoad.newFriend)
}
},
setUserInfo:(state,payLoad) = >{
state.userInfo=payLoad.userInfo
},
setToken:(state,payLoad) = >{
state.token=payLoad.token
}
}
Copy the code
Used in templates
<div class="home"> < button@click ="handleAddAge"> Add a new friend </ button@click ="handleAddFriend">Copy the code
Js part
methods:{
handleAddAge(){
Instead of calling state and getters directly, commit mutation with the commit keyword
this.$store.commit('addAge', {number:1})},handleAddFriend(){
let name="Pikachu";
this.$store.commit('addFriend', {newFriend:name
})
}
}
Copy the code
The second argument is best written as an object when called, so we can pass more information.
this.$store.commit('mutationName', {key1:val1,
key2:val2,
key3:val3
})
Copy the code
However, this writing will still encounter the same problem, that is, if you need to operate multiple data, it will become troublesome, at this time we need mapMutations, through it to map the method
3.1 mapMutations auxiliary function
Same thing as mapState, mapGetters
The difference is that mapMutations maps all the methods in mutations to the methods in instance methods
So we can use it this way
methods:{ ... mapMutations(['addAge'])}Copy the code
MapMutations ([‘addAge’]) is equivalent to the following code
methods: {addAge(payLoad){
this.$store.commit('addAge',payLoad)
}
}
Copy the code
Parameters we can write when this method is called
<button @click="AddAge({number:1})"</button>Copy the code
You can also have aliases
methods:{ ... mapMutations({handleAddAge:'addAge'})}Copy the code
<button @click="handleAddAge({number:1})"</button>Copy the code
At this time, some people will say, why should I go around and change state from mutations? Can I just change state?
Like this:
addAge(){
this.$store.state.userInfo.age +=5;
}
Copy the code
Actually, the result is ok, so why do I transfer it from mutations?
Here’s why:
-
Mutations does more than just do assignments
-
Vue. Js has a similar buried point operation in mutations. If it is operated from mutations, it can be detected, and it can be more convenient to use debugging tools, which can detect real-time changes, while directly changing the properties in state cannot be monitored in real time
Note: Mutations can only write synchronous methods, not asynchronous, such as AXIos, setTimeout, etc., which cannot be written. The main function of mutations is to modify state.
The reason is similar: if written asynchronous in mutations, it can also be successfully adjusted, but because it is asynchronous, it cannot be traced by debugging tools, so it is not recommended to write so, which is not conducive to debugging, which is the official agreement.
3.2 Replacing Mutation event types with constants
Converts the original method name from a string to a constant
mutations.js
const ADD_AGE ='addAge'
const CHANGE_NAME ='changeName'
const ADD_FRIEND='addFriend'
const SET_USER_INFO='setUserInfo'
const SET_TOKEN='setToken'
// Then replace the old method name with a constant
export default {
[ADD_AGE](state,payLoad){
state.userInfo.age+=payLoad.number
},
[CHANGE_NAME](state,payLoad){
state.userInfo.userName+=payLoad.userName
},
[ADD_FRIEND](state,payLoad){
if(state.friends.indexOf(payLoad.newFriend) < 0){
state.friends.push(payLoad.newFriend)
}
},
[SET_USER_INFO](state,payLoad){
state.userInfo=payLoad.userInfo
},
[SET_TOKEN]:(state,payLoad) = >{
state.token=payLoad.token
}
}
Copy the code
Why do YOU write that?
- It is not easy to write wrong, strings are easy to write wrong, and when a string is written wrong, it will not report an error position. Instead, it will use a constant. If it is written wrong, ESLint will indicate an error position
- When using an Action mutation, use the same constants in the action to avoid hand-slip-write errors
Replace mutations with constants. We can create a new file (mutation_type. Js) that stores these constants
Mutation_type. Js part
const ADD_AGE ='addAge'
const CHANGE_NAME ='changeName'
const ADD_FRIEND='addFriend'
const SET_USER_INFO='setUserInfo'
const SET_TOKEN='setToken'
export {
ADD_AGE,
CHANGE_NAME ,
ADD_FRIEND,
SET_USER_INFO,
SET_TOKEN
}
Copy the code
And then it’s introduced in retro.js
import {
ADD_AGE,
CHANGE_NAME,
ADD_FRIEND,
SET_USER_INFO,
SET_TOKEN
} from "./mutation_type.js"
export default {
[ADD_AGE](state,payLoad){
state.userInfo.age+=payLoad.number
},
[CHANGE_NAME](state,payLoad){
state.userInfo.userName+=payLoad.userName
},
[ADD_FRIEND](state,payLoad){
if(state.friends.indexOf(payLoad.newFriend) < 0){
state.friends.push(payLoad.newFriend)
}
},
[SET_USER_INFO](state,payLoad){
state.userInfo=payLoad.userInfo
},
[SET_TOKEN]:(state,payLoad) = >{
state.token=payLoad.token
}
}
Copy the code
Fourth, the actions
Action is similar to mutation
Just a few things to keep in mind:
- The action can commit mutation and then mutation to change the state
- Action does not operate on state directly, but mutation
- Actions contain asynchronous operations, similar to AXIOS requests, that can be written in the action
- Methods in action are asynchronous by default and return promises
Why is that? Because it’s Vuex
actions.js
import {
ADD_AGE,
CHANGE_NAME,
ADD_FRIEND,
SET_USER_INFO,
SET_TOKEN
} from "./mutation_type.js"
export default{
// Define an action that asynchronously retrieves user information
async getUserInfo(context){
// Context can be understood as an object of the entire Store. Similar to this.$store, which contains state, getter, mutations, and actions
const res = await axios.get('/ interface url')
// mutation_type. Js is used
context.commit( SET_USER_INFO,{userInfo:res.userData}
)
},
// Define an action that asynchronously obtains the user's token
async getToken(context){
const res = await axios.get('/ interface url')
context.commit(
SET_TOKEN,
{token:res.token}
)
}
}
Copy the code
Of course, we can deconstruct the properties of the context by destructing them
Async getUserInfo:({commit,dispatch})=>{const res = await axios.get('/ interface url') commit(SET_USER_INFO, {userInfo:res.userData}) }Copy the code
Consider a real development scenario where the userInfo property value in state is empty, and the corresponding information is retrieved after login.
After login, you need to get the user information displayed on the interface, how to get it?
Call the getUserInfo method in actions when you first enter the page
Vue part
<template>
<div>{{realName}}</div>
</template>
export default{
computed(){
/ / the third step
realName:this.$store.getters.realName
},
created(){
/ / the first step
this.reqUserInfo()
},
methods: {reqUserInfo(){
// Use the dispatch keyword to dispatch the action instead of invoking state and getters
this.$store.dispatch('getUserInfo')}}}Copy the code
Let’s go through the above process
- Called when the page is initialized
this.reqUserInfo()
Methods.this.reqUserInfo()
Distribute agetUserInfo
The action of - in
getUserInfo
In this action, we do the following:
async getUserInfo(context){
//1. Obtain data asynchronously from the interface
const res = await axios.get('/ interface url')
//2. Commit a mutation to change the state userInfo
context.commit( SET_USER_INFO,{userInfo:res.userData})
},
Copy the code
- A user name that gets up-to-date user information in computed
// get the latest userInfo realName:this.$store.getters. RealNameCopy the code
So you should understand the one-way data flow of Vuex
The interface — > dispatch action — > Action submit mutation — >mutation change state — >getters returns the latest state value to the interface
4.1 mapActions auxiliary functions
MapActions, like mapMutations, maps methods from actions to methods
So, when we have more actions, we can use them like this
methods:{ ... mapActions(['getUserInfo'.'getToken'])}Copy the code
The equivalent of
methods:{
getUserInfo(){
return this. $store. Dispatch (' getUserInfo ')},getToken(){
return this. $store. Dispatch (" getToken ")},}Copy the code
You can also have aliases
methods:{ ... mapActions( {reqUserData:'getUserInfo'},
{reqToken:'getToken')}},Copy the code
By the way, I should add
Additional Actons can be distributed on action, as follows
async getUserInfo(context){
const res = await axios.get('/ interface url')
context.commit( SET_USER_INFO,{userInfo:res.userData})
// Send another action here
context.dispatch('getToken')},Copy the code
5. To sum up
- Depending on state to get new data, use this.$store.getters. value
- To get the state data directly, use this.$store.state. value
- Use this. codestore.mit (‘mutation value ‘) to synchronize the value of the state property.
- Asynchronously modify state with this.$store.dispatch(‘action value ‘)
- Auxiliary functions such as mapState, mapMutations, mapGetters, and mapActions allow us to handle multiple methods and attributes