Storage 封装
一 JSON - javascript Object Notation(符号) 基于JavaScript原生语法,能够序列化对象、数组、数值、字符串、布尔值和 null。
JSON.parse(s,(key,value)=>{
console.log(key,value);
// s 为字符串 '"字符"' 或者 '数字' 、'null' 、'{}'、'[]' key 都为空,value 为其解析的值
// s 为JSON对象字符串,key 和 value 才为其解析的值
// undefined、Symbol和BigInt,数字也不支持NaN、Infinity和-Infinity,都会报错
});
JSON.parse();
JOSN.parse 解析 非数字字符串, 必须为这种格式 '"abc"' , 如果为 'abc' 会抛出异常;
JSON.pase 解析 数字字符串,'2343' , 'null' ,'true' , '{}', '[]' 解析为原来的值。
JSON.stringify();
JSON.stringify(v,(key,value)=>{
// 用来处理 value 为undefined ,如果为undefined 或者函数 则会将该项取消,不会展示
if(value === undefined || typeof value === 'function'){
return null;
}
return value;
缩进);
// JSON.stringify( undefined, function, Bigint, Symbol 等 ) 结果都为undefined ,注意不是字符串的undefined
注意:localStorage 存 数字,最后取出来依旧为字符串。需要把起包裹在一个对象内。或者使用JSON.stringify 进行存储。
注意:当对象的层级过深,数量级很大时候,执行JSON.parse 和 JSON.stringify 的时候 会递归去进行类型的判断和转化,就会很耗时。
优化的做法是,使用 fast-json-stringify 和 fast-json-parse
localStorage/ sessionStorage / indexDB
sessionStorage : 打开多个 相同URL 的 Tabs 页面,会创建 各自 的 sessionStorage 内存空间,而 localStorage会 共用一个内存空间。
storage 下的方法
setItem()
removeItem()
getItem()
clrear()
封装注意点
- 传入的是 sessionStorage 还是 localStorage , 因为这两个 在方法上是一致的。
- nameSpace 命名空间,是否使用同一个命名空间,某个项目下。通过一个参数来判断,默认是使用。
- 是否通过一个队列来异步存取。性能分析,localStorage 中 进行存取其实是非常快的,因为都是存取的字符串。但是JSON.stringify 复杂的对象会很慢。
- 尽量不要把所有数据都存到一个命名空间内,特别是 有大数据的时候。将其写成单独的一个空间内。
- 支持异步存取。写一个队列,存入先放入队列中,过一段时间的延迟再存入缓存中。 所以取的时候,也要先去队列中去取。 (所以定义某个数据的存取,需要写函数进行,不然你根本无法预料其他人是怎么去存取的数据,无法控制)。 - 放弃,因为相当于只做了一个防抖操作。
- 大数据放到 indexDB 中去
- fastJSON
- 支持 fastJSON + TS 方法 (库:fastJsonParse, faseJsonStringify)
localStorage 的读取性能 和 存入的 字符串长度关系 ?读取的性能非常强,存入10万个字符 和 存入 1个字符 的时间都很快,几乎可以忽略不计。
所以,localStorage 慢的原因,只是 JSON 的序列化和反序列化慢。
需求 - 只能使用SDK 提供的方式进行读取缓存
代理模式 的扩展 思想
先缓存一下,原生的 setItem 和 getItem 函数。然后直接将 localStorage.setItem = null
在封装的缓存内部,调用setItem.apply( localstorage,[key, value] )
其他方式也是一样的,比如,直接重写 window.console.log 函数,在其内部判断是否配置了某个参数,只有在配置了才能正常打印。保证线上环境不会出现console 打印。
如何知道local的大小,利用字符串转 Blob 后的length 可大致计算出。
function countStringSize(str:string){
return new Blob([str]).length;
}
开始编写 LocalStorage
判断 浏览器是否支持
function isObj(v) {
return typeof v === 'object' && v !== null;
}
export class CacheStorage {
private isSupport: boolean;
private storage: Storage;
private nameSpace: string;
private type: string;
constructor(type: 'localStorage' | 'sessionStorage', nameSpace: string) {
this.type = type;
this.storage = window[type];
this.nameSpace = nameSpace;
this.isSupport = window[type] !== undefined;
}
countStorageSize(): number {
const length = this.storage.length;
const keys = Object.keys(this.storage);
let size = 0;
for (let i = 0; i < length; i++) {
const blob = new Blob([keys[i]]);
size += blob.size;
}
return size;
}
setItem(key: string, value: unknown, isNameSpace = true): void {
if (value === undefined) {
value = null;
}
let lKey, lValue;
if (isNameSpace) {
lKey = this.nameSpace;
const valueString = localStorage.getItem(this.nameSpace);
let obj;
if (valueString) {
obj = JSON.parse(valueString);
if (isObj(obj)) {
obj[key] = value;
}
} else {
obj = {};
obj[key] = value;
}
lValue = JSON.stringify(obj);
} else {
lKey = key;
lValue = JSON.stringify(value);
}
try {
this.storage.setItem(lKey, lValue);
} catch (e) {
if (e.name === 'QuotaExceededError' || e.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
console.warn(this.type + 'Storage 存储已达上限!');
}
// 现在不管什么报错,都执行清空操作
this.storage.clear();
}
}
getItem
if (!isNameSpace) {
const str = this.storage.getItem(key);
return JSON.parse(str);
}
const value = this.storage.getItem(this.nameSpace);
return JSON.parse(value)?.[key];
}
removeItem(key: string, isNameSpace = true): void {
if (!isNameSpace) {
return this.storage.removeItem(key);
}
const value = JSON.parse(this.storage.getItem(this.nameSpace));
Reflect.deleteProperty(value, key);
this.storage.setItem(this.nameSpace, JSON.stringify(value));
}
clear(isNameSpace = true): void {
if (!isNameSpace) {
return this.storage.clear();
}
return this.storage.removeItem(this.nameSpace);
}
}
使用indexDB库。由于indexDB的存取是异步的,且不需要进行JSON 数据格式的转化,所以理论上效率比存localStorage 还要高。
使用 indexDB 库 -