摘要:接下來我們會從上述兩個功能點出發,完成一個持久化插件。在我們的持久化插件中,就是在這個函數內部對數據進行持久化操作。而則整個被監聽,所以任何對于的改動都會被持久化并能夠被恢復。
在做 Vue 相關項目的時候,總會遇到因為頁面刷新導致 Store 內容丟失的情況。復雜的項目往往涉及大量的狀態需要管理,如果僅因為一次刷新就需要全部重新獲取,代價也未免太大了。
那么我們能不能對這些狀態進行本地的持久化呢?答案是可以的,社區里也提供了不少的解決方案,如 vuex-persistedstate,vuex-localstorage 等插件,這些插件都提供了相對完善的功能。當然除了直接使用第三方插件以外,我們自己來 DIY 一個也是非常容易的。
這個持久化插件主要有2個功能:
能夠選擇需要被持久化的數據。
能夠從本地讀取持久化數據并更新至 Store。
接下來我們會從上述兩個功能點出發,完成一個 Vuex 持久化插件。
Gist地址:https://gist.github.com/jrain...一、學習寫一個 Vuex 插件
在線體驗地址:https://codepen.io/jrainlau/p...
引用 Vuex 官網 的例子:
Vuex 的 store 接受 plugins 選項,這個選項暴露出每次 mutation 的鉤子。Vuex 插件就是一個函數,它接收 store 作為唯一參數:
const myPlugin = store => { // 當 store 初始化后調用 store.subscribe((mutation, state) => { // 每次 mutation 之后調用 // mutation 的格式為 { type, payload } }) }然后像這樣使用:
const store = new Vuex.Store({ // ... plugins: [myPlugin] })
一切如此簡單,關鍵的一點就是在插件內部通過 store.subscribe() 來監聽 mutation。在我們的持久化插件中,就是在這個函數內部對數據進行持久化操作。
二、允許用戶選擇需要被持久化的數據首選初始化一個插件的主體函數:
const VuexLastingPlugin = function ({ watch: "*", storageKey: "VuexLastingData" }) { return store => {} }
插件當中的 watch 默認為全選符號 *,允許傳入一個數組,數組的內容為需要被持久化的數據的 key 值,如 ["key1", "key2"] 等。接著便可以去 store.subscribe() 里面對數據進行持久化操作了。
const VuexLastingPlugin = function ({ watch: "*" }) { return store => { store.subscribe((mutation, state) => { let watchedDatas = {} // 如果為全選,則持久化整個 state // 否則將只持久化被列出的 state if (watch === "*") { watchedDatas = state } else { watch.forEach(key => { watchedDatas[key] = state[key] }) } // 通過 localStorage 持久化 localStorage && localStorage.setItem(storageKey, JSON.stringify(watchedDatas)) }) } }
按照 Vuex 的規范,有且只有通過 mutation 才能夠修改 state,于是按照上面的步驟,我們便完成了對數據進行實時持久化的工作。
這里也有一個小問題,就是寫入 watch 參數的數組元素必須是 state 當中的最外層 key ,不支持形如 a.b.c 這樣的嵌套 key。這樣的功能顯然不夠完善,所以我們希望可以增加對嵌套 key 的支持。
新建一個工具函數 getObjDeepValue():
function getObjDeepValue (obj, keysArr) { let val = obj keysArr.forEach(key => { val = val[key] }) return val }
該函數接收一個對象和一個 key 值數組, 返回對應的值,我們來驗證一下:
var obj = { a: { name: "aaa", b: { name: "bbb", c: { name: "ccc" } } } } getObjDeepValue(obj, "a.b.c".split(".")) // => { name: "ccc" }
驗證成功以后,便可以把這個工具函數也放進 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)) })
經過這一改造,通過 watch 寫入的 key 值將支持嵌套的形式,整個插件將會更加靈活。
三、從本地讀取持久化數據并更新至 Store從上面的步驟我們已經能夠靈活監聽 store 里的數據并持久化它們了,接下來的工作就是完成如何在瀏覽器刷新之后去讀取本地持久化數據,并把它們更新到 store。
為插件添加一個默認為 true 的選項 autoInit,作為是否自動讀取并更新 store 的開關。從功能上來說,刷新瀏覽器之后插件應該自動讀取 localStorage 里面所保存的數據,然后把它們更新到當前的 store。關鍵的點就是如何把 deep_${key} 的值正確賦值到對應的地方,所以我們需要再新建一個工具函數 setObjDeepValue():
function setObjDeepValue (obj, keysArr, value) { let key = keysArr.shift() if (keysArr.length) { setObjDeepValue(obj[key], keysArr, value) } else { obj[key] = value } }
該函數接收一個對象,一個 key 值數組,和一個 value ,設置對象對應 key 的值,我們來驗證一下:
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 } } } */
有了這個工具方法,就可以正式操作 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 內置的 store.replaceState 方法修改 store.state store.replaceState({ ...storeState, ...localState }) } }
上面這段代碼會在頁面初始化的時候讀取 storage 的值,然后把形如 deep_a.b.c 的值提取并賦值到 store.state.a.b.c 當中,最后通過 store.replaceState() 方法更新整個 store.state 的值。這樣便完成了從本地讀取持久化數據并更新至 Store 的功能。
四、案例測試我們可以寫一個案例,來測試下這個插件的運行情況。
在線體驗: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 進行了數據持久化。在整個 state.a 都被修改的情況下,僅僅只有 state.a.b.c 被存入了 localStorage ,數據恢復的時候也只修改了這個屬性。而 state.x 則整個被監聽,所以任何對于 state.x 的改動都會被持久化并能夠被恢復。
尾聲這個 Vuex 插件僅在瀏覽器環境生效,未曾考慮到 SSR 的情況。有需要的同學可以在此基礎上進行擴展,就不再展開討論了。如果發現文章有任何錯誤或不完善的地方,歡迎留言和我一同探討。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/109347.html
摘要:可以進行全局的狀態管理,但刷新后刷新后數據會消失,這是我們不愿意看到的。怎么解決呢,我們可以結合本地存儲做到數據持久化,也可以通過插件。 vuex可以進行全局的狀態管理,但刷新后刷新后數據會消失,這是我們不愿意看到的。怎么解決呢,我們可以結合本地存儲做到數據持久化,也可以通過插件-vuex-persistedstate。 歡迎來點點我的個人博客showImg(https://user-...
摘要:眾所周知,的一個全局狀態管理的插件,但是在瀏覽器刷新的時候,內存中的會釋放,通常的解決辦法就是用本地存儲的方式保存數據,然后再初始化的時候再賦值給,手動存再手動取會覺得很麻煩,這個時候就可以使用的插件插件地址歡迎插件原理有一個方法每次在 眾所周知,vuex的一個全局狀態管理的插件,但是在瀏覽器刷新的時候,內存中的state會釋放,通常的解決辦法就是用本地存儲的方式保存數據,然后再vue...
摘要:云新聞云新聞收藏的使用需要注意的地方提交的是,而不是直接的狀態變更可以包含任意異步操作。的使用利用實現了簡單的聊天功能,在同一個服務器下。 title: Socket.io+vue打造新聞社區date: 2017-06-12 20:19:05 tags: [vue.js,javascript,socket.io] vue2.0 + socket.io打造一個DIY新聞社區(web第一...
閱讀 1542·2021-11-04 16:10
閱讀 2793·2021-09-30 09:48
閱讀 2843·2019-08-29 11:31
閱讀 1584·2019-08-28 18:22
閱讀 3233·2019-08-26 13:44
閱讀 1324·2019-08-26 13:42
閱讀 2850·2019-08-26 10:20
閱讀 759·2019-08-23 17:00