Vuex is mainly the state management of VUE. If we only synchronize data values through parameter passing and caching in a project, once the project becomes large and complex, it will become difficult to maintain and manage. Vuex centralizes our frequently used values and can be used throughout the project
-
State: Storage state (variable). Use: $sotre. State. XXX
-
Getters: can be understood as a calculated property of state. Process state members to the outside world. Use: $sotre. Getters. FunctionName ()
-
2. mutations: Changes the status, and it’s simultaneous. Can be used to modify the state in state. Use: $store.com MIT (state, content
-
Actions: Asynchronous operations. $store.dispath(context,payload)
-
Modules: a submodule of the store, used to facilitate state management for large projects.
Install vUE official debugging tool plug-in: VueDevtools, debugging is very convenient
1. Install and initialize vuEX
npm i vuex -s
Copy the code
1. Create a store folder under the root directory, and then create store.js inside and initialize vuex. When the project is large, state, getters, mutations and Actions are all separate files, which are easy to maintain
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
getters: {
},
mutations: {
},
actions: {
}
})
Copy the code
2. Mount vuex to global main.js
import Vue from 'vue'
import store from './store'
import router from './router'
import { Button, ConfigProvider } from 'ant-design-vue'
import 'ant-design-vue/dist/antd.min.css'
import app from './src/App'
Vue.config.productionTip = false
Vue.use(Button)
Vue.use(ConfigProvider)
new Vue({
store,
router,
render: h => h(app)
}).$mount('#app')
Copy the code
2, the State
We can simply think of it as data in the Vue component and globalData in the applet, defining the data we want to share in state
Use this. Codestore.com MIT (state,payload) to inject shared data into state. This.
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { userInfo: {userId: "1286507476287508480", name: "name ", nickname: "pigs", openId: "ogxJ55E2WqZ2-vyK2mlDtEXG28A4shvruybpohpsde", portraitUrl: "https://proxy01.hnchengyue.com/image/portrait/1286507476253954048.jpg?v=4b487d14-9580-443b-9632-fa9615a2812b", qrCode: ", regNo: null, sex: male, birthday: null, employeeState: 1, gmtCreate: null, gmtModified: NULL,}, commonInfo: 'commonInfo is a string' }, getters: { }, mutations: { }, actions: { } })Copy the code
Use this in components
<template> <div> Login page <div> This is information from VueX: {{$store.state.userInfo.name}}</div> </div> </template> <script> import { mapState } from 'vuex' import httpUtil from '.. /utils/request' export default {data () {return {}}, mounted() {console.log() {utils/request' export default {data () {return {}}, mounted() {console.log() ',this.$store.state.userInfo) } } </script>Copy the code
You can also obtain user information in vuEX on the page
The data in VueDevtools is also correct
When our component needs to fetch multiple states, it is a bit repetitive and redundant to declare all the states as calculated properties. To solve this problem, we can use the helper function of mapState, which takes an array, to help us generate calculated properties
import { mapState } from 'store/index.js' ... mapState(['aaa','bbb'])Copy the code
3, Getters
- 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.
- Getters receives two arguments. The first argument is: the state object in the current VueX object; The second argument is the current getters object, which is used to use other getters under getters
Now we add a shared data item to userInfo: commonInfo, which we can query via getters
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { userInfo: {userId: "1286507476287508480", name: "name ", nickname: "pigs", openId: "ogxJ55E2WqZ2-vyK2mlDtEXG28A4shvruybpohpsde", portraitUrl: "https://proxy01.hnchengyue.com/image/portrait/1286507476253954048.jpg?v=4b487d14-9580-443b-9632-fa9615a2812b", qrCode: ", regNo: null, sex: male, birthday: null, employeeState: 1, gmtCreate: null, gmtModified: NULL,}, commonInfo: 'commonInfo is a string' }, getters: { getUserInfo(state) { return state.userInfo; }, getCommonInfo(state,payload){return 'shared data: ${state.commonInfo}'}}})Copy the code
Use this in a component: access this.$store.getters. XXX
<template> <div> Login page <div> This is information from VueX: {{$store.state.userInfo.name}}</div> </div> </template> <script> import { mapState } from 'vuex' import httpUtil from '.. /utils/request' export default {data () {return {}}, mounted() {console.log() {utils/request' export default {data () {return {}}, mounted() {console.log() ', this $store. State. The userInfo) / / or the console. The log (' this is the userInfo in vuex: 'this. $store. The getters. GetUserInfo)}} < / script >Copy the code
The second parameter in Getters can be used with other Getters under Getters
Getters :{getUserInfo(state){return 'name: ${state.userinfo. name}'}, getCommonInfo(state,payload){return 'name: The ${payload. GetUserInfo}; Data ${state.commonInfo} '}}Copy the code
4, Mutation
If a user has changed his or her name, we can now use Mutation to synchronize the data in state, delete it, and add it
Note:
- The only way to change the state in Vuex’s store is to commit mutation
- Mutation is a synchronous operation
Each mutation has a string event type (type) and a callback function (handler). This callback function is where we actually make the state change, and it accepts state (state in the current VueX object) as the first argument; Payload (which the method uses to pass parameters when it is called) is the second parameter
There is a button on the Login page, and now we want to change the name field of userInfo when we click the button
<template> <div> Login page <div> This is information from VueX: {{$store.state.userInfo.name}}</div> <a-button type="primary" @click="onClickBtn"> Primary </a-button> </div> </template> <script> export default { data () { return { } }, methods: {onClickBtn(){let userInfo = this.$store.state.userInfo userinfo. name = 'New test name' this. use new code. store.com MIT ('setUserInfo',{ userInfo: UserInfo}) console.log(' Modified state data: ',this.$store.state.userInfo) } } } </script> import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default New vuex. Store({state: {userInfo: {userId: "1286507476287508480", name: "test name ", nickname: "pigs", openId: "ogxJ55E2WqZ2-vyK2mlDtEXG28A4shvruybpohpsde", portraitUrl: "https://proxy01.hnchengyue.com/image/portrait/1286507476253954048.jpg?v=4b487d14-9580-443b-9632-fa9615a2812b", qrCode: ", regNo: null, sex: male, birthday: null, employeeState: 1, gmtCreate: NULL, gmtModified: NULL,}, userId: '1212121', commonInfo: 'commonInfo is a string' }, getters: { getUserInfo(state) { return state.userInfo; }, getCommonInfo(state,payload){console.log(" payload ",payload) // return 'name: ${payload.getUserInfo}; ${state.commonInfo} 'return' share data: ${state.commonInfo} '}}, mutations: {setUserInfo(state, payload) {console.log('mutations in ', payout. userInfo) state.userInfo = payout. userInfo}}})Copy the code
The new data
Vue.set(state.userInfo,"age",15)
Copy the code
Delete the data
Vue.delete(state.userInfo,'openId')
Copy the code
5, the Actions
Action is similar to mutation, except that:
An Action can contain any asynchronous operation
To change the state asynchronously, use action. The action does not change state directly, but initiates mutation.
Methods in Actions have two default arguments
-
Context The context object (equivalent to this in the arrow function)
-
Payload Mount parameter
import Vue from ‘vue’ import Vuex from ‘vuex’
Vue.use(Vuex) export default new Vuex.Store({ state: { userInfo: { userId: “1286507476287508480”, name: Name of “test”, nickname: “”,” the openId: “ogxJ55E2WqZ2 – vyK2mlDtEXG28A4shvruybpohpsde,” portraitUrl: “Proxy01.hnchengyue.com/image/portr…” , qrCode: “”, regNo: null, sex: male, birthday: null, employeeState: 1, gmtCreate: NULL, gmtModified: NULL,}, userId: ‘1212121’, commonInfo: ‘commonInfo is a string’ }, getters: { getUserInfo(state) { return state.userInfo; }, getCommonInfo(state,payload){console.log(” payload “,payload) // return name: ${payload.getUserInfo}; ${state.commonInfo} return ${state.commonInfo}}, mutations: { setUserInfo(state, payload) { state.userInfo = payload.userInfo } }, actions: { aFun(context, Log (‘actions ‘, payload-userinfo) setTimeout(() => {console.log(context) console.log(‘ Actions ‘, payload-userinfo) setTimeout(() => { context.commit(‘setUserInfo’, payload) }, 2000) } } })
Use this in components
<template> <div> Login page <div> This is information from VueX: {{$store.state.userInfo.name}}</div> <a-button type="primary" @click="onClickBtn"> Primary </a-button> </div> </template> <script> import { mapState } from 'vuex' import httpUtil from '.. /utils/request' export default { data () { return { } }, mounted() { this.$store.dispatch('aFun',{ userInfo: 'hahahahahahahahahahahahaha'})},} </script> <style scoped> </style>Copy the code
We can encapsulate it with promises or async and await
promise
actions: {aFun(context, payload){console.log(context) console.log(' in actions ', paypay. userInfo) return new Promise((resolve, resolve, reject) => { context.commit('setUserInfo', payload) resolve() }) } }Copy the code
Async, await
Suppose getData() returns a Promise
actions: {
async aFun ({ commit }) {
commit('setUserInfo', await getData())
}
}
Copy the code
6, the Module
Because of the use of a single state tree, all the states of an application are grouped into one large object. When the application becomes very complex, the Store object can become quite bloated. To solve this problem, Vuex allows us to split the Store into modules. Each module has its own state, mutation, action, getter, and even nested submodules — split the same way from top to bottom
models: {
modelA: {
state:{},
getters:{},
actions: {}
},
modelB: {
state:{},
getters:{},
actions: {}
}
}
Copy the code
Invoke the state of modelA in the corresponding page
this.$store.state.modelA
Copy the code
Commit or dispatch a method, as before, automatically executes methods of the corresponding type in all modules
this.$store.commit('setUserInfo')
this.$store.dispatch('aFun')
Copy the code
For mutation and getters inside a module, the first argument received is the module’s local state object.
models:{
modelA:{
state:{key:5},
mutations:{
editKey(state){
state.key = 9
}
}
}
}
Copy the code
Similarly, for actions within the module, the local state is exposed through context.state and the rootState is context.rootstate
models:{
modelA:{
state:{count:5},
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
}
Copy the code
The third argument to the method in getters is the root node state
models:{ modelA:{ state:{key:5}, getters:{ getKeyCount(state,getter,rootState){ return rootState.key + state.key } }, . }}Copy the code
The namespace
By default, actions, mutations, and getters inside a module are registered in the global namespace — enabling multiple modules to respond to the same mutation or action.
If you want your modules to be more wrapped and reusable, you can make them namespaced by adding namespaced: True. When a module is registered, all its getters, actions, and mutations are automatically named according to the path the module was registered with. Such as:
Const store = new vuex.store ({modules: {account: {namespaced: true, // Module assets) state: () => ({... }), // module states are already nested, and using the 'namespaced' attribute doesn't affect them. Getters: {isAdmin () {... } // -> getters['account/isAdmin'] }, actions: { login () { ... } // -> dispatch('account/login') }, mutations: { login () { ... } / / - > commit (' account/login ')}, / / nested modules modules: {myPage / / father module namespace: {state: (a) = > ({... }), getters: { profile () { ... } // -> getters['account/profile']}}, // Further nested namespace posts: {namespaced: true, state: () => ({... }), getters: { popular () { ... } // -> getters['account/posts/popular'] } } } } } })Copy the code
7. Auxiliary functions: mapState, mapMutations, mapGetters, etc
MapState helper function
Note: the attribute of mapState must correspond to the value of the attribute of state, that is, the attribute value of state is called add, so mapState is called add. If we change it to add2, we will not get the value of add, and the value of add2 is also undefined
<script> import { mapState } from 'vuex'; export default { data() { return { } }, methods: { }, computed: { ... mapState({ add: state => state.add, counts: state => state.counts }) }, mounted() { console.log(this.add) console.log(this.counts) } } </script>Copy the code
MapGetters helper function
The mapGetters helper function simply maps the getters in the store to local computed properties
import { mapGetters } from 'vuex' export default { // ... Computed: {// Mix getters into a computed object using the object expansion operator... mapGetters([ 'getUserInfo', 'getCommonInfo', // ... ] )}}Copy the code
MapActions helper function
import { mapActions } from 'vuex' export default { // ... Computed: {// mapActions Use method 1 to map aFun() to this.$store.dispatch('aFun')... MapActions ([' aFun ') / / the second approach... mapActions ({' aFun ':' aFun})}Copy the code
MapMutations auxiliary function
import { mapMutations } from 'vuex' export default { // ... methods: { ... MapMutations (['setUserInfo', // map 'this.setUserinfo ()' to 'this.setUserInfo ',]),}}Copy the code
8. Standardize the directory structure
- Application-level state should be centralized into a single Store object.
- Submitting mutation is the only way to change the state, and the process is synchronous.
- Asynchronous logic should be wrapped in actions.
If the Store file is too large, simply split the Action, mutation, and getter into separate files. This is also mentioned above
Store ├ ─ ─ index. Js # we assembled module and export store ├ ─ ─ actions. Js # root level action ├ ─ ─ mutations. Js # root level mutation └ ─ ─ modules ├ ─ ─ ├ ─ scients.js # Scients.js #Copy the code
9. Use VUEX to realize simple case of changing theme color
You may have seen it in some websites or apps. When you click on a color block, the background color of a place in the whole world will be the same, just like the project I made below. So without further ado about the code
The project is written by uniAPP framework, which is similar to VUE
First of all, we define the default theme color in the state of VUex, and most of the time, we obtain the global configuration from the background. It is simple to write in the code, which is convenient for testing. Getters and mutations are also written in advance
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const extConfig = uni.getExtConfigSync ? uni.getExtConfigSync() : {}; Const store = new vuex. store ({state: {shopName:' home ', phone: extconfig. phone, appID: extconfig. appid, token: new vuex. store ({state: {shopName:' home ', phone: extconfig. phone, appID: extconfig. appid, token: ExtConfig token, themeBgColor: extConfig themeBgColor | | '# 00 b000', / / global theme colors}, getters: {getThemeBgColor: (state, theme) => { console.log(state); console.log(theme); return state.themeBgColor } }, mutations: { setMerchant(merchant){ state.merchant = merchant }, setThemeBgColor: (state, theme) => { console.log(state, theme); state.themeBgColor = theme.themeBgColor } } }) export default storeCopy the code
Reference vuex in main.js
Change the theme color on my page
<script> var that; import { mapGetters } from 'vuex' import uniPopup from '.. /.. /components/uni-popup/uni-popup.vue' export default {data() {return {globalTheme: ", // themeList: [{text: 'Dim', color: '#41555d' }, { text: 'Grey', color: '#808080' }, { text: 'Dark', color: '#161a23' }, { text: 'Water', color: '#88ada6' }, { text: 'Blush', color: '#f9906f' }, { text: 'Orange', color: '#ff8936' }, { text: 'Indigo', color: '#27547d' }, { text: 'Volcano', color: '#fa541c' }, { text: 'Verdant', color: '#519a73' }, { text: 'PinkGold', color: '#f2be45' }, { text: 'Verdigris', color: '#549688' }, { text: 'DeepBlack', color: '#3d3b4f' }, { text: 'DarkGreen', color: '#75878a' }, { text: 'IndigoBlue', color: '#177cb0' }, { text: 'BluishGreen', color: '#48c0a3' }, ] } }, components: { uniPopup }, computed: { getGlobalTheme() { return this.$store.state.themeBgColor } }, watch: { getGlobalTheme(val){ console.log(val); this.globalTheme = val this.$store.commit('setThemeBgColor',{ themeBgColor: val }) uni.setNavigationBarColor({ frontColor: '#ffffff', backgroundColor: val, }) } }, methods: OpenThemeModal (){this.$refs.popup.open()}, (){this.themeBgColor = item.color this. store.com MIT ('setThemeBgColor',{themeBgColor: item.color }) this.$refs.popup.close() } } } </script>Copy the code
After changing the theme color, we need to listen for the change of state value in vuex and set the color we selected for navbar. Use computed and Watch to listen
-
First, we write a computed property in computed, which returns the themeBgColor of State in state management
-
Then listen for changes to the calculated property in Watch and reassign it.
Get the modified theme color on the shopping cart page
Put it in the onShow lifecycle for listening
onShow() { console.log(this.$store.state.themeBgColor); uni.setNavigationBarColor({ frontColor: '#ffffff', backgroundColor: this.$store.state.themeBgColor, }) }, computed: {getGlobalTheme() {return this.$store.state.themeBgColor}}, watch: {getGlobalTheme(val){console.log(" Shopping cart: ",val); this.globalTheme = val // uni.setNavigationBarColor({ // frontColor: '#ffffff', // backgroundColor: val, // }) } },Copy the code
10. Use VUEX to realize simple full-screen control of music player
In the above case, state, getters and so on in VUEX are all put in a JS file. Now we separate them out separately and check the effect first. The exit animation has not been written yet
Click on the small player below can also be expanded
Vuex is simply used to control whether a music player is in full screen or not
The index. The js code
Import the file after it has been created, and don’t forget to mount it to main.js
import Vue from 'vue' import Vuex from 'vuex' import * as getters from './getters.js' import mutations from './ roads.js' import state from './state.js' import actions from './actions.js' vue.use (Vuex) Const store = new vuex. store ({// namespaced: const store = new vuex. true, state, getters, actions, mutations }) export default storeCopy the code
State. The js code
Do an init on state
import { playMode } from '.. /utils/ playconfig. js' const state = {isPlaying: false,// Whether isFullScreen: false,// Whether full screen playingList: SequenceList: [],// sequenceList: [],// playingMode: playmode. sequence,// playMode. CurPlayMusicIndex: -1,// Index of current playing music} export default stateCopy the code
Getters. Js code
In getters, we return the data and finally get it through mapGetters
export const isPlaying = state => state.isPlaying export const isFullScreen = state => state.isFullScreen export const playingList = state => state.playingList export const sequenceList = state => state.sequenceList export const PlayingMode = State => state.playingMode export const curPlayMusicIndex = State => state.curPlayMusicIndex // Current playing music export const curPlayMusic = (state) => { return state.playingList[state.curPlayMusicIndex] || {} }Copy the code
Mutations. Js code
import * as types from './mutationsTypes.js'
const mutations = {
[types.setIsPlaying](state, flag) {
state.isPlaying = flag
},
[types.setIsFullScreen](state, flag) {
state.isFullScreen = flag
},
[types.setPlayingList](state, list) {
state.playingList = list
},
[types.setSequenceList](state, list) {
state.sequenceList = list
},
[types.setPlayingMode](state, mode) {
state.playingMode = mode
},
[types.setCurPlayMusicIndex](state, index) {
state.curPlayMusicIndex = index
},
}
export default mutations
Copy the code
MutationsTypes is just a file of type type
export const setIsPlaying = 'setIsPlaying'
export const setIsFullScreen = 'setIsFullScreen'
export const setPlayingList = 'setPlayingList'
export const setSequenceList = 'setSequenceList'
export const setPlayingMode = 'setPlayingMode'
export const setCurPlayMusicIndex = 'setCurPlayMusicIndex'
Copy the code
The actions of code
All submissions are referred to actions
import * as mutationsTypes from './mutationsTypes.js' const actions = { selectPlayMusic:({commit, state}, {playingList, Index}) = > {/ / console log (mutationsTypes) / / submit order operation, playlists, index of the current broadcast music, whether the operation of the full screen commit (mutationsTypes setSequenceList, playingList) commit(mutationsTypes.setPlayingList, playingList) commit(mutationsTypes.setCurPlayMusicIndex, index) commit(mutationsTypes.setIsFullScreen, true) commit(mutationsTypes.setIsPlaying, true) } } export default actionsCopy the code
Finally, mount it globally
import Vue from 'vue' import App from './App' import { httpUrl } from 'utils/config.js' import toast from 'utils/toast.js' import Util from 'utils/utils.js' import store from 'store/index.js' Vue.config.productionTip = false Vue.prototype.$httpUrl = httpUrl Vue.prototype.$toast = toast Vue.prototype.$Util = Util Vue.prototype.$store = store App.mpType = 'app' const app = new Vue({ ... App, store }) app.$mount()Copy the code
Play component code
<script> import { mapGetters, mapMutations } from 'vuex' export default { data() { return { } }, methods: {this.isshowPlayer = false this.setisFullScreen (false)}, OnClickOpenPlayer (){this.setisFullScreen (true) {this.createAudioPlay()},... MapMutations (['setIsFullScreen'])}, // Listen on getters computed: {... mapGetters([ 'isFullScreen', 'playingList', 'curPlayMusic' ]) }, } </script>Copy the code