Bootstrap

Vue3下使用Vuex(store)实现响应式全局变量


本文记录了如何使用vuex建立响应式全局变量,内容有:按类别建立多模块、如何引入、使用、如何不借助第三方工具在localStorage、sessionStorage中实现数据持久化,以及vuex store state的赋值问题。

如何利用插件简化每个store的自动持久化以及pinia实现可参见:Vue3下使用pinia(store)实现状态管理(响应式全局变量)及自动持久化

1 安装

项目路径下,终端内执行

vue add vuex -S

或者

yarn install vuex -S

2 编写vuex配置文件

2.1 目录及文件结构

在这里插入图片描述

2.2 index.js文件

可以在此文件内直接编辑全局根参数,或引入模块级参数

import {createStore} from 'vuex'  
export default createStore({
  state: {
  },
  getters: {},
  mutations: {},
  actions: {},
  modules: {} 
  })

2.3 编写vuex模块级参数文件

在store/modeules目录下新建模块级参数文件。例如

/**
 * app全局变量
 *@author MuYi
 *@date 2022/3/21 8:58
 *@version 1.0
 */
export default {
  namespace: 'true',
  state() {
    return {
      /**
       * app信息
       */
      appInfo: {
        registerCompany: '请联系注册您的公司',
        version: '1.0.0',
        copyright: 'WinTown Software studio All rights reserved.',
        copyrightYear: '©2021-2022',
        author: ''
      }, 
      theme: { 
        menuMode: 'vertical', 
        colorBackground: '#009999',
      }
    }
  },
  mutations: {
    /**
     * 设置app信息
     * @param appInfo
     */
    saveAppInfo(state, appInfo) {
     state.appInfo = appInfo;
    }, 
    saveTheme(state, theme) {
      state.theme = theme;
    }
  },
  actions: {
    updateTheme(context, theme) {
      context.commit("theme", theme);
    },
    updateAppInfo(context, appInfo) {
      context.commit("appInfo", appInfo)
    }
  },
  getters: {
    theme(state) {
      return state.theme;
    },
    appInfo(state) {
      return state.appInfo;
    }
  }
}

2.4 index.js中引入模块级参数

import {createStore} from 'vuex'
import appGlobal from "@/store/modules/appGlobal";

export default createStore({
  state: {
  },
  getters: {},
  mutations: {},
  actions: {},
  modules: {
    /**
     * app全局参数
     */
    appGlobal,
  }
})

3 引入

main.js文件中

import store from './store'

4 使用

程序片段

<template>
	 <p class="appTitle ">{{appInfo.registerCompany}}</p>
	 ... ...
<script>
	  import {reactive, toRefs, computed, getCurrentInstance} from "vue";
	  import {useStore} from "vuex"
	  export default {
    	name: "LoginView",
	    setup() {
      		const appInfo = computed(() => useStore().state.appGlobal.appInfo)
      		... ...
      		return { appInfo ... ...}
    ... ...  		

5 持久化

随着浏览器的刷新,存储在auex中的数据会被自动清除。vuex中的数据持久化,可以采用vuex-persistedstate link插件实现vuex数据同步到本地。

默认自动同步所有数据,但可以实现指定字段存储。目前仅支持一种存储介质(localStorage、cookie或sessionStorage)。

出于安全考虑,store中的数据按使用类别,应存储在不同类型的本地存储介质中,建议手动编写处理方法,虽然麻烦,但自由度高。

5.1 vuex值存储在sessionStorge中

/**
* 系统非公开信息,同步保存在session中,退出应被清除
*@author MuYi
*@date 2022/3/29 16:23
*@version 1.0
*/
/**
* 初始值。
* sessionStorage不为空时,填写该值
* @param key
* @return {string}
*/
function parseInitState(key) {
 return sessionStorage.getItem(key) !=null? sessionStorage.getItem(key) : "";
}

export default {
 namespace: 'true',
 state: {
   token: parseInitState('token')
 },
 mutations: {
   /**
    * 保存令牌,同步session保存
    * @param state
    * @param token
    */
   saveToken(state, token) {
     state.token = token;
     sessionStorage.setItem("token", token);
   },
   /**
    *清空所有,同步清空session
    */
   clearSystemInfo(state) {
     state.token = "";
     sessionStorage.removeItem("token");
   }
 },
 actions: {
   saveToken(context, token) {
     context.commit("saveToken", token);
   },
   clearSystemInfo(context) {
     context.commit("clearSystemInfo")
   }

 },
 getters: {
   token(state) {
     return state.token;
   }
 }
}

5.1 vuex值存储在localStorge中

/**
 * app全局变量
 *@author MuYi
 *@date 2022/3/21 8:58
 *@version 1.0
 */
const moduleName = "theme"
const defaultTheme = {
  /**
   * element元素尺寸
   */
  size: 'small',
  /**
   * 纵向菜单
   * false/true 对应horizontal / vertical
   */
  menuIsVertical: true,
  /**
   * 背景色
   */
  colorBackground: '#009999',
  /**
   * 个人信息背景色
   */
  colorProfile: '#007777',
  /**
   * 当前项背景色
   */
  colorBackgroundActive: '#00BBBB',
  /**
   * 前景色
   */
  colorNormal: '#FFFFFF',
  /**
   * 当前项色
   */
  colorActive: '#00FFFF',
  /**
   * 图标色
   */
  colorIcon: '#eef1f6',
  /**
   * 当前项图标色
   */
  colorIconActive: '#feffff',
  /**
   * 菜单收紧
   */
  menuIsCollapse: false,
}
Object.freeze(defaultTheme)
export default {
  namespace: 'true',
  state: localStorage.getItem(moduleName) != null ?
    JSON.parse(localStorage.getItem(moduleName)) : defaultTheme,
  mutations: {
    /**
     * 设置app样式
     * @param theme
     */
    saveTheme(state, theme) {
      if (!theme.menuIsVertical && theme.menuIsCollapse) {
        theme.menuIsCollapse = false
      }
      for (let item in theme) {
        state[item]=theme[item]
      }
      localStorage.setItem(moduleName, JSON.stringify(state))
    },
    resetTheme(state) {
      for (let item in defaultTheme) {
        state[item]=defaultTheme[item]
      }
      localStorage.setItem(moduleName, JSON.stringify(state))
    },

    /**
     * 切换纵横菜单方向,当切换后为横向时自动theme.menuIsCollapse = false
     * @param state
     */
    changeMenuIsVertical(state) {
      state.menuIsVertical = !state.menuIsVertical;
      if (!state.menuIsVertical) {
        state.menuIsCollapse = false;
      }
      localStorage.setItem(moduleName, JSON.stringify(state))
    },
    changeMenuCollapse(state) {
      if (!state.menuIsVertical) {
        state.menuIsCollapse = false
      } else
        state.menuIsCollapse = !state.menuIsCollapse;
      localStorage.setItem(moduleName, JSON.stringify(state))
    },
  },
  actions: {
    saveTheme(context, theme) {
      context.commit("saveTheme", theme);
    },
    resetTheme(context) {
      context.commit("resetTheme");
    },
  },
  getters: {
    theme(state) {
      return state;
    },
  }
}

注意

初学乍道,以下总结不对的,请留言指出。

  • 使用模块化参数定义,尽量避免参数全部定义在根下。
  • state为proxy类型,如果自定义数据为json类型时,可以采用state.xxx=newValue的方式赋值,否则应采用最后例子中的方法遍历枚举赋值
  • localStorage、sessionStorage中存取数据为string类型,需要使用JSON.xxx方法转换
;