前言
localStorage
使用是一个老生常谈的话题,本文不讲解基础 api
,主要教你如何封装一个优雅的localStorage
工具,以及一些 localStorage
中一些你不知道的知识点。
-
优雅的
Storage
工具类如何封装(支持前缀key、加密存储、过期时间,ts封装等) -
localStorage
真实存储大小/存储统计 -
localStorage
如何监听 -
localStorage
同源问题与同源窗口通信
优雅的 Storage 工具如何封装(前缀、加密、过期时间等)
该工具函数设计
-
采用工厂方法+闭包设计模式,不直接实例化类,而是根据传入的参数来配置和返回一个
SmartStorage
的实例。 -
支持带前缀的键:通过
prefixKey
参数可以为存储的键名添加一个前缀,默认为空字符串。这个功能可以帮助避免键名冲突,特别是当在同一个域下的不同应用或组件中使用同一种存储方式时。 -
支持过期时间:在存储数据时,可以为每项数据设置一个过期时间(单位为秒),存储的数据结构中会包括实际的值、存储时间戳以及过期时间戳。在读取数据时,会检查数据是否过期,如果已经过期,则自动删除
-
支持加密存储:存储数据时根据参数配置可先进行加密,读取数据时再解密,加密使用的
crypto
模块 -
错误处理:在读取数据时,如果解密过程出错或数据格式不正确,会捕获异常并返回默认值,这提高了程序的健壮性。
-
支持常用的
api
(set get remove clear
) -
TypeScript
实现
接下来是代码实现:在未进行代码实现前可以基于上面的设计自己实现一下,然后对照下我的代码实现
/**
* 封装一个local
*/
import { decrypt as aesDecrypt, encrypt as aesEncrypt } from 'crypto-js/aes';
import UTF8, { parse } from 'crypto-js/enc-utf8';
import pkcs7 from 'crypto-js/pad-pkcs7';
import CTR from 'crypto-js/mode-ctr';
import {isNil} from 'lodash';
interface EncryptionParams {
key: string;
iv: string;
}
export interface Encryption {
encrypt(plainText: string): string;
decrypt(cipherText: string): string;
}
/**
* 加密类简单实现
*/
class AesEncryption implements Encryption {
private readonly key;
private readonly iv;
constructor({ key, iv }: EncryptionParams) {
this.key = parse(key);
this.iv = parse(iv);
}
get getOptions() {
return {
mode: CTR, // 加密部分不赘余,自行搜索参数学习
padding: pkcs7, // 加密部分不赘余,自行搜索参数学习
iv: this.iv,
};
}
encrypt(plainText: string) {
return aesEncrypt(plainText, this.key, this.getOptions).toString();
}
decrypt(cipherText: string) {
return aesDecrypt(cipherText, this.key, this.getOptions).toString(UTF8);
}
}
export interface CreateSmartStorageParams extends EncryptionParams {
prefixKey: string;
storage: Storage;
hasEncrypt: boolean;
timeout?: number;
}
/**
* localStorage工厂方法实现
* @param param0
* @returns
*/
export const createSmartStorage = ({
prefixKey = '',
storage = localStorage, // 这里其实也可以支持sessionStorage,自行配置
key = cacheConfig.key, // 修改为自己项目cacheConfig中的key
iv = cacheConfig.iv, // 修改为自己项目cacheConfig中的iv
timeout = null,
hasEncrypt = true,
}: Partial<CreateSmartStorageParams> = {}) => {
if (hasEncrypt && [key.length, iv.length].some((item) => item !== 16)) {
throw new Error('When hasEncrypt is true, the key or iv must be 16 bits!');
}
//
const persistEncryption: Encryption = new AesEncryption({
key: cacheConfig.key,// 修改为自己项目cacheConfig中的key
iv: cacheConfig.iv,// 修改为自己项目cacheConfig中的iv
})
/**
* Cache class
* Construction parameters can be passed intolocalStorage,
* @class Cache
* @example
*/
const SmartStorage = class SmartStorage {
private storage: Storage;
private prefixKey?: string;
private encryption: Encryption;
private hasEncrypt: boolean;
/**
*
* @param {*} storage
*/
constructor() {
this.storage = storage;
this.prefixKey = prefixKey;
this.encryption = persistEncryption;
this.hasEncrypt = hasEncrypt;
}
private getKey(key: string) {
return `${this.prefixKey}${key}`.toUpperCase();
}
/**
* Set cache
* @param {string} key
* @param {*} value
* @param {*} expire Expiration time in seconds
* @memberof Cache
*/
set(key: string, value: any, expire: number | null = timeout) {
const stringData = JSON.stringify({
value,
time: Date.now(),
expire: !isNil(expire) ? new Date().getTime() + expire * 1000 : null,
});
const stringifyValue = this.hasEncrypt ? this.encryption.encrypt(stringData) : stringData;
this.storage.setItem(this.getKey(key), stringifyValue);
}
/**
* Read cache
* @param {string} key
* @param {*} def
* @memberof Cache
*/
get(key: string, def: any = null): any {
const val = this.storage.getItem(this.getKey(key));
if (!val) return def;
try {
const decVal = this.hasEncrypt ? this.encryption.decrypt(val) : val;
const data = JSON.parse(decVal);
const { value, expire } = data;
if (isNil(expire) || expire >= new Date().getTime()) {
return value;
}
this.remove(key);
} catch (e) {
return def;
}
}
/**
* Delete cache based on key
* @param {string} key
* @memberof Cache
*/
remove(key: string) {
this.storage.removeItem(this.getKey(key));
}
/**
* Delete all caches of this instance
*/
clear(): void {
this.storage.clear();
}
};
return new SmartStorage();
};
再补充几个 localStorage
相关可能你不知道的知识点。
localStorage 存储大小
-
localStorage
的存储空间是5M
,但是单位是字符串的长度值, 或者utf-16
的编码单元,也可以说是10M
字节空间。 -
localStorage
的key
键也是占存储空间的。 -
localStorage
如何统计已使用空间
function sieOfLS() {
return Object.entries(localStorage).map(v => v.join('')).join('').length;
}
这个函数也可以加到storage工具函数中
localStorage.clear();
localStorage.setItem("
标签:封装,string,storage,iv,LocalStorage,key,localStorage,窗口
From: https://blog.csdn.net/moshowgame/article/details/140595185