关于页面刷新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 相关文章推荐
js或css实现滚动广告的几种方案
Jan 28 Javascript
JS Excel读取和写入操作(模板操作)实现代码
Apr 11 Javascript
基于jquery的跟随屏幕滚动代码
Jul 24 Javascript
Javascript 多浏览器兼容总结(实战经验)
Oct 30 Javascript
jquery实现简单的banner轮播效果【实例】
Mar 30 Javascript
JS中关于事件处理函数名后面是否带括号的问题
Nov 16 Javascript
使用node.js中的Buffer类处理二进制数据的方法
Nov 26 Javascript
微信小程序promsie.all和promise顺序执行
Oct 27 Javascript
图文详解vue框架安装步骤
Feb 12 Javascript
JS实现继承的几种常用方式示例
Jun 22 Javascript
《javascript设计模式》学习笔记七:Javascript面向对象程序设计组合模式详解
Apr 08 Javascript
微信小游戏中three.js离屏画布的示例代码
Oct 12 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
php5中类的学习
2008/03/28 PHP
PHP获取windows登录用户名的方法
2014/06/24 PHP
用php+ajax新建流程(请假、进货、出货等)
2017/06/11 PHP
extjs DataReader、JsonReader、XmlReader的构造方法
2009/11/07 Javascript
Linux下编译安装php libevent扩展实例
2015/02/14 Javascript
Javascript优化技巧之短路表达式详细介绍
2015/03/27 Javascript
JQuery实现鼠标移动图片显示描述层的方法
2015/06/25 Javascript
全面解析Bootstrap表单使用方法(表单控件状态)
2015/11/24 Javascript
Node.js 条形码识别程序构建思路详解
2016/02/14 Javascript
jQuery+CSS实现简单切换菜单示例
2016/07/27 Javascript
AngularJS之依赖注入模拟实现
2016/08/19 Javascript
js实现表单及时验证功能 用户信息立即验证
2016/09/13 Javascript
Javascript生成带参数的二维码示例
2016/10/10 Javascript
AngularJS基于ui-route实现深层路由的方法【路由嵌套】
2016/12/14 Javascript
React实现点击删除列表中对应项
2017/01/10 Javascript
JavaScript中正则表达式使数字、中文或指定字符高亮显示
2017/10/31 Javascript
JS动态插入脚本和插入引用外部链接脚本的方法
2018/05/21 Javascript
angular实现input输入监听的示例
2018/08/31 Javascript
CSS3 动画卡顿性能优化的完美解决方案
2018/09/20 Javascript
基于javascript canvas实现五子棋游戏
2020/07/08 Javascript
Python中的生成器和yield详细介绍
2015/01/09 Python
在Python中使用SQLite的简单教程
2015/04/29 Python
python正则表达式之作业计算器
2016/03/18 Python
Python基于property实现类的特性操作示例
2018/06/15 Python
python实现NB-IoT模块远程控制
2018/06/20 Python
Tensorflow 同时载入多个模型的实例讲解
2018/07/27 Python
python 执行文件时额外参数获取的实例
2018/12/18 Python
python3实现绘制二维点图
2019/12/04 Python
西铁城美国官方网站:Citizen Watch美国
2019/11/08 全球购物
德国婴儿服装和婴儿用品购买网站:Baby Sweets
2019/12/08 全球购物
Java如何支持I18N?
2016/10/31 面试题
营销总经理的岗位职责
2013/12/15 职场文书
老师自我鉴定范文
2013/12/25 职场文书
酒店中秋节活动方案
2014/01/31 职场文书
六查六看自查材料
2014/02/17 职场文书
2014年企业员工工作总结
2014/12/09 职场文书