關(guān)于頁(yè)面刷新vuex數(shù)據(jù)消失問(wèn)題解決方案
VBox持續(xù)進(jìn)行中,哀家苦啊,有沒(méi)有誰(shuí)給個(gè)star。
vuex是vue用于數(shù)據(jù)存儲(chǔ)的,和redux充當(dāng)同樣的角色。
最近在VBox開(kāi)發(fā)的時(shí)候遇到的問(wèn)題,頁(yè)面刷新或者關(guān)閉瀏覽器再次打開(kāi)的時(shí)候數(shù)據(jù)歸零。這是頭疼的問(wèn)題。
網(wǎng)上搜,大家的方案都是把數(shù)據(jù)轉(zhuǎn)移到 localStorage或者其他持久化存儲(chǔ)(例如indexDB)。
這倒是可以,我在設(shè)計(jì)之初因?yàn)榇颐?,沒(méi)有考慮周全,這下好,然不成每個(gè) mutation都去存一下。
這個(gè)弄的我很不開(kāi)心,周六在公司,本來(lái)就困的要死,又想不到合理的解決方案,昏昏沉沉睡著了。
醒了后,最初想采用 柯里化和高階函數(shù)來(lái)解決這個(gè)問(wèn)題,很可惜,沒(méi)有正解。
最小化修改,又不想動(dòng)現(xiàn)有代碼,代理二字最為不過(guò)。記得上次我寫(xiě)IBook之初,也用Proxy來(lái)攔截修改,同時(shí)存數(shù)據(jù)到磁盤(pán)文件。
沒(méi)錯(cuò)方案就是 ES6的Proxy,嘗試之后,確實(shí)是可以的。
源碼地址:https://github.com/xiangwenhu/vbox/tree/master/src/utils
這里有兩個(gè)問(wèn)題
1. 初始值的問(wèn)題。
2. 我要可以配置哪些字段需要持久化,store里面的數(shù)據(jù),不代表我都需要持久化。
首先解決是 localStorage存儲(chǔ)的問(wèn)題,因?yàn)樾枰D(zhuǎn)換字符串,簡(jiǎn)單封裝一個(gè) LStorage.js,當(dāng)然你也可以用 https://github.com/tsironis/lockr , https://github.com/nbubna/store 或者你喜歡的,小輪子我就自己寫(xiě)了。
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)
}
}
其次就是代理的簡(jiǎn)單封裝,LSproxy.js
這個(gè)版本還是有問(wèn)題的,現(xiàn)在只能代理二級(jí)屬性,對(duì)現(xiàn)在的我而言已經(jīng)是夠用了的。
createHanlder 創(chuàng)建二級(jí)屬性的代理
copy 復(fù)制對(duì)象,當(dāng)然你可以寫(xiě)更加兼容優(yōu)雅的方法
proxy 創(chuàng)建state的代理
import LStorage from './LStorage'
/**
* 代理二級(jí)屬性
* @param {*} lsKey 存在localStorage的key
* @param {*} pk 一級(jí)屬性的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)
}
}
}
/**
* 僅僅存需要存放的數(shù)據(jù)
* @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 需要存儲(chǔ)的鍵
*/
const proxy = function (initState, lsKey, keys = []) {
let ks = keys, obj = Object.assign({}, initState, LStorage.getItem(lsKey))
// 代理二級(jí)屬性
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 }
調(diào)用這邊,基本就沒(méi)有什么變化, 就多了一句 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 內(nèi)置
* @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不為空,表示是插放,(前提是已經(jīng)添加到playing)
if (song) {
let index = state.list.findIndex(s => s.songmid === song.songmid)
if (index >= 0) {
state.current = state.list[index]
return
}
return
}
// 如果current為空,表示沒(méi)有播放的歌曲
if (!state.current && state.list && state.list.length > 0) {
state.current = state.list[0]
return
}
// 如果不是插放,并且current不為空
if (!song && state.current) {
// 播放的歌曲是不是在當(dāng)前的列表
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
}
這種方案的缺點(diǎn)也是很明顯的,
1. 代碼只能代理二級(jí),對(duì)我一般情況應(yīng)該是夠用了,扁平化state
2. 代理二級(jí)屬性和數(shù)組,要是屬性平凡修改的時(shí)候,代理是會(huì)重復(fù)觸發(fā)的,比如,添加30首歌曲的時(shí)候,是發(fā)生了30次存儲(chǔ)。 當(dāng)然我覺(jué)得也是有方案可以?xún)?yōu)化的。
優(yōu)點(diǎn)我覺(jué)得是,
1. state的數(shù)據(jù)與localStorage的同步過(guò)程分離開(kāi)
2. 對(duì)現(xiàn)有代碼的注入是相當(dāng)少的。
當(dāng)然我上面代碼本身也還是存在問(wèn)題的
1. 二級(jí)監(jiān)聽(tīng)不能在proxy執(zhí)行的時(shí)候返回,因?yàn)槿绻麑傩阅J(rèn)值為null/undefined,或者初始化就沒(méi)有設(shè)置默認(rèn)值,是不會(huì)被監(jiān)聽(tīng)到的,應(yīng)該是放到一級(jí)屬性監(jiān)聽(tīng)里面, 進(jìn)行一個(gè)判斷
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
參考文章:
解決VUEX刷新的時(shí)候出現(xiàn)數(shù)據(jù)消失
- 詳解Vue中使用Echarts的兩種方式
- 使用Vue開(kāi)發(fā)動(dòng)態(tài)刷新Echarts組件的教程詳解
- vue結(jié)合Echarts實(shí)現(xiàn)點(diǎn)擊高亮效果的示例
- vue中echarts3.0自適應(yīng)的方法
- 在Vue中使用echarts的方法
- 在vue中通過(guò)axios異步使用echarts的方法
- 解決vue 路由變化頁(yè)面數(shù)據(jù)不刷新的問(wèn)題
- 詳解刷新頁(yè)面vuex數(shù)據(jù)不消失和不跳轉(zhuǎn)頁(yè)面的解決
- vue使用keep-alive實(shí)現(xiàn)數(shù)據(jù)緩存不刷新
- 解決VUEX刷新的時(shí)候出現(xiàn)數(shù)據(jù)消失
- vue.js中使用echarts實(shí)現(xiàn)數(shù)據(jù)動(dòng)態(tài)刷新功能
相關(guān)文章
iview 權(quán)限管理的實(shí)現(xiàn)
本文主要介紹了iview 權(quán)限管理,iview-admin2.0自帶權(quán)限管理??梢酝ㄟ^(guò)設(shè)置路由的meta對(duì)象的參數(shù)access來(lái)分配權(quán)限。感興趣的可以了解一下2021-07-07
vue中定時(shí)器setInterval的使用及說(shuō)明
這篇文章主要介紹了vue中定時(shí)器setInterval的使用及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
Vue.js 2.0和Cordova開(kāi)發(fā)webApp環(huán)境搭建方法
下面小編就為大家分享一篇Vue.js 2.0和Cordova開(kāi)發(fā)webApp環(huán)境搭建方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
使用Vue手寫(xiě)一個(gè)對(duì)話(huà)框
相信大家之前都寫(xiě)過(guò)一些組件,尤其是這樣的彈窗組件,這篇文章主要來(lái)和大家聊聊如何使用Vue手寫(xiě)一個(gè)對(duì)話(huà)框,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-04-04
vue-cli3 取消eslint校驗(yàn)代碼的解決辦法
這篇文章主要介紹了vue-cli3 取消eslint校驗(yàn)代碼的解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01
在vue react中如何使用Web Components組件
這篇文章主要介紹了在vue react中如何使用Web Components組件問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05
vue項(xiàng)目初始化過(guò)程中錯(cuò)誤總結(jié)
在Vue.js項(xiàng)目初始化和構(gòu)建過(guò)程中,可能會(huì)遇到多種問(wèn)題,首先,npm?install過(guò)程中報(bào)錯(cuò),如提示“No?such?file?or?directory”,建議刪除package-lock.json文件后重新安裝,在build或run時(shí),若出現(xiàn)core-js相關(guān)錯(cuò)誤2024-09-09
vue+element-ui+ajax實(shí)現(xiàn)一個(gè)表格的實(shí)例
下面小編就為大家分享一篇vue+element-ui+ajax實(shí)現(xiàn)一個(gè)表格的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-03-03

