Fairy
逼着你往前走的,不是前方梦想的微弱光芒,而是身后现实的万丈深渊。 ---------致自己

1、问题描述:

一般在登录成功的时候需要把用户信息,菜单信息放置vuex中,作为全局的共享数据。但是在页面刷新的时候vuex里的数据会重新初始化,导致数据丢失。因为vuex里的数据是保存在运行内存中的,当页面刷新时,页面会重新加载vue实例,vuex里面的数据就会被重新赋值。

2、解决思路:

办法一:将vuex中的数据直接保存到浏览器缓存中(sessionStorage、localStorage、cookie)

办法二:在页面刷新的时候再次请求远程数据,使之动态更新vuex数据

办法三:在父页面向后台请求远程数据,并且在页面刷新前将vuex的数据先保存至sessionStorage(以防请求数据量过大页面加载时拿不到返回的数据)

分析:

办法一的缺点是不安全,不适用大数据量的存储;

办法二适用于少量的数据,并且不会出现网络延迟;

办法三是要讲的重点,办法二和办法一一起使用。

3、解决过程:@1

3.1、选择合适的浏览器存储

localStorage -- 是永久存储在本地,除非你主动去删除;

sessionStorage -- 是存储到当前页面关闭为止,和其他tab页没关联;

cookie -- 则根据你设置的有效时间来存储,但缺点是不能储存大数据且不易读取,会和后台进行交互。

本方法选择的是sessionStorage,选择的原因是由于vue是单页面应用,操作都是在一个页面跳转路由,另一个原因是sessionStorage可以保证打开页面时sessionStorage的数据为空,而如果是localStorage则会读取上一次打开页面的数据。

3.2、解决方案

由于state里的数据是响应式,所以sessionStorage存储也要跟随变化,而且只能通过mutations来改变state中的值。

首先在用户登录成功之后,然后把用户信息、菜单信息通过actions分发保存至vuex中。然后在菜单页面计算vuex中state的菜单数据,将数据解析组装成前端组件所需的格式,然后渲染组件,生成菜单树。如果页面没有刷新,则一切正常。

登录成功后将用户和菜单数据同步至vuex

在菜单页面监听vuex中菜单数据

页面刷新的解决方案:

页面刷新的时候异步请求后台数据,然后动态更新vuex中的数据,其中会有一种情况就是,网络延迟、数据量大的问题。此时还没等vuex获取到后台返回的数据,页面就已经加载完成了,这样就会造成数据丢失。所以该解决方案就是,监听浏览器刷新前事件,在浏览器刷新之前就把vuex里的数据保存至sessionStorage中,刷新成功后如果异步请求的数据还没返回则直接获取sessionStorage里的数据,否则获取vuex里的数据。(只有刷新后还没取到后台数据,才会从sessionStorage里取。确保数据的安全性,就算获取sessionStorage里的数据也是安全的,因为每次刷新都会重新赋值,不必担心数据被篡改问题,其次就是对sessionStorage里的数据做了加密操作)

在父页面向后台请求数据,并且监听浏览器刷新前事件,将vuex数据保存至sessionStorage

在父页面向后台请求数据,将返回的数据分发至vuex

3、解决过程:@2 vuex-persistedstate

原理:

将vuex的state存在localStorage或sessionStorage或cookie中一份

刷新页面的一瞬间,vuex数据消失,vuex会去sessionStorage中拿回数据,变相的实现了数据刷新不丢失~

使用方法:

安装:npm install vuex-persistedstate --save

在store下的index.js中,引入并配置

import createPersistedState from "vuex-persistedstate"

const store = new Vuex.Store({
  // ...
  plugins: [createPersistedState()]
})

此时可以选择数据存储的位置,可以是localStorage/sessionStorage/cookie,此处以存储到sessionStorage为例,配置如下:

import createPersistedState from "vuex-persistedstate"
const store = new Vuex.Store({
  // ...
  plugins: [createPersistedState({
      storage: window.sessionStorage
  })]
})

存储指定state:

vuex-persistedstate默认持久化所有state,指定需要持久化的state,配置如下:

