【前端】Vuex模块化和持久化应用示例

news2024/9/24 17:15:52

概述

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";
/* eslint-disable no-new */
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)四个参数。前两个参数stategetters是本模块中的数据状态对象和计算属性对象,可在方法中按照如下格式进行引用。

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 '';
	}
  }
}

后两个参数rootStaterootGetters可以用来访问根和其他模块的数据状态和计算属性。比如下面是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属性,并设置keystorage属性,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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/137423.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

「Python|场景案例」如何给图片添加水印

本文主要介绍如何使用python的PIL库给图片增加水印 文章目录背景说明工具准备处理步骤源代码处理效果展示背景说明 当我们想给一些图片添加水印的时候&#xff0c;尤其是图片数量较多的时候&#xff0c;就可以使用python进行自动化处理。包括但不限于在自媒体上发布自己的各种…

如何使用自助式商业智能 (BI) 避免组织中的数据孤岛

许多组织都存在数据问题。当许多员工远程工作&#xff08;或在混合环境中&#xff09;并在多个位置使用多个设备访问公司数据时&#xff0c;他们正在处理信息过载问题。这只会加剧数据孤岛的问题。 数据孤岛正是它听起来的样子&#xff1a;孤立在一个孤立的用户/环境中的数据&…

jdk版本和Class编译版本对应关系

JDK version和class file version(Class编译版本号)对应关系 JDK 17 61, JDK 16 60, JDK 15 59, JDK 14 58, JDK 13 57, JDK 12 56, JDK 11 55, JDK 10 54, JDK 9 53, JDK 8 52, JDK 7 51, JDK 6.0 50, JDK 5.0 …

数据结构-归并排序

一、概念及其介绍 归并排序&#xff08;Merge sort&#xff09;是建立在归并操作上的一种有效、稳定的排序算法&#xff0c;该算法是采用分治法(Divide and Conquer&#xff09;的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每…

三年“云改”,移动云这份答卷有多“硬”?

作者 | 曾响铃 文 | 响铃说 云计算是推动数字经济与实体经济深度融合的催化剂&#xff0c;是重点领域数字产业发展的助推器。近年来我国云计算产业发展提速&#xff0c;加快推动实体企业转型升级和创新发展。 2022年是移动云实施“云改”战略的第3年&#xff0c;也是移动云全…

【人工智能】基于五笔字型规范和人工神经网络的简中汉字识别【二】

环境搭建 一、NVIDIA驱动安装与更新二、Anaconda安装三、Pytorch安装四、验证CUDA和cudnn版本一、NVIDIA驱动安装与更新 显卡驱动就是用来驱动显卡的程序,它是硬件所对应的软件。 正常情况下,配有显卡的电脑都是安装有驱动程序的,但是有的时候驱动可能版本较低,所支持的 …

爬虫内容学习-工具类---Selenium

一、爬虫学习建议&#xff1a; 在编写python爬虫程序时&#xff0c;只需要做以下两件事&#xff1a; 发送GET请求&#xff0c;获取HTML [第一类] 解析HTML&#xff0c;获取数据 [第二类] 这两件事&#xff0c;python都有相应的库帮你去做&#xff0c;你只需要知道…

关乎你我,2022年都经历了哪些安全事件?|上云那些事

2022年&#xff0c;网络安全跟人们的工作生活关联愈发紧密。腾讯安全联合南方日报、南方&#xff0c;携手共建《上云那些事》栏目&#xff0c;为企业提供网络安全建设新思路&#xff0c;帮助大家及时发现身边的网络安全陷阱&#xff0c;提升安全意识和防范能力&#xff0c;减少…

论文阅读-虚假信息检测综述 - Fake News Detection on Social Media: A Data Mining Perspective

论文链接&#xff1a;https://arxiv.org/pdf/1708.01967.pdf 目录 摘要 1 引言 2. 假新闻定义 2.1 假新闻的定义 2.2 传统新闻媒体上的假新闻 2.3社交媒体上的假新闻 3.假新闻检测 3.1问题定义 3.2 特征提取 3.2.1 新闻内容特征 3.2.2 社会语境特征 3.3 模型构建 …

浅谈DNS域名解析的过程

