Vue
项目中使用Vuex
作为状态管理已经是比较通用的做法了,Vuex
本质上类似全局的变量存储,方便在所有Vue
组件中共享,而且也可以动态改变状态。
目前使用Vuex
可以把登录数据保存到localStorage
中,实现短时间内免登陆。
存储localStorage
参考:Vue项目集成vuex-persistedstate
但是Vuex
似乎没有考虑提供一个重置或者清空state
的方法,需要自己来实现。
清理登录数据
注销时需要清理store
比较麻烦,store
是在内存中,哪怕是删除localStorage
,也可能再次把内存中的store
数据写入localStorage
,造成注销失败。
直接删除localStorage
可以直接删除localStorage
,删除后还需要刷新页面,似乎体验不是很好,整个页面会刷新。
window.localStorage.removeItem(STORAGE_KEY) window.location.reload()
重置store
还是考虑从store
下手,重置store
的思路如下:
- 在
store
初始化的时候把store
中的初始state
存下来 - 给
actions
和mutations
增加一个重置(__resetStoreState
)方法,并把重置方法名称保存到重置方法列表中 - 如果有子模块,递归处理子模块,重复第2步骤
- 提供一个外部访问的重置方法,具体逻辑就是把重置方法列表中的重置方法触发一下。
具体实现
具体代码如下(StoreHelper.js
),依赖lodash
库:
import cloneDeep from 'lodash/cloneDeep' import store from '@/store' import Vuex from 'vuex' const TO_RESET_STORE_STATE_KEYS = [] // 需要清理的Key const RESET_STORE_STATE_KEY = '__resetStoreState' // 内部清理方法名 /** * store重置初始化,递归处理子模块,并把需要重置的key保存下来 * * @param configData store数据 * @param excludeNames 不清理的模块名,有些数据可能需要在注销时也不清理 * @param parentModuleKey 模块名拼接,处理子模块 */ export function $createStoreWithResetHandler (configData, excludeNames = [], parentModuleKey = '') { const resetStoreKey = parentModuleKey ? `${parentModuleKey}/${RESET_STORE_STATE_KEY}` : RESET_STORE_STATE_KEY TO_RESET_STORE_STATE_KEYS.push(resetStoreKey) // reset相关key存入数组中 if (configData.modules) { Object.keys(configData.modules).forEach(moduleName => { if (excludeNames.indexOf(moduleName) === -1) { const module = configData.modules[moduleName] const newParentModuleKey = parentModuleKey ? `${parentModuleKey}/${moduleName}` : moduleName $createStoreWithResetHandler(module, excludeNames, newParentModuleKey) } }) } if (configData.state) { configData.mutations = configData.mutations || {} configData.actions = configData.actions || {} if (!configData.mutations[RESET_STORE_STATE_KEY]) { const initialState = cloneDeep(configData.state) configData.mutations[RESET_STORE_STATE_KEY] = state => Object.assign(state, cloneDeep(initialState)) } if (!configData.actions[RESET_STORE_STATE_KEY]) { configData.actions[RESET_STORE_STATE_KEY] = ({ commit }) => commit(RESET_STORE_STATE_KEY) } } return configData } /** * 重置store方法 * * @returns Promise */ export function $resetStoreState () { // 需要重置的key已经存下来,循环key触发重置 const resetResults = TO_RESET_STORE_STATE_KEYS.map(resetKey => store.dispatch(resetKey)) return Promise.allSettled(resetResults) } /** * 包装store,方便使用 * * @param configData * @param excludeNames * @returns {Store<unknown>} */ export function $wrapStore (configData, excludeNames = []) { return new Vuex.Store($createStoreWithResetHandler(configData, excludeNames)) } /** * 导出$resetStoreState方法,方便使用 * Vue.use(StoreHelper) */ export default { install: (Vue, options = {}) => Object.assign(Vue.prototype, { $resetStoreState }) }
包装store搜集数据
包装store
搜集state
数据,以及需要触发reset
的相关方法
store.js
默认代码(去掉import
):
Vue.use(Vuex) export default new Vuex.Store({ state: {}, mutations: {}, actions: {}, modules: { Common: CommonStore, Theme: ThemeStore }, plugins: [createPersistedState()] })
修改代码也比较简单,把new Vuex.Store
换成$wrapStore
,如果部分模块不要reset
,可以过滤掉:
Vue.use(Vuex) Vue.use(StoreHelper) export default $wrapStore({ state: {}, mutations: {}, actions: {}, modules: { Common: CommonStore, Theme: ThemeStore }, plugins: [createPersistedState()] }, ['Common']) // Common模块不要reset
触发store重置
在需要的地方触发一下重置,比如注销,然后跳转到登录页面即可
this.$resetStoreState().then(() => { // 跳转到登录页面 })