关于页面刷新vuex数据消失问题解决方案
VBox持续进行中,哀家苦啊,有没有谁给个star。
vuex是vue用于数据存储的,和redux充当同样的角色。
最近在VBox开发的时候遇到的问题,页面刷新或者关闭浏览器打开的时候数据归零。这是头疼的问题。
网上搜,大家的方案都是把数据转移到 localStorage或者其他持久化存储(例如indexDB)。
这倒是可以,我在设计之初因为匆忙,没有考虑周全,这下好,然不成每个 mutation都去存一下。
这个弄的我很不开心,周六在公司,本来就困的要死,又想不到合理的解决方案,昏昏沉沉睡着了。
醒了后,最初想采用 柯里化和高阶函数来解决这个问题,很可惜,没有正解。
最小化修改,又不想动现有代码,代理二字最为不过。记得上次我写IBook之初,也用Proxy来拦截修改,存数据到磁盘文件。
没错方案就是 ES6的Proxy,尝试之后,确实是可以的。
源码地址
这里有两个问题
1. 初始值的问题。
2. 我要可以配置哪些字段需要持久化,store里面的数据,不代表我都需要持久化。
解决是 localStorage存储的问题,因为需要转换字符串,简单封装一个 LStorage.js,你也可以用 , 或者你喜欢的,小轮子我就自己写了。
const ls = window.localStorage // https://github./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,或者初始化就没有设置默认值,是不会被监听到的,应该是放到一级属性监听里面, 进行一个判断
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持狼蚁SEO。
参考文章
编程语言
- 如何快速学会编程 如何快速学会ug编程
- 免费学编程的app 推荐12个免费学编程的好网站
- 电脑怎么编程:电脑怎么编程网咯游戏菜单图标
- 如何写代码新手教学 如何写代码新手教学手机
- 基础编程入门教程视频 基础编程入门教程视频华
- 编程演示:编程演示浦丰投针过程
- 乐高编程加盟 乐高积木编程加盟
- 跟我学plc编程 plc编程自学入门视频教程
- ug编程成航林总 ug编程实战视频
- 孩子学编程的好处和坏处
- 初学者学编程该从哪里开始 新手学编程从哪里入
- 慢走丝编程 慢走丝编程难学吗
- 国内十强少儿编程机构 中国少儿编程机构十强有
- 成人计算机速成培训班 成人计算机速成培训班办
- 孩子学编程网上课程哪家好 儿童学编程比较好的
- 代码编程教学入门软件 代码编程教程