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


Posted in Javascript onJuly 03, 2017

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

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/lockr
export 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刷新的时候出现数据消失

Javascript 相关文章推荐
javascript制作2048游戏
Mar 30 Javascript
javascript实现页面刷新时自动清空表单并选中的方法
Jul 18 Javascript
jquery实现简单的表单验证
Nov 17 Javascript
jQuery插件Flexslider实现图片轮播、图文结合滑动切换效果
Apr 16 Javascript
JS实现控制文本框的内容
Jul 10 Javascript
AngularJS指令中的绑定策略实例分析
Dec 14 Javascript
jquery append与appendTo方法比较
May 24 jQuery
总结js函数相关知识点
Feb 27 Javascript
用JS实现根据当前时间随机生成流水号或者订单号
May 31 Javascript
简单了解JavaScript中常见的反模式
Jun 21 Javascript
vue中nextTick用法实例
Sep 11 Javascript
解决vux 中popup 组件Mask 遮罩在最上层的问题
Nov 03 Javascript
解决VUEX刷新的时候出现数据消失
Jul 03 #Javascript
vue.js学习之UI组件开发教程
Jul 03 #Javascript
用js将long型数据转换成date型或datetime型的实例
Jul 03 #Javascript
Vue.js实例方法之生命周期详解
Jul 03 #Javascript
基于Vuejs和Element的注册插件的编写方法
Jul 03 #Javascript
Async Validator 异步验证使用说明
Jul 03 #Javascript
在vue-cli脚手架中配置一个vue-router前端路由
Jul 03 #Javascript
You might like
php Smarty 字符比较代码
2011/02/27 PHP
php编程实现获取excel文档内容的代码实例
2011/06/28 PHP
解析php 版获取重定向后的地址(代码)
2013/06/26 PHP
PHP+Mysql实现多关键字与多字段生成SQL语句的函数
2014/11/05 PHP
php给数组赋值的实例方法
2019/09/26 PHP
tp5框架基于Ajax实现列表无刷新排序功能示例
2020/02/10 PHP
学习javascript,实现插入排序实现代码
2011/07/31 Javascript
js函数的延迟加载实现代码
2012/10/11 Javascript
jcrop基本参数一览
2013/07/16 Javascript
js日期联动示例
2014/05/02 Javascript
JavaScript link方法入门实例(给字符串加上超链接)
2014/10/17 Javascript
谈谈impress.js初步理解
2015/09/09 Javascript
JS基于Mootools实现的个性菜单效果代码
2015/10/21 Javascript
JavaScript小技巧整理
2015/12/30 Javascript
JQuery在循环中绑定事件的问题详解
2016/06/02 Javascript
js正则表达式惰性匹配和贪婪匹配用法分析
2016/12/26 Javascript
jQuery实现菜单的显示和隐藏功能示例
2018/07/24 jQuery
jQuery+PHP实现上传裁剪图片
2020/06/29 jQuery
Vue 3.0中jsx语法的使用
2020/11/13 Javascript
[50:38]DOTA2-DPC中国联赛 正赛 Phoenix vs CDEC BO3 第二场 3月7日
2021/03/11 DOTA
python函数缺省值与引用学习笔记分享
2013/02/10 Python
python通过pil将图片转换成黑白效果的方法
2015/03/16 Python
CentOS中使用virtualenv搭建python3环境
2015/06/08 Python
Python的Flask站点中集成xhEditor文本编辑器的教程
2016/06/13 Python
python增加矩阵维度的实例讲解
2018/04/04 Python
在python3中pyqt5和mayavi不兼容问题的解决方法
2019/01/08 Python
Numpy的简单用法小结
2019/08/28 Python
完美解决TensorFlow和Keras大数据量内存溢出的问题
2020/07/03 Python
高校自主招生自荐信
2013/12/09 职场文书
2014学习十八届四中全会精神思想汇报范文
2014/10/23 职场文书
2014年职称评定工作总结
2014/11/26 职场文书
婚内分居协议书范文
2014/11/26 职场文书
Python基础详解之描述符
2021/04/28 Python
Django+Nginx+uWSGI 定时任务的实现方法
2022/01/22 Python
Java界面编程实现界面跳转
2022/06/16 Java/Android
使用scrapy实现增量式爬取方式
2022/06/21 Python