import createPersistedState from "vuex-persistedstate"
const store = new Vuex.Store({
  // ...
  plugins: [createPersistedState({
      storage: window.sessionStorage,
      reducer(val) {
          return {
          // 只储存state中的user
          user: val.user
        }
     }
  })]

此刻的val 对应store/modules文件夹下几个js文件存储的内容,也就是stor/index中import的几个模块,可以自己打印看看。希望哪一部分的数据持久存储,将数据的名字在此配置就可以,目前我只想持久化存储user模块的数据。

注意:如果此刻想配置多个选项,将plugins写成一个一维数组,不然会报错。

import createPersistedState from "vuex-persistedstate"
import createLogger from 'vuex/dist/logger'
// 判断环境 vuex提示生产环境中不使用
const debug = process.env.NODE_ENV !== 'production'
const createPersisted = createPersistedState({
  storage: window.sessionStorage
})
export default new Vuex.Store({
 // ...
  plugins: debug ? [createLogger(), createPersisted] : [createPersisted]
})

3、解决过程:@3 vuex-along

将state里的数据保存一份到本地存储(localStorage、sessionStorage、cookie)

//App.vue

<script>
    export default{
        created() {
            //在页面加载时读取sessionStorage里的状态信息
             if (sessionStorage.getItem("store") ) {
                this.$store.replaceState(Object.assign({}, this.$store.state,JSON.parse(sessionStorage.getItem("store"))))
            }

            //在页面刷新时将vuex里的信息保存到sessionStorage里
            window.addEventListener("beforeunload",()=>{
                sessionStorage.setItem("store",JSON.stringify(this.$store.state))
            })
        }
    }
</script>

2.使用第三方插件如vuex-along(本质上还是利用session等缓存到本地)

import Vue from 'vue'
import Vuex from 'vuex'
import VueXAlong from 'vuex-along'
Vue.use(Vuex)
let productData = require('@/assets/api/list.json')

const moduleA = {
    state: {
        a1: 'a1',
        a2: 'a2',
    }
}
export default new Vuex.Store({
    state: {
        productList: [],//列表数据
        cartList: [],//购物车数据
    },
    mutations: {
        //产品列表
        setProductList(state, data){
            state.productList = data
        },
        //加入购物车
        setCartList(state, id){
            let  cartIndex = state.cartList.findIndex(item => item.id === id)
            if(cartIndex < 0){
                state.cartList.push({id, count: 1})
            }else{
                state.cartList[cartIndex].count++
            }
        },
        //删除购物车商品
        deleteCartList(state, id){
            let  cartIndex = state.cartList.findIndex(item => item.id === id)
            state.cartList.splice(cartIndex, 1)
        },
        //编辑购物车商品数量
        editCartList(state, data){
            let cartIndex = state.cartList.findIndex(item => item.id === data.id)
            state.cartList[cartIndex].count = data.count
        },
        clearCart(state){
            state.cartList = []
        },
    },
    actions: {
        getProductList(context){
            //模拟ajax请求,将返回的信息直接存储在store
            setTimeout(()=>{
                context.commit('setProductList', productData)
            }, 2000)
        },
        buy(context){
            //模拟ajax请求通过返回promise对象将结果返回给操作提交的地方
            return new Promise((resolve, reject) => {
                setTimeout(()=>{
                    context.commit('clearCart')
                    resolve({msg:'下单成功', code: 200})
                    //reject({message: '提交失败', code: 300})
                }, 1000)
            })
        }
    },
    modules: {
        ma: moduleA
    },
    //缓存state的数据到storage
    plugins: [VueXAlong()],
    //全量的参数配置(sessionStorage 数据恢复优先级高于 localStorage)
    /* plugins: [VueXAlong({
        // 设置保存的集合名字,避免同站点下的多项目数据冲突
        name: 'my-app-VueXAlong',
        //过滤ma的数据将其他的数据存入localStorage
        local: {
            list: ['ma'],//需要监听的属性名或者模块名
            isFilter: true,//是否过滤而非保存
        },
        //保存ma的a1数据到sessionStorage
        session: {
            list: ['ma.a1'],
            isFilter: false,
        },
        //仅使用sessionStorage保存
        //justSession: true,
    })] */
})
posted on 2022-01-04 23:14  Fairy-02  阅读(907)  评论(0编辑  收藏  举报