用户在浏览器输入www.baidu.com时&#xff0c;DNS域名解析大致分为以下几个过程: 浏览器客户端检查自身有没有该域名的缓存&#xff1a; 如果浏览器有命中&#xff0c;直接返回该域名对应的IP地址&#xff0c;解析结束; (这个缓存可以设置TTL来控制有效时间&#xff0c;有点像A…

用C++求两个数的最大公约数和最小公倍数。(数论的基础思想)

目录原理最大公约数最小公倍数代码运行结果原理 最大公约数 有两个数字n和m。现在要求两个数字的最大公约数。 例如&#xff1a;n为18&#xff0c;m为4. 正常我们的思路求解最大公约数是暴力破解&#xff0c;遍历一遍公约数&#xff0c;取最大的那个&#xff0c;但是这样有一…

关闭Mac的Microsoft AutoUpdate弹框提示

macOS安装Microsoft Office for Mac之后&#xff0c;有时候会弹出Microsoft Auto Update微软应用自动更新工具。就像下面这样&#xff1a;&#xff08;我不知道您会不会烦&#xff0c;我是烦了&#xff09; 如果您也和我一样&#xff0c;不喜欢这样不经过允许就自动弹框的提示&…

【uiautomator2】 Android自动化测试框架

UiAutomator是Google提供的用来做安卓自动化测试的一个Java库. Uiautomator工作流程 1.在移动设备上安装atx-agent(守护进程),随后atx-agent启动uiautomator2服务(默认7912端口)进行监听 2.在PC上编写测试脚本并执行(相当于发送HTTP请求到移动设备的server端) 3.移动设备通过Wi…

python import失败解决方案

错误从何而起? 此时有这么一个目录结构 我们从a/b下执行python.exe ./main.py命令开始 案例: a/b/main.py导入a/package if __name__ "__main__":# 报错 ModuleNotFoundError: No module named packageimport package # 报错 ImportError: attempted relative…

Netty网络编程 - NIO基础

一. NIO 基础 non-blocking io 非阻塞 IO 1. 三大组件 1.1 Channel & Buffer channel 有一点类似于 stream&#xff0c;它就是读写数据的双向通道&#xff0c;可以从 channel 将数据读入 buffer&#xff0c;也可以将 buffer 的数据写入 channel&#xff0c;而之前的 st…

时间序列分析之auto_arima自动调参

背景 我们在进行ARIMA建模时&#xff0c;有一个非常重要的事情就是确定其中超参数p, d, q。 一般的流程需要先根据平稳性来确认差分的阶数d&#xff0c;然后根据平稳序列来观察ACF图和PACF图来确认p和q&#xff0c;当然中间还要根据网格训练查看AIC的值来确认&#xff0c;真个…

软件设计模式-行为型模式

行为型模式 行为型模式是对在不同的对象之间划分责任和算法的抽象化通过行为型模式&#xff0c;可以更加清晰地划分类与对象的职责&#xff0c;并研究系统在运行时实例对象之间的交互。在系统运行时&#xff0c;对象并不是孤立的&#xff0c;他们可以通过相互通信与协作完成某…

数据赋能的未来,看向嵌入式BI

数据分析能力越来越成为消费者和企业的必备品应用程序&#xff0c;复杂程度各不相同&#xff0c;从简单地一个网页或门户上托管一个可视化或仪表板&#xff0c;到在一个云服务上实现数据探索、建模、报告和可视化创建的应用程序。BI的实现方式越来越多&#xff0c;无论规模大小…

南京晓庄操作系统期末复习【大题】

操作系统期末复习大题第六章磁盘调度寻道时间与移动次数转换I/O中断请求第五章地址转换页面置换第四章动态分区地址转换第三章银行家算法处理机调度算法第二章进程同步第一章多道运行时间第六章 磁盘调度 前提小知识&#xff1a; 1.先来先服务&#xff08;FCFS&#xff09;:…

ros版本apollo7.0.0规划控制算法

apollo.ros-7.0.0 上次给大家带来了之前学习apollo时开发的内容apollo.ros-1.0.0和apollo.ros-3.0.0&#xff0c;主要是针对apollo 1.0.0和3.0.0版本进行了ros1下的移植和规划控制算法的学习。本次在之前工作的基础上&#xff0c;针对apollo 7.0.0版本&#xff0c;进行了ros1下…