VBox持续进行中,哀家苦啊,有没有谁给个star。

Github地址:https://github.com/xiangwenhu/vbox, 欢迎大家点赞 

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) >= && 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 < ) {
state.list.push(song)
}
}, /**
* 添加歌曲
* @param {*} state 内置
* @param {*} songs 歌曲列表
*/
addSongs(state, songs) {
let index = -
songs.forEach(song => {
index = state.list.findIndex(s => s.songmid === song.songmid)
if (index < ) {
state.list.push(song)
}
})
}, /**
* 删除歌曲
* @param {*} state
* @param {*} songmid 歌曲媒体id
*/
removeSong(state, songmid) {
let index = state.list.findIndex(s => s.songmid === songmid)
index >= && state.list.splice(index, )
}, /**
* 批量删除歌曲
* @param {*} state
* @param {*} songmids 歌曲媒体列表
*/
removeSongs(state, songmids = []) {
let index = -
songmids.forEach(songmid => {
index = state.list.findIndex(s => s.songmid === songmid)
index >= && state.list.splice(index, )
})
}, /**
* 播放下一首,
* @param {*} state
* @param {*} song 为空
*/
next(state, song) {
// 如果song不为空,表示是插放,(前提是已经添加到playing)
if (song) {
let index = state.list.findIndex(s => s.songmid === song.songmid)
if (index >= ) {
state.current = state.list[index]
return
}
return
}
// 如果current为空,表示没有播放的歌曲
if (!state.current && state.list && state.list.length > ) {
state.current = state.list[]
return
}
// 如果不是插放,并且current不为空
if (!song && state.current) {
// 播放的歌曲是不是在当前的列表
let index = state.list.findIndex(s => s.songmid === state.current.songmid)
// 如果在歌曲列表里面,接着播放下首
if (index >= ) {
state.current = (index === state.list.length - ? state.list[] : state.list[index + ])
} else {
state.current = state.list[]
}
}
}
} export default {
namespaced: true,
state,
mutations
}

这种方案的缺点也是很明显的,

1. 代码只能代理二级,对我一般情况应该是够用了,扁平化state

2. 代理二级属性和数组,要是属性平凡修改的时候,代理是会重复触发的,比如,添加30首歌曲的时候,是发生了30次存储。 当然我觉得也是有方案可以优化的。

优点我觉得是,

1. state的数据与localStorage的同步过程分离开

2. 对现有代码的注入是相当少的。

当然我上面代码本身也还是存在问题的

1. 二级监听不能在proxy执行的时候返回,因为如果属性默认值为null/undefined,或者初始化就没有设置默认值,是不会被监听到的,应该是放到一级属性监听里面, 进行一个判断

不知道各位大神有什么好的方法,可以分享出来。

参考文章:

解决VUEX刷新的时候出现数据消失

