Vuex模块化与持久化深入讲解

目录
  • 概述
  • Vuex的模块化
  • state数据状态对象
  • getters计算属性对象
  • actions异步请求对象
  • mutations数据同步对象
  • Vuex的使用方式
    • 在自定义组件中使用
    • 在自定义js文件中引用
  • Vuex持久化配置
  • main.js代码
  • modules/user.js代码

概述

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

到此这篇关于Vuex模块化与持久化深入讲解的文章就介绍到这了,更多相关Vuex模块化与持久化内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 深入理解Vuex 模块化(module)

    一.为什么需要模块化 前面我们讲到的例子都在一个状态树里进行,当一个项目比较大时,所有的状态都集中在一起会得到一个比较大的对象,进而显得臃肿,难以维护.为了解决这个问题,Vuex允许我们将store分割成模块(module),每个module有自己的state,mutation,action,getter,甚至还可以往下嵌套模块,下面我们看一个典型的模块化例子 const moduleA = { state: {....}, mutations: {....}, actions: {....},

  • Vuex 模块化使用详解

    前言 上回我们说了一下 vuex 的简单使用,最后面的时候有说了,由于使用单一状态树,应用的所有状态会集中到一个比较大的对象.当应用变得非常复杂时,store 对象就有可能变得相当臃肿. 为了解决以上问题,Vuex 允许我们将 store 分割成模块(module).每个模块拥有自己的 state.mutation.action.getter.甚至是嵌套子模块--从上至下进行同样方式的分割,今天我们也来简单了解一下他的使用,深入学习可能还是要去看官方文档 1 文件结构 文件结构的话,模块化的使用

  • Vuex 进阶之模块化组织详解

    上上篇:Vuex 入门 上一篇:Vuex 提升 自制vuex LOGO 前两篇讲解了一下 Vuex 的基本使用方法,可是在实际项目中那么写肯定是不合理的,如果组件太多,不可能把所有组件的数据都放到一个 store.js 中的,所以就需要模块化的组织 Vuex,首先看一下 项目结构. 项目结构 一.首先执行以下命令: vue init webpack-simple vuex-demo cd vuex-demo npm install npm install vuex -S npm run dev

  • Vuex模块化应用实践示例

    Vuex作为Vue全家桶的成员之一,重要性肯定不用多说,正在做Vue项目的同学,随着项目需求.功能逐渐增加,用到Vuex也是早晚的事儿,作为一个前端,只能面对现实:学不动也得学! 这篇文章主要介绍Vuex在大型项目中的模块化及持久化应用实践,下面正文开始 Vuex的应用场景 多个组件视图共享同一状态时(如登录状态等) 多个组件需要改变同一个状态时 多个组件需要互相传递参数且关系较为复杂,正常传参方式变得难以维护时 持久化存储某些数据 所以我们把组件共享的状态抽离出来,不管组件间的关系如何,都通过

  • Vuex数据持久化实现的思路与代码

    什么是vuex vuex :是一个专为vue.js开发的状态管理器,采用集中式存储的所有组件状态 五个属性: state.getters.mutations.actions.module 基本使用: 新建store.js文件,最后在main.js中引入,并挂载到实列上 import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const state = {} const getters = {} const mutations = {

  • VUEX 数据持久化,刷新后重新获取的例子

    VUEX 数据持久化 // store.js getters: { userInfo(state) { console.log('getters',state); if (!state.userInfo.id) { let token = getStorage("token"); console.log('token',token); if (token) { let userInfo = getStorage("userInfo"); state.userInfo

  • Vuex模块化与持久化深入讲解

    目录 概述 Vuex的模块化 state数据状态对象 getters计算属性对象 actions异步请求对象 mutations数据同步对象 Vuex的使用方式 在自定义组件中使用 在自定义js文件中引用 Vuex持久化配置 main.js代码 modules/user.js代码 概述 Vuex作为VUE状态管理组件,能够将项目公共数据进行统一管理.而且可以按照不同的业务功能将数据状态分模块管理.另外,对于网页刷新导致Vuex状态丢失的问题可以使用vuex-persistedstate插件配置将

  • Vuex模块化和命名空间namespaced实例演示

    1. 目的: 让代码更好维护,让多种数据分类更加明确. 2. 修改store/index.js store/index.js const countAbout = { namespaced:true,//开启命名空间 state:{x:1}, mutations: { ... }, actions: { ... }, getters: { bigSum(state){ return state.sum * 10 } } } const personAbout = { namespaced:tru

  • MySQL 数据持久化过程讲解

    目录 1. 过程简述 2. 内存中的操作 3. 磁盘的持久化 3.1 事务日志的作用 3.2 表结构的两步存储 1. 过程简述 理解MySQL数据的持久化过程,能很好的帮助我们加深对于MySQL底层的理解,在本文,我们以一种通俗的方式梳理一下这个过程,帮助大家建立起初步的认识,如果大家感兴趣,可以去深入学习与研究这个过程. MySQL数据的存储总体上可以分为两部分,内存中的存储过程以及硬盘的持久化存储,这里,就涉及到了内存中buffer poll和redo log以及磁盘上的事务日志和表结构,在

  • Vuex的五大核心详细讲解

    目录 1.什么是vuex 2.什么时候用Vuex 3.搭建vuex环境 4.五个核心 State Mutation Action getters Modules 5.四个map方法的使用 1.什么是vuex Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 2.什么时候用Vuex 多个组件依赖于同一状态. 来自不同组件的行为需要变更同一状态. Vuex 可以帮助我们管理共享状态,并

  • Vuex模块化实现待办事项的状态管理

    前言 在vue里,组件之间的作用域是独立的,父组件跟子组件之间的通讯可以通过prop属性来传参,但是在兄弟组件之间通讯就比较麻烦了.比如A组件要告诉一件事给B组件,那么A就要先告诉他们的爸组件,然后爸组件再告诉B.当组件比较多,要互相通讯的事情很多的话,爸组件要管他们那么多事,很累的.vuex正是为了解决这个问题,让多个子组件之间可以方便的通讯. 项目介绍 待办事项中的一个事件,它可能拥有几个状态,未完成.已完成.已取消或被删除等.这个事件需要在这多种状态之间切换,那么使用vuex来管理也是非常

  • vuex中数据持久化插件vuex-persistedstate使用详解

    目录 数据持久化vuex-persistedstate使用 安装起步 配置使用 自定义存储方式 想使用cookie同理 使用vuex-persistedstate插件遇到的问题 数据持久化vuex-persistedstate使用 vuex是在中大型项目中必不可少的状态管理组件,刷新会重新更新状态,但是有时候我们并不希望如此.例如全局相关的,如登录状态.token.以及一些不常更新的状态等,我们更希望能够固化到本地,减少无用的接口访问,以及更佳的用户体验. 安装起步 npm i vuex-per

随机推荐