對於 PC 端的 VUE 項目來講,刷新頁面導致數據消失是一個繞不開的坑。好在 vuex-persistedstate插件能夠解決這個問題。
vuex-persistedstate
它的原理是:
- 每次 mutation 都將整個 store 保存到本地(localStorage/sessionStorage/cookie);
- 初始化時用本地數據替換(replaceState)掉 store。
它的代碼簡潔、邏輯清晰,且對業務代碼毫無侵入,可以說是我輩楷模。(劇終...
今天,咱就雞蛋里挑骨頭,站在個人的角度上主觀的評評這個方案的缺點。
第一:和 Vuex 綁定,對於大多數項目來說,用 Vuex 基本上是白白增加復雜度。
第二:對於超大型項目來講,頻繁的快照整個 store ,可能會有性能消耗。
第三:需要我們關注什么時候刪除本地數據。
那么,有沒有其他的可替代方案呢?
onbeforeunload
新的方案,個人覺得應該做到:
- 不強制,需要才導入
- 只在關鍵時刻才保存,這個時機就是頁面刷新的那一刻
- 不依賴除 Vue 外的任何庫/框架
經過一番折騰,有了下面的代碼 saveBeforeUnload.mixin.js
:
import Store from "@/utils/sessionStorage";
const CACHE_PREFIX = "CACHE_KEY_";
const getCacheKey = path => CACHE_PREFIX + path;
export default {
created() {
const CACHE_KEY = getCacheKey(this.$route.path);
const CACHE_DATA = Store.get(CACHE_KEY);
if (CACHE_DATA) {
Object.assign(this, CACHE_DATA);
Store.remove(CACHE_KEY);
}
},
beforeRouteEnter(to, from, next) {
next(vm => {
window.onbeforeunload = () => {
const CACHE_KEY = getCacheKey(vm.$route.path);
Store.set(CACHE_KEY, vm.$data);
window.onbeforeunload = null;
};
});
},
};
從文件名可以看出,它其實是一個 mixin,需要在頁面中導入。這段代碼有如下缺陷:
- 刷新時,只能保存當前這個頁面的數據,如果多個頁面都 mixin 了,其他頁面的數據不會被保存
- 由於是 mixin 方式,對業務代碼有些許侵入但不影響運行
進化版
如果想要保存多個頁面的數據,需要將代碼做如下更改:
import Store from "@/utils/sessionStorage";
const CACHE_PREFIX = "CACHE_KEY_";
const getCacheKey = path => CACHE_PREFIX + path;
const handler = {};
export default {
created() {
const CACHE_KEY = getCacheKey(this.$route.path);
const CACHE_DATA = Store.get(CACHE_KEY);
if (CACHE_DATA) {
Object.assign(this, CACHE_DATA);
Store.remove(CACHE_KEY);
}
},
beforeRouteEnter(to, from, next) {
next(vm => {
const CACHE_KEY = getCacheKey(vm.$route.path);
if (handler[CACHE_KEY]) return;
handler[CACHE_KEY] = () => {
Store.set(CACHE_KEY, vm.$data);
}
window.addEventListener('beforeunload', handler[CACHE_KEY]);
});
},
destroyed() {
const CACHE_KEY = getCacheKey(this.$route.path);
if (handler[CACHE_KEY]) {
window.addEventListener("beforeunload", handler[CACHE_KEY]);
handler[CACHE_KEY] = null;
}
}
};
export function removeCachedData(location) {
Store.remove(getCacheKey(location.path));
}
並在每次路由 push/replace 時,刪除 CACHE_KEY 對應的本地數據,防止舊數據被渲染。
const __push = router.push;
router.push = (location, ...args) => {
if (typeof location === 'string') {
location = {
path: location
}
}
removeCachedData(location);
__push(location, ...args);
}