关于页面刷新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的document.ready更快的方法
Apr 28 Javascript
jquery必须知道的一些常用特效方法及使用示例(整理)
Jun 24 Javascript
js编码、解码函数介绍及其使用示例
Sep 05 Javascript
JS.GetAllChild(element,deep,condition)使用介绍
Sep 21 Javascript
js的隐含参数(arguments,callee,caller)使用方法
Jan 28 Javascript
js离开或刷新页面检测(且兼容FF,IE,Chrome)
Mar 05 Javascript
js 操作符汇总
Nov 08 Javascript
js贪吃蛇游戏实现思路和源码
Apr 14 Javascript
纯js实现的积木(div层)拖动功能示例
Jul 19 Javascript
javascript 作用于作用域链的详解
Sep 27 Javascript
Vue 事件处理操作实例详解
Mar 05 Javascript
Bootstrap告警框(alert)实现弹出效果和短暂显示后上浮消失的示例代码
Aug 27 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实例分享之二维数组排序
2014/05/15 PHP
php制作简单模版引擎
2016/04/07 PHP
php遍历、读取文件夹中图片并分页显示图片的方法
2016/11/15 PHP
prototype Element学习笔记(篇一)
2008/10/26 Javascript
Javascript 事件流和事件绑定
2009/07/16 Javascript
jquery 全局AJAX事件使用代码
2010/11/05 Javascript
JavaScript高级程序设计(第3版)学习笔记5 js语句
2012/10/11 Javascript
jQuery实现的fixedMenu下拉菜单效果代码
2015/08/24 Javascript
javascript中return,return true,return false三者的用法及区别
2015/11/17 Javascript
全面解析Bootstrap中tooltip、popover的使用方法
2016/06/13 Javascript
基于JS如何实现给字符加千分符(65,541,694,158)
2016/08/03 Javascript
Angular.Js中过滤器filter与自定义过滤器filter实例详解
2017/05/08 Javascript
ztree简介_动力节点Java学院整理
2017/07/19 Javascript
浅谈如何使用webpack构建多页面应用
2018/05/30 Javascript
vue watch深度监听对象实现数据联动效果
2018/08/16 Javascript
vue使用el-upload上传文件及Feign服务间传递文件的方法
2019/03/15 Javascript
vue 实现v-for循环回来的数据动态绑定id
2019/11/07 Javascript
Js和VUE实现跑马灯效果
2020/05/25 Javascript
JavaScript Html实现移动端红包雨功能页面
2021/01/10 Javascript
Python win32com 操作Exce的l简单方法(必看)
2017/05/25 Python
浅谈dataframe中更改列属性的方法
2018/07/10 Python
Python计算不规则图形面积算法实现解析
2019/11/22 Python
django-csrf使用和禁用方式
2020/03/13 Python
英国DIY汽车维修配件网站:DIY Car Service Parts
2019/08/30 全球购物
如何利用find命令查找文件
2015/02/07 面试题
机电专业大学生求职信
2013/10/04 职场文书
高分子材料与工程专业个人求职信
2013/12/15 职场文书
竞选班干部的演讲稿
2014/04/24 职场文书
求职信结尾怎么写
2014/05/26 职场文书
计生专干事迹
2014/05/28 职场文书
债务追讨授权委托书范本
2014/10/16 职场文书
党的群众路线教育实践活动实施方案
2014/10/31 职场文书
熟背这些句子,让您的英语口语突飞猛进(135句)
2019/09/06 职场文书
详解Mysql 函数调用优化
2021/04/07 MySQL
python使用openpyxl库读写Excel表格的方法(增删改查操作)
2021/05/02 Python
Spring Cloud Netflix 套件中的负载均衡组件 Ribbon
2022/04/13 Java/Android