Vuex模塊化與持久化深入講解
概述
Vuex作為VUE狀態(tài)管理組件,能夠?qū)㈨?xiàng)目公共數(shù)據(jù)進(jìn)行統(tǒng)一管理。而且可以按照不同的業(yè)務(wù)功能將數(shù)據(jù)狀態(tài)分模塊管理。另外,對(duì)于網(wǎng)頁刷新導(dǎo)致Vuex狀態(tài)丟失的問題可以使用vuex-persistedstate
插件配置將數(shù)據(jù)保存在localStorage
或者sessionStorage
中。
本文測(cè)試環(huán)境如下:
“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參數(shù),用于實(shí)例化VUE對(duì)象。
// main.js import Vue from 'vue' import store from "./store"; /* eslint-disable no-new */ new Vue({ el: '#app', router, store, components: { App }, template: '<App/>' })
其中./store/index.js
文件是Vuex狀態(tài)實(shí)例,使用new Vuex.Store()
進(jìn)行狀態(tài)實(shí)例化,并將根狀態(tài)的state
,getters
,mutations
,actions
添加到參數(shù),以及各個(gè)模塊添加到modules
對(duì)象參數(shù)中。
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 = {} // 實(shí)例化狀態(tài)對(duì)象 export default new Vuex.Store({ state, getters, mutations, actions, modules: { // 將各個(gè)模塊放入modules屬性中 user, room, chat } })
以上./store/index.js
文件中,有user
,room
,chat
三個(gè)模塊,模塊之間大同小異,下面僅以user
模塊進(jìn)行講解。其中
state數(shù)據(jù)狀態(tài)對(duì)象
state
,作為數(shù)據(jù)狀態(tài)的存儲(chǔ),是一個(gè)匿名函數(shù)返回的對(duì)象,該對(duì)象中有一個(gè)curTheme
主題字符串和一個(gè) curUser
用戶對(duì)象。
// state: 用戶相關(guān)狀態(tài) const state = () => ({ curTheme: 'light', curUser: { id: '123456', name: '張三' }, })
getters計(jì)算屬性對(duì)象
getters
,作為計(jì)算屬性,類似vue組件中的computed屬性。該對(duì)象中是一個(gè)個(gè)的方法函數(shù),該函數(shù)按照順序有(state, getters, rootState, rootGetters)
四個(gè)參數(shù)。前兩個(gè)參數(shù)state
和getters
是本模塊中的數(shù)據(jù)狀態(tài)對(duì)象和計(jì)算屬性對(duì)象,可在方法中按照如下格式進(jìn)行引用。
curUserId
中可以使用state.curUser
來訪問當(dāng)前模塊中數(shù)據(jù)狀態(tài)對(duì)象中的curUser對(duì)象。
使用方法:state.curUserId
isCurUserId
中返回的是一個(gè)函數(shù),該函數(shù)接收一個(gè)userId
參數(shù),通過getters.curUserId
可以訪問當(dāng)前計(jì)算屬性對(duì)象中的curUserId
屬性。
使用方法:state.isCurUserId(userId)
getCurThemeByUserId
中返回的是一個(gè)函數(shù),該函數(shù)接收一個(gè)userId
參數(shù),通過getters.isCurUserId(userId)
函數(shù)確認(rèn)是否是當(dāng)前用戶,并返回對(duì)應(yīng)主題。
使用方法:state.getCurThemeByUserId(userId)
const getters = { // 獲取當(dāng)前用戶ID curUserId: (state, getters, rootState, rootGetters) => { return state.curUser ? state.curUser.id : undefined }, // 比對(duì)userId是否是當(dāng)前用戶id isCurUserId: (state, getters, rootState, rootGetters) => { return (userId) => { return userId == getters.curUserId; } }, // 根據(jù)userId獲取當(dāng)前主題 getCurThemeByUserId: (state, getters, rootState, rootGetters) => { return (userId) => { if(getters.isCurUserId(userId)) return state.curTheme; else return ''; } } }
后兩個(gè)參數(shù)rootState
和rootGetters
可以用來訪問根和其他模塊的數(shù)據(jù)狀態(tài)和計(jì)算屬性。比如下面是room
模塊的getters
屬性。
// room.js const getters = { // 測(cè)試 testRoom: (state, getters, rootState, rootGetters) => { // 獲取userid let curUserId = rootGetters['user/curUserId'] // 根據(jù)userId獲取當(dāng)前主題 let curTheme = rootGetters['user/getCurThemeByUserId'](curUserId) return 'test'; } }
actions異步請(qǐng)求對(duì)象
actions
,內(nèi)部是一個(gè)個(gè)函數(shù),所有異步操作需要放到這里,且如果需要更改數(shù)據(jù)狀態(tài),則必須通過commit
調(diào)用相應(yīng)的mutation
。且需要注意該函數(shù)有兩個(gè)參數(shù)(context, payload)
,其中context是一個(gè)對(duì)象,包括了state
,‘rootState’,‘commit’,‘dispatch’,‘getters’,'rootGetters’等參數(shù)。
需要注意的是,actions中的(context, payload)
參數(shù)中context是對(duì)象,所以里面的參數(shù)可以是無須的。但是getters中的(state, getters, rootState, rootGetters)
是四個(gè)參數(shù),并且是有序的,千萬注意順序!??!
// actions,異步操作,通過mutation進(jìn)行更新數(shù)據(jù) const actions = { //context:{ // state, 等同于store.$state,若在模塊中則為局部狀態(tài) // rootState, 等同于store.$state,只存在模塊中 // commit, 等同于store.$commit // dispatch, 等同于store.$dispatch // getters 等同于store.$getters,若在模塊中為局部狀態(tài) // rootGetters 等同于store.$getters // } // 用戶登錄 async login ({state, rootState, commit, dispatch, getters, rootGetters}, {name, passwd}) { // getters使用 getters.curUserId // 獲取當(dāng)前模塊中的curUserId計(jì)算屬性 rootGetters['user/curUserId'] // 獲取user模塊中的curUserId計(jì)算屬性 // dispatch使用 dispatch('logout') // 調(diào)用本模塊中的logout異步請(qǐng)求 dispatch('room/getRoomByUserId', null, { root: true }) // 調(diào)用rooom模塊的getRoomByUserId異步請(qǐng)求 // commit使用 commit('SET_CUR_USER', null) // 調(diào)用本模塊mutation方法 commit('room/SET_ROOM_LIST', null, { root: true }) // 調(diào)用room模塊mutation方法 }, // 登出 async logout({commit}) { let res = await $api.logout() localStorage.removeItem("token"); commit("SET_CUR_USER", null); // 關(guān)閉socket鏈接 websocket.close(); await $router.push("/") },
mutations數(shù)據(jù)同步對(duì)象
mutations
,數(shù)據(jù)同步對(duì)象,內(nèi)部是一個(gè)個(gè)同步函數(shù),該函數(shù)中主要是為了修改state屬性。注意千萬不要在actions
或者其他地方直接設(shè)置state
數(shù)據(jù)狀態(tài),若要修改state
狀態(tài),必須使用commit
。因?yàn)橹挥性?code>mutations方法中修改才能觸發(fā)Vuex數(shù)據(jù)和視圖同步更新。
其他地方更新數(shù)據(jù),需要使用commit方法
commit('room/SET_ROOM_LIST', null)
另外,對(duì)象和數(shù)組類型修改時(shí)不能使用state.curUser = curUser
這種方式。需要使用Vue.set()方法進(jìn)行修改,否則也不會(huì)觸發(fā)數(shù)據(jù)視圖的更新。
Vue.set(state, 'curUser', curUser) Vue.set(state.curUser, 'name', '張三') Vue.set(state.list, 0, "2");
// mutations,定義更新數(shù)據(jù)方法,同步操作 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屬性,并設(shè)置key
和storage
屬性,key是鍵名,storage是存儲(chǔ)位置,可以是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, }) ] })
因?yàn)閘ocalStorage不會(huì)隨著網(wǎng)頁刷新而丟失數(shù)據(jù),所以將Vuex數(shù)據(jù)狀態(tài)存儲(chǔ)在此解決刷新丟失數(shù)據(jù)的問題。如下圖,可以看到相應(yīng)的數(shù)據(jù)存儲(chǔ)。
另外,由于是明文存儲(chǔ),可能存在安全問題,可以使用以下插件對(duì)數(shù)據(jù)進(jìn)行加密存儲(chǔ)。
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), } }) ] })
加密之后,控制臺(tái)顯示如下,可以看到vuex中內(nèi)容已加密。
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: 用戶相關(guān)狀態(tài) const state = () => ({ curTheme: 'light', curUser: undefined, }) // getters: 用戶相關(guān)計(jì)算屬性,類似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進(jìn)行更新數(shù)據(jù) const actions = { //context:{ // state, 等同于store.$state,若在模塊中則為局部狀態(tài) // rootState, 等同于store.$state,只存在模塊中 // commit, 等同于store.$commit // dispatch, 等同于store.$dispatch // getters 等同于store.$getters // } // 獲取用戶信息 async getCurUser ({ state, commit }) { // dispatch('room/getRoomByUserId', value, { root: true }) // 調(diào)用另外模塊 // 獲取登錄后存儲(chǔ)在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") // 關(guān)閉socket鏈接 websocket.close(); return; } try{ // 將token傳到后臺(tái)獲取對(duì)應(yīng)用戶信息 let res = await $api.getUserInfoByToken(); if(200 != res.code) throw new Error(res.message); localStorage.removeItem(SystemConfig.storageKey); // 登陸成功后清空會(huì)話緩存 // 獲取到用戶信息,設(shè)置到curUser狀態(tài)中 commit('SET_CUR_USER', res.data); // 設(shè)置websocket websocket.connect(state.curUser.id) } catch(err) { // 會(huì)話失效后應(yīng)該清理本地緩存 localStorage.removeItem("token"); // 關(guān)閉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) { // 設(shè)置本地緩存 localStorage.token = token; // 獲取用戶信息 await dispatch('getCurUser'); // 如果獲取用戶信息成功,則打開websocket并進(jìn)入大廳 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); // 關(guān)閉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,定義更新數(shù)據(jù)方法,同步操作 // const mutations = { SET_CUR_USER (state, curUser) { Vue.set(state, 'curUser', curUser) }, } export default { namespaced: true, state, getters, mutations, actions }
項(xiàng)目傳送門:https://github.com/louislee92/vue-module-persistedstate
到此這篇關(guān)于Vuex模塊化與持久化深入講解的文章就介紹到這了,更多相關(guān)Vuex模塊化與持久化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS 實(shí)現(xiàn)獲取對(duì)象屬性個(gè)數(shù)的方法小結(jié)
這篇文章主要介紹了JS 實(shí)現(xiàn)獲取對(duì)象屬性個(gè)數(shù)的方法,結(jié)合實(shí)例形式總結(jié)分析了JS 獲取對(duì)象屬性個(gè)數(shù)的三種常用方法,需要的朋友可以參考下2023-05-05VUE 配置vue-devtools調(diào)試工具及安裝方法
vue-devtools是一款基于chrome瀏覽器的插件,用于vue應(yīng)用的調(diào)試,這款vue調(diào)試神器可以極大地提高我們的調(diào)試效率。幫助我們快速的調(diào)試開發(fā)vue應(yīng)用。這篇文章主要介紹了VUE 配置vue-devtools調(diào)試工具及安裝步驟 ,需要的朋友可以參考下2018-09-09Vue props 單向數(shù)據(jù)流的實(shí)現(xiàn)
這篇文章主要介紹了Vue props 單向數(shù)據(jù)流的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-11-11