关于页面刷新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 相关文章推荐
location.href语句与火狐不兼容的问题
Jul 04 Javascript
Jquery 点击按钮显示和隐藏层的代码
Jul 25 Javascript
DIV外区域Click后关闭DIV的实现代码
Dec 21 Javascript
jQuery 回调函数(callback)的使用和基础
Feb 26 Javascript
JS实现往下不断流动网页背景的方法
Feb 27 Javascript
javascript常用的方法分享
Jul 01 Javascript
jQuery基于ID调用指定iframe页面内的方法
Jul 06 Javascript
Angular通过angular-cli来搭建web前端项目的方法
Jul 27 Javascript
js实现数组和对象的深浅拷贝
Sep 30 Javascript
学习jQuery中的noConflict()用法
Sep 28 jQuery
详解Vue中组件传值的多重实现方式
Aug 16 Javascript
解决jquery validate 验证不通过后验证正确的信息仍残留在label上的方法
Aug 27 jQuery
解决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
是否存在第一台收音机的说法
2021/03/01 无线电
php防止sql注入代码实例
2013/12/18 PHP
PHP判断远程图片是否存在的几种方法
2014/05/04 PHP
PHP实现的DES加密解密实例代码
2016/04/06 PHP
轻松掌握php设计模式之访问者模式
2016/09/23 PHP
PHP unlink与rmdir删除目录及目录下所有文件实例代码
2018/02/07 PHP
js对象之JS入门之Array对象操作小结
2011/01/09 Javascript
jqgrid 简单学习笔记
2011/05/03 Javascript
js自动闭合html标签(自动补全html标记)
2012/10/04 Javascript
JS 实现获取打开一个界面中输入的值
2013/03/19 Javascript
js实现索引图片切换效果
2015/11/21 Javascript
angularjs中ng-attr的用法详解
2016/12/31 Javascript
Vue用v-for给src属性赋值的方法
2018/03/03 Javascript
D3.js实现简洁实用的动态仪表盘的示例
2018/04/04 Javascript
JS实现图片转换成base64的各种应用场景实例分析
2018/06/22 Javascript
vue实现组件之间传值功能示例
2018/07/13 Javascript
angular2实现统一的http请求头方法
2018/08/13 Javascript
Vue axios设置访问基础路径方法
2018/09/19 Javascript
vue 关闭浏览器窗口的时候,清空localStorage的数据示例
2019/11/06 Javascript
Vue使用axios引起的后台session不同操作
2020/08/14 Javascript
解决vux 中popup 组件Mask 遮罩在最上层的问题
2020/11/03 Javascript
Python实现计算文件夹下.h和.cpp文件的总行数
2015/04/23 Python
Python安装官方whl包和tar.gz包的方法(推荐)
2017/06/04 Python
Python numpy实现数组合并实例(vstack,hstack)
2018/01/09 Python
Python Web程序部署到Ubuntu服务器上的方法
2018/02/22 Python
python爬取网易云音乐评论
2018/11/16 Python
Python实现Linux监控的方法
2019/05/16 Python
Django框架模型简单介绍与使用分析
2019/07/18 Python
浅谈PyQt5中异步刷新UI和Python多线程总结
2019/12/13 Python
GUESS西班牙官方网上商城:美国服饰品牌
2017/03/15 全球购物
刑事代理授权委托书
2014/09/17 职场文书
民主生活会对照检查材料思想汇报
2014/09/27 职场文书
大二学年个人总结
2015/03/03 职场文书
2015年车间管理工作总结
2015/07/23 职场文书
会计专业自荐信范文
2019/05/22 职场文书
python基于OpenCV模板匹配识别图片中的数字
2021/03/31 Python