页面刷新vuex数据消失问题解决方案的更多相关文章

  1. 页面刷新vuex数据消失问题解决方案 之 vuex中间件

    之前我写了一篇用ES6 Proxy方案解决数据同步的文章 页面刷新vuex数据消失问题解决方案. 今天和同事沟通这个vuex数据还原问题,我说我的方法很奇异.聊着聊着,同事咋不用  store.sub ...

  2. vue:vue页面刷新vuex数据消失问题

    vuex中数据刷新页面消失问题:a页面请求的数据保存在vuex中,只要不刷新,那跳转到b页面里也可以用,但如果b页面刷新,那vuex里的数据就会消失,可以得解决方法:a页面用的数据a页面的生命周期函数 ...

  3. 页面刷新 vuex 数据重新被初始化

    1.原因 vuex里用来存储的也只是一个全局变量,当页面刷新,该全局变量自然不存在了. 2.解决 使用localStorage存储一份 (1)storage.js /** * vuex localSt ...

  4. 使用sessionStorage解决vuex在页面刷新后数据被清除的问题

    https://www.jb51.net/article/138218.htm 1.原因 2.解决方法 localStorage没有时间期限,除非将它移除,sessionStorage即会话,当浏览器 ...

  5. vue 使用localStorage解决vuex在页面刷新后数据被清除的问题

    通常,我们在使用vue编写页面时,会需要使用vuex在组件间传递(或者说共同响应)同一个数据的变化.例如:用户的登录信息. 下面,我们使用传递用户登录信息的例子来一步步解决这个问题. 首先,我们的第一 ...

  6. 【vue】使用localStorage解决vuex在页面刷新后数据被清除的问题

    通常,我们在使用vue编写页面时,会需要使用vuex在组件间传递(或者说共同响应)同一个数据的变化.例如:用户的登录信息. 下面,我们使用传递用户登录信息的例子来一步步解决这个问题. 首先,我们的第一 ...

  7. JS页面刷新保持数据不丢失

    转自:https://blog.csdn.net/qq_32439101/article/details/78134877 下面是 DOM Storage 的接口定义: interface Stora ...

  8. djano modles values+ajax实现无页面刷新更新数据

    做项目的过程中想通过不刷新页面的方式来进行页面数据刷新,开始使用http://www.cnblogs.com/ianduin/p/7761400.html方式将查询结果数据进行序列化.发现可以行,但是 ...

  9. django中form页面刷新后自动提交的解决方案

    如果一个页面包含了form,同时这个form中的提交按钮是type=submit的input的时候,你刷新该页面,就会有弹窗提示是否重新提交表单,这个特性不胜其烦,常见解决方法有两个: 第一种是前端的 ...

随机推荐

  1. Java_中建立0-10M的消息(字符串)

    直接用StringBuilder,它的append方法方便快速构建字符串. StringBuilder sb1=new StringBuilder(); for(int i=0;i<1024*1 ...

  2. 关于ONION/Wncry勒索病毒

    1.通告 2017年5月12日起,在国内外网络中发现爆发基于Windows网络共享协议进行攻击传播的蠕虫恶意代码,这是不法分子通过改造之前泄露的NSA黑客武器库中“永恒之蓝”攻击程序发起的网络攻击事件 ...

  3. Apache solr(一)

    概念:Apache Solr 是一个开源的搜索服务器.Solr 使用 Java 语言开发,主要基于 HTTP 和 Apache Lucene 实现.Apache Solr 中存储的资源是以 Docum ...

  4. 【PHP】文件写入和读取详解

    文章提纲: 一.实现文件读取和写入的基本思路 二.使用fopen方法打开文件 三.文件读取和文件写入操作 四.使用fclose方法关闭文件 五.文件指针的移动 六.Windows和UNIX下的回车和换 ...

  5. 主机设置ss代理,虚拟机共享代理

    代理的原理: 关于代理的具体的书面定义你百度谷歌可以知道.这里,我想简单通过一个例子,说明代理的原理: 假如,你在北京,但你女朋友在广州,你有东西要给你的女朋友,但是正好你这几天公司有事,所以你不能去 ...

  6. 强制解包看 Swift 的设计

    不知道大家有没有发现,在一个 Objective-C 和 Swift 混编的 App 中,当把一个 OC 中的参数转到 Swift 时,Swift 会自动把这个变量进行强制解包.举个例子,我在 OC ...

  7. EF通用数据层封装类(支持读写分离,一主多从)

    浅谈orm 记得四年前在学校第一次接触到 Ling to Sql,那时候瞬间发现不用手写sql语句是多么的方便,后面慢慢的接触了许多orm框架,像 EF,Dapper,Hibernate,Servic ...

  8. Eclipse用法:自动生成get和set方法

      方法一 Java的类中,除了常量声明为静态且公有的,一般的对象数据作用域,都是声明为私有的.这样做能保护对象的属性不会被随意改变,调试的时候也会方便很多:在类的公有方法中大一个调用栈就能看到哪里改 ...

  9. HTML5 客户端存储数据的两种方式

    HTML5 提供了两种在客户端存储数据的新方法: localStorage - 没有时间限制的数据存储 sessionStorage - 针对一个 session 的数据存储 之前,这些都是由 coo ...

  10. springMvc <form action="">提交跳转路径问题

    表单提交后,action的URL写的是, login/login.do,每次跳转后都变成login/ login/login.do,很显然是相对路径没有搞清楚. 应该弄清楚相对路径,并在JSP代码中加 ...