Vuex作为VUE状态管理组件,能够将项目中公共数据状态进行统一管理,并且可以按照不同的业务功能将数据状态分模块管理,即数据状态模块化。
另外,对于网页刷新导致Vuex状态丢失的问题可以使用vuex-persistedstate
插件配置将数据保存在localStorage或者sessionStorage中,即数据状态持久化。
同时使用secure-ls
插件将明文数据加密,以防数据泄露风险。
本文测试环境如下:
Node版本:v10.14.2
Npm版本:6.4.1
package.json依赖的插件版本如下:
“vue”: “^2.2.37”,
“vue-router”: “^3.0.1”,
“vuex”: “^3.0”,
“vuex-persistedstate”: “^4.1.0”
“secure-ls”: “^1.2.6”,
Vuex的模块化将按照顺序分为3个部分:Vuex组件实例化与引用、子模块的创建、模块化的使用方法。
首先创建Vuex状态实例文件src/store/index.js
,用于创建状态实例,并将根状态的四个参数(state, getters, mutations, actions),以及各个模块实例添加到modules
对象参数中。详见以下代码。
// src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import user from "./modules/user";
import room from "./modules/room"
// 使用Vuex组件
Vue.use(Vuex)
const state = () => ({})
const getters = {}
const mutations = {}
const actions = {}
// 实例化状态对象
export default new Vuex.Store({
// 根模块的四类对象
state, getters, mutations, actions,
modules: { // 将各个模块放入modules属性中
user, room
}
})
至于以上代码中引入的user,room子模块,下文将进行介绍。
接下来要将Vuex组件实例添加到Vue实例中,具体操作见如下代码。
// src/main.js
import Vue from 'vue'
import store from "./store";
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: ' '
})
上文.src/store/index.js
文件中,有user和room2个模块,模块之间大同小异,下面主要以user
子模块进行讲解。本节主要介绍子模块的创建,具体使用方法将在2.3节有详细介绍。
// src/store/modules/user.js
// user子模块
const state = () => ({}) // 详见2.2.1小节
const getters = {} // 详见2.2.2小节
const actions = {} // 详见2.2.3小节
const mutations = {} // 详见2.2.4小节
export default {
namespaced: true,
state,
getters,
mutations,
actions
}
state作为数据状态的存储,是一个匿名函数返回的对象。
如下代码所示,该对象中两个数据状态curTheme和curUser,其中curTheme是字符串类型,curUser是对象类型。
// src/store/modules/user.js
// state: 用户相关状态
const state = () => ({
curTheme: 'light',
curUser: {
id: '123456',
name: '张三'
}
})
getters作为计算属性,类似vue组件中的computed属性。
如下代码所示,该对象中是一个个的方法函数,该函数按照顺序有(state, getters, rootState, rootGetters)
四个参数。
// src/store/modules/user.js
const getters = {
// 获取当前用户ID
curUserId: (state, getters, rootState, rootGetters) => {
return state.curUser ? state.curUser.id : undefined
},
// 比对userId是否是当前用户id
isCurUserId: (state, getters, rootState, rootGetters) => {
return (userId) => {
return userId == getters.curUserId;
}
},
// 根据userId获取当前主题
getCurThemeByUserId: (state, getters, rootState, rootGetters) => {
return (userId) => {
if(getters.isCurUserId(userId)) return state.curTheme;
else return '';
}
}
}
其中,前两个参数state
和getters
是本模块中的数据状态对象(即2.2.1节中介绍的state对象)和计算属性对象(即本节介绍的getters对象),这两个参数是局部参数,只能访问本模块中的数据状态和计算属性对象。
后两个参数rootState
和rootGetters
可以用来访问根模块和其他子模块的数据状态和计算属性对象。
actions内部是也是一个个函数,主要用于执行异步请求逻辑。而且,如果需要更改数据状态,则必须通过commit
调用相应的mutation
对象。
另外,该函数有两个参数(context, payload)
,其中context是一个对象,即{state, rootState, commit, dispatch, getters, rootGetters}
等6个参数(具体解释见如下代码中注释),palyload为调用时的自定义输入参数。
需要注意的是,actions中的
(context, payload)
参数中context是对象,所以里面的参数可以是无序的。但是getters中的(state, getters, rootState, rootGetters)
是四个参数,并且是有序的,千万注意顺序!!!
// src/store/modules/user.js
// actions,异步操作,通过mutation进行更新数据
const actions = {
//context:{
// state, 本模块数据状态对象,例如state.curTheme
// rootState, 根模块数据状态对象,例如rootState.user.curTheme
// commit, 用于调用mutation函数,例如commit('SET_CUR_USER', null)
// dispatch, 用于调用action函数,例如dispatch('logout')
// getters 本模块计算属性对象,例如getters.curUserId
// rootGetters 根模块计算属性对象,例如rootGetters['user/curUserId']
// }
// 用户登录
async login ({state, rootState, commit, dispatch, getters, rootGetters}, {name, passwd}) {
// 用户登录逻辑
let res = await $api.login();
if(!res.success) Message.error('登录失败');
},
// 登出
async logout({commit}) {
let res = await $api.logout()
},
mutations数据同步对象,内部是一个个同步函数,该函数没有返回值,内部逻辑主要是为了修改state属性。
注意千万不要在actions或者其他地方直接设置state数据状态。若要修改state状态,必须使用commit
调用mutations函数,因为只有在mutations函数中修改才能触发Vuex数据和视图同步更新。
// src/store/modules/user.js
// mutations,定义更新数据方法,同步操作
const mutations = {
SET_CUR_THEME (state, curTheme) {
state.curTheme = curTheme
},
SET_CUR_USER (state, curUser) {
// curUser是对象类型,需要使用Vue.set方法赋值,数组类型也一样需要
Vue.set(state, 'curUser', curUser)
},
}
Vuex模块化后不仅可以在自定义组件中使用,而且可以引入store实例使用。
// RoomGaming.vue
<template>...</template>
<script>
import {mapActions, mapGetters, mapState, mapMutations} from "vuex";
export default {
computed: {
// 展开user模块的curTheme和curUser
...mapState('user', ['curTheme', 'curUser']),
// 展开user模块的三个计算属性
...mapGetters('user', ['curUserId', 'isCurUserId', 'getCurThemeByUserId']),
},
methods: {
// 展开user模块的login和logout两个异步函数
...mapActions('user', ['login', 'logout']),
// 展开user模块的SET_CUR_THEME和SET_CUR_USER函数
...mapMutations('user', ['SET_CUR_THEME', 'SET_CUR_USER']),
// 如何使用
test() {
// 通过属性访问getters
let id = this.curUserId; // mapGetters展开
let id = this.$store.getters['user/curUserId']
// 通过函数访问getters
let flag = this.isCurUserId();// mapGetters展开
let flag = this.$store.getters['user/curUserId']();
// 通过dispatch触发actions
let res = await this.$store.dispatch('user/login');
let res = await this.login(); // mapActions展开
// 通过commit提交mutations
this.$store.commit('user/SET_CUR_THEME', 'dark');
this.SET_CUR_THEME('dark'); // mapMutations展开
}
},
}
</script>
不仅在自定义组件中使用,而且可以引入store实例在任何js文件中使用。使用方法如同自定义组件中的this.$store
。
// ReceiveService.js
import $store from '../store'
const testFunction = (data) => {
// mutations
$store.commit("gamexstx/SET_CLOCKWISE", data.clockwise);
$store.commit("gamexstx/SET_BOTTOM", data.bottom);
$store.commit("gamexstx/SET_DEGREE", data.degree);
$store.commit("gamexstx/SET_PLAYER_STATE", data.playerState);
// getters
let index = $store.getters['gamexstx/curDrawIndex']
let code = $store.getters['gamexstx/getCardInGroup1ByIndex'](index);
// actions
await $store.dispatch('cardxstx/playDrawCardAnim', {code, target});
// state
if($store.state.gamexstx.degree > 0) return;
}
在src/store/index.js
中添加plugins属性,并设置key
和storage
属性,key是键名,storage是存储位置,可以是window.localStorage
也可以是window.sessionStorage
。
// src/store/index.js
import createPersistedState from 'vuex-persistedstate'
export default new Vuex.Store({
state,
getters,
mutations,
actions,
modules: {
user, room, chat, gamexstx, cardxstx
},
plugins: [
createPersistedState({
key: 'vuex',
storage: window.localStorage,
})
]
})
因为localStorage不会随着网页刷新而丢失数据,所以将Vuex数据状态存储在此解决刷新丢失数据的问题。如下图,可以看到相应的数据存储。
另外,由于是明文存储,可能存在安全问题,可以使用secure-ls
插件对数据进行加密存储。
// src/store/index.js
import SecureLS from "secure-ls"
var ls = new SecureLS({
encodingType: "aes", //加密类型
isCompression: false, //是否压缩
encryptionSecret: "encryption", //PBKDF2值 加密秘密
});
export default new Vuex.Store({
state,
getters,
mutations,
actions,
modules: {
user, room, chat, gamexstx, cardxstx
},
plugins: [
createPersistedState({
// 以下使用ls加密
key: 'vuex',
storage: {
getItem: (key) => ls.get(key),
setItem: (key, value) => ls.set(key, value),
removeItem: (key) => ls.remove(key),
}
})
]
})
项目传送门:https://github.com/louislee92/vue-module-persistedstate