页面刷新vuex数据消失问题解决方案

小编 2026-06-05 阅读:253 评论:0
VBox持续进行中,哀家苦啊,有没有谁给个star。Github地址:https://githu...

VBox持续进行中,哀家苦啊,有没有谁给个star。

Github地址:https://github.com/xiangwenhu/vbox, 欢迎大家点赞 

vuex是vue用于数据存储的,和redux充当同样的角色。

最近在VBox开发的时候遇到的问题,页面刷新或者关闭浏览器再次打开的时候数据归零。这是头疼的问题。

网上搜,大家的方案都是把数据转移到 localStorage或者其他持久化存储(例如indexDB)。

这倒是可以,我在设计之初因为匆忙,没有考虑周全,这下好,然不成每个 mutation都去存一下。

这个弄的我很不开心,周六在公司,本来就困的要死,又想不到合理的解决方案,昏昏沉沉睡着了。

醒了后,最初想采用 柯里化和高阶函数来解决这个问题,很可惜,没有正解。

最小化修改,又不想动现有代码,代理二字最为不过。记得上次我写IBook之初,也用Proxy来拦截修改,同时存数据到磁盘文件。

没错方案就是 ES6的Proxy,尝试之后,确实是可以的。

源码地址:https://github.com/xiangwenhu/vbox/tree/master/src/utils

 

这里有两个问题

1. 初始值的问题。

2. 我要可以配置哪些字段需要持久化,store里面的数据,不代表我都需要持久化。

 

首先解决是 localStorage存储的问题,因为需要转换字符串,简单封装一个 LStorage.js,当然你也可以用 https://github.com/tsironis/lockr , https://github.com/nbubna/store 或者你喜欢的,小轮子我就自己写了。

const ls = window.localStorage// https://github.com/tsironis/lockrexport default {  getItem(key) {    try {      return JSON.parse(ls.getItem(key))    } catch (err) {      return null    }  },  setItem(key, val) {    ls.setItem(key, JSON.stringify(val))  },  clear() {    ls.clear()  },  keys() {    return ls.keys()  },  removeItem(key) {    ls.removeItem(key)  }}

其次就是代理的简单封装,LSproxy.js

这个版本还是有问题的,现在只能代理二级属性,对现在的我而言已经是够用了的。

createHanlder 创建二级属性的代理

copy 复制对象,当然你可以写更加兼容优雅的方法

proxy  创建state的代理

import LStorage from './LStorage'/** * 代理二级属性 * @param {*} lsKey 存在localStorage的key * @param {*} pk    一级属性的key */function createHanlder(lsKey, pk) {  return {    set: function (target, key, value, receiver) {      let item = LStorage.getItem(lsKey)      if (item && item[pk]) {        item[pk][key] = value        LStorage.setItem(lsKey, item)      }      return Reflect.set(target, key, value, receiver)    }  }}/** * 仅仅存需要存放的数据 * @param {*} source  * @param {*} keys  */function copy(source, keys = []) {  if (!source) {    return source  }  let d = Object.create(null)  keys.forEach(k => { d[k] = source[k] })  return d}/** * 代理state * @param {*} initState 初始化的值 * @param {*} lsKey  localStorage的key * @param {*} keys   需要存储的键 */const proxy = function (initState, lsKey, keys = []) {  let ks = keys, obj = Object.assign({}, initState, LStorage.getItem(lsKey))  // 代理二级属性  keys.forEach(k => {    obj[k] = new Proxy(obj[k], createHanlder(lsKey, k))  })  // 存入合并的值  LStorage.setItem(lsKey, copy(obj, keys))  return new Proxy(obj, {    set: function (target, key, value, receiver) {      ks.indexOf(key) >= 0 && LStorage.setItem(lsKey, copy(target, keys))      return Reflect.set(target, key, value, receiver)    }  })}export { proxy }

调用这边,基本就没有什么变化, 就多了一句  state = proxy(state, 'playing', ['list'])

import { proxy } from '../utils/LSProxy'let state = {  list: [],  current: null}state = proxy(state, 'playing', ['list'])const mutations = {  /**   * 添加歌曲   * @param {*} state    * @param {*} song 歌曲信息    */  addSong(state, song) {    let index = state.list.findIndex(s => s.songmid === song.songmid)    if (index < 0) {      state.list.push(song)    }  },  /**   * 添加歌曲   * @param {*} state  内置   * @param {*} songs  歌曲列表   */  addSongs(state, songs) {    let index = -1    songs.forEach(song => {      index = state.list.findIndex(s => s.songmid === song.songmid)      if (index < 0) {        state.list.push(song)      }    })  },  /**   * 删除歌曲   * @param {*} state    * @param {*} songmid  歌曲媒体id    */  removeSong(state, songmid) {    let index = state.list.findIndex(s => s.songmid === songmid)    index >= 0 && state.list.splice(index, 1)  },  /**   * 批量删除歌曲   * @param {*} state    * @param {*} songmids 歌曲媒体列表    */  removeSongs(state, songmids = []) {    let index = -1    songmids.forEach(songmid => {      index = state.list.findIndex(s => s.songmid === songmid)      index >= 0 && state.list.splice(index, 1)    })  },  /**   * 播放下一首,   * @param {*} state    * @param {*} song 为空   */  next(state, song) {    // 如果song不为空,表示是插放,(前提是已经添加到playing)    if (song) {      let index = state.list.findIndex(s => s.songmid === song.songmid)      if (index >= 0) {        state.current = state.list[index]        return      }      return    }    // 如果current为空,表示没有播放的歌曲    if (!state.current && state.list && state.list.length > 0) {      state.current = state.list[0]      return    }    // 如果不是插放,并且current不为空    if (!song && state.current) {      // 播放的歌曲是不是在当前的列表      let index = state.list.findIndex(s => s.songmid === state.current.songmid)      // 如果在歌曲列表里面,接着播放下首      if (index >= 0) {        state.current = (index === state.list.length - 1 ? state.list[0] : state.list[index + 1])      } else {        state.current = state.list[0]      }    }  }}export default {  namespaced: true,  state,  mutations}

 

这种方案的缺点也是很明显的,

1. 代码只能代理二级,对我一般情况应该是够用了,扁平化state

2. 代理二级属性和数组,要是属性平凡修改的时候,代理是会重复触发的,比如,添加30首歌曲的时候,是发生了30次存储。 当然我觉得也是有方案可以优化的。

 

优点我觉得是,

1. state的数据与localStorage的同步过程分离开

2. 对现有代码的注入是相当少的。

 

当然我上面代码本身也还是存在问题的

1. 二级监听不能在proxy执行的时候返回,因为如果属性默认值为null/undefined,或者初始化就没有设置默认值,是不会被监听到的,应该是放到一级属性监听里面, 进行一个判断

 

不知道各位大神有什么好的方法,可以分享出来。

 

参考文章:

解决VUEX刷新的时候出现数据消失

版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表