关于页面刷新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 相关文章推荐
基于jquery的滚动鼠标放大缩小图片效果
Oct 27 Javascript
JSON 数字排序多字段排序介绍
Sep 18 Javascript
javascript 表格内容排序 简单操作示例代码
Jan 03 Javascript
js淡入淡出焦点图幻灯片效果代码分享
Sep 08 Javascript
jQuery事件用法详解
Oct 06 Javascript
jQuery Validate 数组 全部验证问题
Jan 12 Javascript
快速使用node.js进行web开发详解
Apr 26 Javascript
详解Node 定时器
Feb 26 Javascript
JS实现json数组排序操作实例分析
Oct 28 Javascript
Vue SPA 初次进入加载动画实现代码
Nov 14 Javascript
ES5和ES6中类的区别总结
Dec 21 Javascript
JS实现百度搜索框
Feb 25 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
Can't create/write to file 'C:\WINDOWS\TEMP\...MYSQL报错解决方法
2011/06/30 PHP
nginx+thinkphp下解决不支持pathinfo模式
2015/07/01 PHP
PHP实现单条sql执行多个数据的insert语句方法
2019/10/11 PHP
js 判断浏览器类型 去全角、半角空格 自动关闭当前窗口
2009/04/10 Javascript
jQuery ajax cache缓存问题
2010/07/01 Javascript
jQuery JSON的解析方式分享
2011/04/05 Javascript
JavaScript之IE的fireEvent方法详细解析
2013/11/20 Javascript
调用DOM对象的focus使文本框获得焦点
2014/02/19 Javascript
js打造数组转json函数
2015/01/14 Javascript
JQuery简单实现锚点链接的平滑滚动
2015/05/03 Javascript
Jquery对新插入的节点 绑定Click事件失效的解决方法
2016/06/02 Javascript
一步一步封装自己的HtmlHelper组件BootstrapHelper(二)
2016/09/14 Javascript
JS实现的简易拖放效果示例
2016/12/29 Javascript
jquery实现百叶窗效果
2017/01/12 Javascript
大白话讲解JavaScript的Promise
2017/04/06 Javascript
AngularJS 实现点击按钮获取验证码功能实例代码
2017/07/13 Javascript
[01:00:52]2018DOTA2亚洲邀请赛 4.4 淘汰赛 EG vs LGD 第一场
2018/04/05 DOTA
python的id()函数解密过程
2012/12/25 Python
python3处理含有中文的url方法
2018/05/10 Python
Python内存读写操作示例
2018/07/18 Python
Python修改文件往指定行插入内容的实例
2019/01/30 Python
python 实现的发送邮件模板【普通邮件、带附件、带图片邮件】
2019/07/06 Python
感知器基础原理及python实现过程详解
2019/09/30 Python
Python selenium的基本使用方法分析
2019/12/21 Python
解决python ThreadPoolExecutor 线程池中的异常捕获问题
2020/04/08 Python
利用python生成照片墙的示例代码
2020/04/09 Python
详解python对象之间的交互
2020/09/29 Python
Urban Outfitters英国官网:美国平价服饰品牌
2016/11/25 全球购物
蔻驰法国官网:COACH法国
2018/11/14 全球购物
春节超市活动方案
2014/08/14 职场文书
自我介绍演讲稿范文
2014/08/21 职场文书
2014年学习全国道德模范事迹思想汇报
2014/09/15 职场文书
国土资源局开展党的群众路线教育实践活动整改措施
2014/09/26 职场文书
安全承诺书
2015/01/19 职场文书
教你用Java Swing实现自助取款机系统
2021/06/11 Java/Android
SQL Server一个字符串拆分多行显示或者多行数据合并成一个字符串
2022/05/25 SQL Server