页面刷新数据丢失
在vue中data、vuex store等都数据都是在内存当中的,页面一旦刷新,这些数据就会丢失(或者说被重置为初始值),在某些时候很影响用户体验。
缓存,恢复
要想使数据刷新不丢失,就得监听页面刷新事件,在刷新前将数据缓存到本地存储,页面刷新后再将数据从本地存储恢复。目前较普遍的做法是类似这样:
//App.vue的created():
created() {
//在页面加载从本地读取状态数据并写入vuex
if (sessionStorage.getItem("store")) {
this.$store.replaceState(
Object.assign(
{},
this.$store.state,
JSON.parse(sessionStorage.getItem("store"))
)
);
}
//页面刷新前将vuex里的状态数据保存到sessionStorage
window.addEventListener(
"beforeunload",()=>{
sessionStorage.setItem("store",JSON.stringify(this.$store.state));
});
}
这样做是直接将一整个store保存到本地,但是很多时候我们需要保存到本地的只是一些关键性数据,这样做缺少灵活性,而且对于那些并不想放在vuex中的数据极不友好。
改进:dataCache.ts
基于上面将数据缓存到本地存储然后页面加载时恢复的思想,我对上面的方法做了一些改进:
let caches: Record<string, { [index: string]: unknown }> = {};
let isFirst = false;
let baseId = 0;
const generateId = () => baseId++;
export default {
/**
* 将data注册进缓存,可指定自定义的id(通常不推荐这样做),可指定需要进行缓存的属性
* @param data 需要注册的数据对象
* @param id 为需要注册的数据对象的id,id最好不要是纯数字,并且必须保证id的唯一性
* @param propertyKeys 指定需要将data的哪些属性进行缓存
*/
register(data: { [index: string]: unknown }, id?: string, ...propertyKeys: PropertyKey[]): number | string {
if (id === undefined) {
id = (generateId()).toString();
} else {
const nid = Number(id);
//字符串的值为数字,为了防止与默认的id生成策略冲突,将其转化成 id+'_'+generateId() 的形式
if (!isNaN(nid)) {
id += '_' + generateId();
}
}
const hasId = Reflect.has(caches, id);
//第一次渲染就id就已经存在,说明提供了重复的id
if (isFirst && hasId) {
throw new Error('Duplicate id:\'' + id + '\'');
}
//不是首次渲染且id存在,则对data执行属性值的注入
else if (hasId) {
const cache = Reflect.get(caches, id as PropertyKey);
if (cache instanceof Array) {
for (let i = 0; i < cache.length; i++) {
data[i] = cache[i];
}
} else {
const keys = propertyKeys.length < 1 ? Reflect.ownKeys(cache) : propertyKeys;
for (const key of keys) {
Reflect.set(data, key, Reflect.get(cache, key));
}
}
}
//将data放进缓存区
Reflect.set(caches, id, data);
return id;
},
/**
* 初始化data-caches,在App.vue的created钩子中调用此函数即可
* @param itemKey 在sessionStorage中存取caches时的key
*/
init(itemKey: string): void {
// 页面加载时读取sessionStorage中的数据
const jsonString = sessionStorage.getItem(itemKey);
// jsonString为null说明是第一次渲染页面
if (jsonString === null) {
isFirst = true;
} else {
caches = JSON.parse(jsonString);
}
// 监听页面刷新,刷新时将数据保存到sessionStorage
window.addEventListener("beforeunload", () => {
sessionStorage.setItem(itemKey, JSON.stringify(caches));
});
}
}
用法
第一步:初始化
将上述代码保存到一个文件名为 dataSessionCache.js
的文件里,
然后在App.vue的created钩子中调用 init 函数进行初始化,需要提供一个itemName:
//App.vue
import dc from "./ts/dataCache";
export default {
created() {
/*参数 itemKey 将作为 sessionStorage 进行
getItem、setItem 操作时的 key,需要保证其唯一性.*/
dc.init('data-caches');
},
//others
......
};
第二步:注册
对于vue组件中的数据
对于vue组件中的数据例如data,将需要缓存的数据写到一个对象里面,然后再created钩子中进行注册,例如:
import dc from "@/js/dataCache.ts";
export default {
data: () => ({
//need cache
cached: {
num : 134,
str: 'str'
......
}
//other needn't cache
......
}),
created(){
//第二个参数是待缓存的数据对象,必须是一个对象或者数组
sc.register(this.$data.cached);
}
};
对于VueX store
对于vuex store中的数据也可以用类似的方法:
//store中:
export default new Vuex.Store({
state: {
cached: {
key: 'data'
}
//others
......
},
//others
......
})
//App.vue中
import dsc from "@/js/dataCache.ts";
export default {
created() {
//别忘了init
dsc.init('data-caches');
//注册
dsc.register(this.$store.state.cached);
},
//others
......
};
缓存对象的非全部属性
register
的原型是register(id, obj, ...propertyKeys)
,第三个参数的作用是指定需要进行缓存的属性,如果propertyKeys
为空则会缓存其全部属性。例如,当我们想 在不改变原有代码的情况下 缓存一个单文本组件的部分data时,可以这样写:
export default {
data: () => ({
a:1,
b:'b'
}),
created() {
//只缓存属性a
dsc.register('this.data,undefined,'a');
},
//others
......
}
好处
- 在页面刷新前后进行数据的保存/恢复工作,性能开销少
- 对原有代码仅需少量修改,或者根本不需要修改原有代码
- 简单易用,灵活方便,初始化、注册两步就OK,并且可以灵活的指定需要缓存的数据
git仓库
https://gitee.com/szw-yunie/data-session-cache.js