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

  1. 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.
  2. 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.
  3. 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

  1. Application-level state should be centralized into a single Store object.
  2. Submitting mutation is the only way to change the state, and the process is synchronous.
  3. 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

  1. First, we write a computed property in computed, which returns the themeBgColor of State in state management

  2. 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