Vuex模块化与持久化深入讲解
概述
Vuex作为VUE状态管理组件,能够将项目公共数据进行统一管理。而且可以按照不同的业务功能将数据状态分模块管理。另外,对于网页刷新导致Vuex状态丢失的问题可以使用vuex-persistedstate
插件配置将数据保存在localStorage
或者sessionStorage
中。
本文测试环境如下:
“vue”: “^2.2.37”,
“vue-router”: “^3.0.1”,
“vuex”: “^3.0”,
“vuex-persistedstate”: “^4.1.0”
“secure-ls”: “^1.2.6”,
Vuex的模块化
首先是main.js
文件中引用Vuex组件,引入./store/index.js
作为store参数,用于实例化VUE对象。
// main.js
import Vue from 'vue'
import store from "./store";
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
其中./store/index.js
文件是Vuex状态实例,使用new Vuex.Store()
进行状态实例化,并将根状态的state
,getters
,mutations
,actions
添加到参数,以及各个模块添加到modules
对象参数中。
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, chat
}
})
以上./store/index.js
文件中,有user
,room
,chat
三个模块,模块之间大同小异,下面仅以user
模块进行讲解。其中
state数据状态对象
state
,作为数据状态的存储,是一个匿名函数返回的对象,该对象中有一个curTheme
主题字符串和一个 curUser
用户对象。
// state: 用户相关状态
const state = () => ({
curTheme: 'light',
curUser: {
id: '123456',
name: '张三'
},
})
getters计算属性对象
getters
,作为计算属性,类似vue组件中的computed属性。该对象中是一个个的方法函数,该函数按照顺序有(state, getters, rootState, rootGetters)
四个参数。前两个参数state
和getters
是本模块中的数据状态对象和计算属性对象,可在方法中按照如下格式进行引用。
curUserId
中可以使用state.curUser
来访问当前模块中数据状态对象中的curUser对象。
使用方法:state.curUserId
isCurUserId
中返回的是一个函数,该函数接收一个userId
参数,通过getters.curUserId
可以访问当前计算属性对象中的curUserId
属性。
使用方法:state.isCurUserId(userId)
getCurThemeByUserId
中返回的是一个函数,该函数接收一个userId
参数,通过getters.isCurUserId(userId)
函数确认是否是当前用户,并返回对应主题。
使用方法:state.getCurThemeByUserId(userId)
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 '';
}
}
}
后两个参数rootState
和rootGetters
可以用来访问根和其他模块的数据状态和计算属性。比如下面是room
模块的getters
属性。
// room.js
const getters = {
// 测试
testRoom: (state, getters, rootState, rootGetters) => {
// 获取userid
let curUserId = rootGetters['user/curUserId']
// 根据userId获取当前主题
let curTheme = rootGetters['user/getCurThemeByUserId'](curUserId)
return 'test';
}
}
actions异步请求对象
actions
,内部是一个个函数,所有异步操作需要放到这里,且如果需要更改数据状态,则必须通过commit
调用相应的mutation
。且需要注意该函数有两个参数(context, payload)
,其中context是一个对象,包括了state
,‘rootState’,‘commit’,‘dispatch’,‘getters’,'rootGetters’等参数。
需要注意的是,actions中的(context, payload)
参数中context是对象,所以里面的参数可以是无须的。但是getters中的(state, getters, rootState, rootGetters)
是四个参数,并且是有序的,千万注意顺序!!!
// actions,异步操作,通过mutation进行更新数据
const actions = {
//context:{
// state, 等同于store.$state,若在模块中则为局部状态
// rootState, 等同于store.$state,只存在模块中
// commit, 等同于store.$commit
// dispatch, 等同于store.$dispatch
// getters 等同于store.$getters,若在模块中为局部状态
// rootGetters 等同于store.$getters
// }
// 用户登录
async login ({state, rootState, commit, dispatch, getters, rootGetters}, {name, passwd}) {
// getters使用
getters.curUserId // 获取当前模块中的curUserId计算属性
rootGetters['user/curUserId'] // 获取user模块中的curUserId计算属性
// dispatch使用
dispatch('logout') // 调用本模块中的logout异步请求
dispatch('room/getRoomByUserId', null, { root: true }) // 调用rooom模块的getRoomByUserId异步请求
// commit使用
commit('SET_CUR_USER', null) // 调用本模块mutation方法
commit('room/SET_ROOM_LIST', null, { root: true }) // 调用room模块mutation方法
},
// 登出
async logout({commit}) {
let res = await $api.logout()
localStorage.removeItem("token");
commit("SET_CUR_USER", null);
// 关闭socket链接
websocket.close();
await $router.push("/")
},
mutations数据同步对象
mutations
,数据同步对象,内部是一个个同步函数,该函数中主要是为了修改state属性。注意千万不要在actions
或者其他地方直接设置state
数据状态,若要修改state
状态,必须使用commit
。因为只有在mutations
方法中修改才能触发Vuex数据和视图同步更新。
其他地方更新数据,需要使用commit方法
commit('room/SET_ROOM_LIST', null)
另外,对象和数组类型修改时不能使用state.curUser = curUser
这种方式。需要使用Vue.set()方法进行修改,否则也不会触发数据视图的更新。
Vue.set(state, 'curUser', curUser)
Vue.set(state.curUser, 'name', '张三')
Vue.set(state.list, 0, "2");
// mutations,定义更新数据方法,同步操作
const mutations = {
SET_CUR_THEME (state, curTheme) {
state.curTheme = curTheme
},
SET_CUR_USER (state, curUser) {
Vue.set(state, 'curUser', curUser)
},
}
Vuex的使用方式
在自定义组件中使用
// RoomGaming.vue
import {mapActions, mapGetters, mapState, mapMutations} from "vuex";
export default {
computed: {
...mapState('user', ['curUser']),
...mapState('room', ['roomVO']),
...mapGetters('room', ['seatCount', 'playerList', 'curPlayer', 'curPlayerStatus',
'curPlayerCanAddSeat', 'curPlayerCanDelSeat', 'curPlayerIsOwner']),
},
methods: {
...mapActions('room', ['leaveRoom', 'playerReady', 'playerAddSeat', 'startGame']),
...mapMutations('room', [])
},
}
在自定义js文件中引用
// 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;
}
Vuex持久化配置
在main.js中添加plugins属性,并设置key
和storage
属性,key是键名,storage是存储位置,可以是window.localStorage
也可以是window.sessionStorage
。
// main.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数据状态存储在此解决刷新丢失数据的问题。如下图,可以看到相应的数据存储。
另外,由于是明文存储,可能存在安全问题,可以使用以下插件对数据进行加密存储。
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),
}
})
]
})
加密之后,控制台显示如下,可以看到vuex中内容已加密。
main.js代码
import Vue from 'vue'
import Vuex from 'vuex'
import user from "./modules/user";
import room from "./modules/room"
import chat from "./modules/chat"
import cardxstx from "./modules/cardxstx"
import gamexstx from "./modules/gamexstx";
import createPersistedState from 'vuex-persistedstate'
import SecureLS from "secure-ls";
import SystemConfig from "../consts/SystemConfig";
Vue.use(Vuex)
const state = () => ({})
const getters = {}
const mutations = {}
const actions = {}
var ls = new SecureLS({
encodingType: "aes", //加密类型
isCompression: false, //是否压缩
encryptionSecret: "encryption", //PBKDF2值 加密秘密
});
localStorage.removeItem(SystemConfig.storageKey);
export default new Vuex.Store({
state,
getters,
mutations,
actions,
modules: {
user, room, chat, gamexstx, cardxstx
},
plugins: [
createPersistedState({
key: SystemConfig.storageKey,
storage: window.localStorage,
// 以下使用ls加密
// key: SystemConfig.storageKey,
// storage: {
// getItem: (key) => ls.get(key),
// setItem: (key, value) => ls.set(key, value),
// removeItem: (key) => ls.remove(key),
// }
})
]
})
modules/user.js代码
// ./store/modules/user.js
import Vue from 'vue'
import $api from '../../api/inter'
import $router from "../../router";
import {Message} from "element-ui";
import websocket from "../../api/websocket";
import SystemConfig from "../../consts/SystemConfig";
// state: 用户相关状态
const state = () => ({
curTheme: 'light',
curUser: undefined,
})
// getters: 用户相关计算属性,类似vue组件中的computed
const getters = {
//
curUserId: (state, getters, rootState, rootGetters) => {
return state.curUser ? state.curUser.id : undefined
},
getUserNameById: (state, getters, rootState, rootGetters) => {
return (userId) => {
if(state.curUser.id == userId) return state.curUser.name;
else return "无名氏"
}
}
}
// actions,异步操作,通过mutation进行更新数据
const actions = {
//context:{
// state, 等同于store.$state,若在模块中则为局部状态
// rootState, 等同于store.$state,只存在模块中
// commit, 等同于store.$commit
// dispatch, 等同于store.$dispatch
// getters 等同于store.$getters
// }
// 获取用户信息
async getCurUser ({ state, commit }) {
// dispatch('room/getRoomByUserId', value, { root: true }) // 调用另外模块
// 获取登录后存储在localStorage中的token值
let token = localStorage.getItem("token");
// console.log("token=" + token)
// 如果token为空则返回空
if(token == undefined || token == null) {
commit('SET_CUR_USER', null);
// Message.warning("用户token失效,将移除本地token");
// 移除token
localStorage.removeItem("token")
// 关闭socket链接
websocket.close();
return;
}
try{
// 将token传到后台获取对应用户信息
let res = await $api.getUserInfoByToken();
if(200 != res.code) throw new Error(res.message);
localStorage.removeItem(SystemConfig.storageKey); // 登陆成功后清空会话缓存
// 获取到用户信息,设置到curUser状态中
commit('SET_CUR_USER', res.data);
// 设置websocket
websocket.connect(state.curUser.id)
} catch(err) {
// 会话失效后应该清理本地缓存
localStorage.removeItem("token");
// 关闭socket链接
websocket.close();
return Promise.reject(err);
}
},
// 游客登录
async loginAsNameless({ dispatch }) {
try{
let res = await $api.loginAsNameless()
await dispatch('loginSuccess', res.data);
} catch (err) {
Message.error(err);
return Promise.reject(err)
}
},
// 用户登录
async loginByUser({dispatch}, params) {
try{
let res = await $api.login(params)
// console.log("用户登录返回:", res)
await dispatch('loginSuccess', res.data);
}catch (err) {
Message.error(err)
return Promise.reject(err)
}
},
// 登录之后的操作
async loginSuccess({state, dispatch}, token) {
// 设置本地缓存
localStorage.token = token;
// 获取用户信息
await dispatch('getCurUser');
// 如果获取用户信息成功,则打开websocket并进入大厅
if(state.curUser != null) {
await $router.push({path: '/hall'})
} else {
$router.push({path: "/"})
}
},
// 登出
async logout({commit}) {
let res = await $api.logout()
localStorage.removeItem("token");
commit("SET_CUR_USER", null);
// 关闭socket链接
websocket.close();
await $router.push("/")
},
// 修改密码
async modifyPass({commit}, params) {
try{
await $api.modifyPass(params)
} catch (err) {
Message.error(err)
return Promise.reject(err)
}
}
}
// mutations,定义更新数据方法,同步操作
//
const mutations = {
SET_CUR_USER (state, curUser) {
Vue.set(state, 'curUser', curUser)
},
}
export default {
namespaced: true,
state,
getters,
mutations,
actions
}
项目传送门:https://github.com/louislee92/vue-module-persistedstate
到此这篇关于Vuex模块化与持久化深入讲解的文章就介绍到这了,更多相关Vuex模块化与持久化内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341