摘要:接下來我們會從上述兩個(gè)功能點(diǎn)出發(fā),完成一個(gè)持久化插件。在我們的持久化插件中,就是在這個(gè)函數(shù)內(nèi)部對數(shù)據(jù)進(jìn)行持久化操作。而則整個(gè)被監(jiān)聽,所以任何對于的改動都會被持久化并能夠被恢復(fù)。
在做 Vue 相關(guān)項(xiàng)目的時(shí)候,總會遇到因?yàn)轫撁嫠⑿聦?dǎo)致 Store 內(nèi)容丟失的情況。復(fù)雜的項(xiàng)目往往涉及大量的狀態(tài)需要管理,如果僅因?yàn)橐淮嗡⑿戮托枰恐匦芦@取,代價(jià)也未免太大了。
那么我們能不能對這些狀態(tài)進(jìn)行本地的持久化呢?答案是可以的,社區(qū)里也提供了不少的解決方案,如 vuex-persistedstate,vuex-localstorage 等插件,這些插件都提供了相對完善的功能。當(dāng)然除了直接使用第三方插件以外,我們自己來 DIY 一個(gè)也是非常容易的。
這個(gè)持久化插件主要有2個(gè)功能:
能夠選擇需要被持久化的數(shù)據(jù)。
能夠從本地讀取持久化數(shù)據(jù)并更新至 Store。
接下來我們會從上述兩個(gè)功能點(diǎn)出發(fā),完成一個(gè) Vuex 持久化插件。
Gist地址:https://gist.github.com/jrain...一、學(xué)習(xí)寫一個(gè) Vuex 插件
在線體驗(yàn)地址:https://codepen.io/jrainlau/p...
引用 Vuex 官網(wǎng) 的例子:
Vuex 的 store 接受 plugins 選項(xiàng),這個(gè)選項(xiàng)暴露出每次 mutation 的鉤子。Vuex 插件就是一個(gè)函數(shù),它接收 store 作為唯一參數(shù):
const myPlugin = store => { // 當(dāng) store 初始化后調(diào)用 store.subscribe((mutation, state) => { // 每次 mutation 之后調(diào)用 // mutation 的格式為 { type, payload } }) }然后像這樣使用:
const store = new Vuex.Store({ // ... plugins: [myPlugin] })
一切如此簡單,關(guān)鍵的一點(diǎn)就是在插件內(nèi)部通過 store.subscribe() 來監(jiān)聽 mutation。在我們的持久化插件中,就是在這個(gè)函數(shù)內(nèi)部對數(shù)據(jù)進(jìn)行持久化操作。
二、允許用戶選擇需要被持久化的數(shù)據(jù)首選初始化一個(gè)插件的主體函數(shù):
const VuexLastingPlugin = function ({ watch: "*", storageKey: "VuexLastingData" }) { return store => {} }
插件當(dāng)中的 watch 默認(rèn)為全選符號 *,允許傳入一個(gè)數(shù)組,數(shù)組的內(nèi)容為需要被持久化的數(shù)據(jù)的 key 值,如 ["key1", "key2"] 等。接著便可以去 store.subscribe() 里面對數(shù)據(jù)進(jìn)行持久化操作了。
const VuexLastingPlugin = function ({ watch: "*" }) { return store => { store.subscribe((mutation, state) => { let watchedDatas = {} // 如果為全選,則持久化整個(gè) state // 否則將只持久化被列出的 state if (watch === "*") { watchedDatas = state } else { watch.forEach(key => { watchedDatas[key] = state[key] }) } // 通過 localStorage 持久化 localStorage && localStorage.setItem(storageKey, JSON.stringify(watchedDatas)) }) } }
按照 Vuex 的規(guī)范,有且只有通過 mutation 才能夠修改 state,于是按照上面的步驟,我們便完成了對數(shù)據(jù)進(jìn)行實(shí)時(shí)持久化的工作。
這里也有一個(gè)小問題,就是寫入 watch 參數(shù)的數(shù)組元素必須是 state 當(dāng)中的最外層 key ,不支持形如 a.b.c 這樣的嵌套 key。這樣的功能顯然不夠完善,所以我們希望可以增加對嵌套 key 的支持。
新建一個(gè)工具函數(shù) getObjDeepValue():
function getObjDeepValue (obj, keysArr) { let val = obj keysArr.forEach(key => { val = val[key] }) return val }
該函數(shù)接收一個(gè)對象和一個(gè) key 值數(shù)組, 返回對應(yīng)的值,我們來驗(yàn)證一下:
var obj = { a: { name: "aaa", b: { name: "bbb", c: { name: "ccc" } } } } getObjDeepValue(obj, "a.b.c".split(".")) // => { name: "ccc" }
驗(yàn)證成功以后,便可以把這個(gè)工具函數(shù)也放進(jìn) store.subscribe() 里使用了:
store.subscribe((mutation, state) => { let watchedDatas = {} if (watch === "*") { watchedDatas = state } else { watch.forEach(key => { // 形如 a.b.c 這樣的 key 會被保存為 deep_a.b.c 的形式 if (data.split(".").length > 1) { watchedDatas[`deep_${key}`] = getObjDeepValue(state, key.split(".")) } else { watchedDatas[key] = state[key] } }) } localStorage && localStorage.setItem(storageKey, JSON.stringify(watchedDatas)) })
經(jīng)過這一改造,通過 watch 寫入的 key 值將支持嵌套的形式,整個(gè)插件將會更加靈活。
三、從本地讀取持久化數(shù)據(jù)并更新至 Store從上面的步驟我們已經(jīng)能夠靈活監(jiān)聽 store 里的數(shù)據(jù)并持久化它們了,接下來的工作就是完成如何在瀏覽器刷新之后去讀取本地持久化數(shù)據(jù),并把它們更新到 store。
為插件添加一個(gè)默認(rèn)為 true 的選項(xiàng) autoInit,作為是否自動讀取并更新 store 的開關(guān)。從功能上來說,刷新瀏覽器之后插件應(yīng)該自動讀取 localStorage 里面所保存的數(shù)據(jù),然后把它們更新到當(dāng)前的 store。關(guān)鍵的點(diǎn)就是如何把 deep_${key} 的值正確賦值到對應(yīng)的地方,所以我們需要再新建一個(gè)工具函數(shù) setObjDeepValue():
function setObjDeepValue (obj, keysArr, value) { let key = keysArr.shift() if (keysArr.length) { setObjDeepValue(obj[key], keysArr, value) } else { obj[key] = value } }
該函數(shù)接收一個(gè)對象,一個(gè) key 值數(shù)組,和一個(gè) value ,設(shè)置對象對應(yīng) key 的值,我們來驗(yàn)證一下:
var obj = { a: { name: "aaa", b: { name: "bbb", c: { name: "ccc" } } } } setObjDeepValue(obj, ["a", "b", "c"], 12345) /** obj = { a: { name: "aaa", b: { name: "bbb", c: 12345 } } } */
有了這個(gè)工具方法,就可以正式操作 store 了。
if (autoInit) { const localState = JSON.parse(storage && storage.getItem(storageKey)) const storeState = store.state if (localState) { Object.keys(localState).forEach(key => { // 形如 deep_a.b.c 形式的值會被賦值到 state.a.b.c 中 if (key.includes("deep_")) { let keysArr = key.replace("deep_", "").split(".") setObjDeepValue(storeState, keysArr, localState[key]) delete localState[key] } }) // 通過 Vuex 內(nèi)置的 store.replaceState 方法修改 store.state store.replaceState({ ...storeState, ...localState }) } }
上面這段代碼會在頁面初始化的時(shí)候讀取 storage 的值,然后把形如 deep_a.b.c 的值提取并賦值到 store.state.a.b.c 當(dāng)中,最后通過 store.replaceState() 方法更新整個(gè) store.state 的值。這樣便完成了從本地讀取持久化數(shù)據(jù)并更新至 Store 的功能。
四、案例測試我們可以寫一個(gè)案例,來測試下這個(gè)插件的運(yùn)行情況。
在線體驗(yàn):https://codepen.io/jrainlau/p...
App.vuestore.js{{$store.state}}
import Vue from "vue" import Vuex from "vuex" import VuexPlugin from "./vuexPlugin" Vue.use(Vuex) export default new Vuex.Store({ plugins: [VuexPlugin({ watch: ["a.b.c", "x"] })], state: { a: { name: "aaa", b: { name: "bbb", c: { name: "ccc" } } }, x: { name: "xxx" } }, mutations: { updateA (state, val) { state.a = val }, updateX (state, val) { state.x = val } } })
從案例可以看出,我們針對 state.a.b.c 和 state.x 進(jìn)行了數(shù)據(jù)持久化。在整個(gè) state.a 都被修改的情況下,僅僅只有 state.a.b.c 被存入了 localStorage ,數(shù)據(jù)恢復(fù)的時(shí)候也只修改了這個(gè)屬性。而 state.x 則整個(gè)被監(jiān)聽,所以任何對于 state.x 的改動都會被持久化并能夠被恢復(fù)。
尾聲這個(gè) Vuex 插件僅在瀏覽器環(huán)境生效,未曾考慮到 SSR 的情況。有需要的同學(xué)可以在此基礎(chǔ)上進(jìn)行擴(kuò)展,就不再展開討論了。如果發(fā)現(xiàn)文章有任何錯(cuò)誤或不完善的地方,歡迎留言和我一同探討。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/109347.html
摘要:可以進(jìn)行全局的狀態(tài)管理,但刷新后刷新后數(shù)據(jù)會消失,這是我們不愿意看到的。怎么解決呢,我們可以結(jié)合本地存儲做到數(shù)據(jù)持久化,也可以通過插件。 vuex可以進(jìn)行全局的狀態(tài)管理,但刷新后刷新后數(shù)據(jù)會消失,這是我們不愿意看到的。怎么解決呢,我們可以結(jié)合本地存儲做到數(shù)據(jù)持久化,也可以通過插件-vuex-persistedstate。 歡迎來點(diǎn)點(diǎn)我的個(gè)人博客showImg(https://user-...
摘要:眾所周知,的一個(gè)全局狀態(tài)管理的插件,但是在瀏覽器刷新的時(shí)候,內(nèi)存中的會釋放,通常的解決辦法就是用本地存儲的方式保存數(shù)據(jù),然后再初始化的時(shí)候再賦值給,手動存再手動取會覺得很麻煩,這個(gè)時(shí)候就可以使用的插件插件地址歡迎插件原理有一個(gè)方法每次在 眾所周知,vuex的一個(gè)全局狀態(tài)管理的插件,但是在瀏覽器刷新的時(shí)候,內(nèi)存中的state會釋放,通常的解決辦法就是用本地存儲的方式保存數(shù)據(jù),然后再vue...
摘要:云新聞云新聞收藏的使用需要注意的地方提交的是,而不是直接的狀態(tài)變更可以包含任意異步操作。的使用利用實(shí)現(xiàn)了簡單的聊天功能,在同一個(gè)服務(wù)器下。 title: Socket.io+vue打造新聞社區(qū)date: 2017-06-12 20:19:05 tags: [vue.js,javascript,socket.io] vue2.0 + socket.io打造一個(gè)DIY新聞社區(qū)(web第一...
摘要:深入學(xué)習(xí)作為配合使用的數(shù)據(jù)狀態(tài)管理庫,針對解決兄弟組件或多層級組件共享數(shù)據(jù)狀態(tài)的痛點(diǎn)問題來說,非常好用。至此,構(gòu)造函數(shù)部分已經(jīng)過了一遍了。 深入學(xué)習(xí)Vuex vuex作為配合vue使用的數(shù)據(jù)狀態(tài)管理庫,針對解決兄弟組件或多層級組件共享數(shù)據(jù)狀態(tài)的痛點(diǎn)問題來說,非常好用。本文以使用者的角度,結(jié)合源碼來學(xué)習(xí)vuex。其中也參考了許多前輩的文章,參見最后的Reference Vue加載Vuex...
閱讀 1564·2021-11-04 16:10
閱讀 2867·2021-09-30 09:48
閱讀 2867·2019-08-29 11:31
閱讀 1605·2019-08-28 18:22
閱讀 3248·2019-08-26 13:44
閱讀 1346·2019-08-26 13:42
閱讀 2873·2019-08-26 10:20
閱讀 781·2019-08-23 17:00