首页 > 其他分享 >LocalStorage封装

LocalStorage封装

时间:2024-07-21 22:26:06浏览次数:22  
标签:封装 string storage iv LocalStorage key localStorage 窗口

前言

localStorage 使用是一个老生常谈的话题,本文不讲解基础 api,主要教你如何封装一个优雅的localStorage 工具,以及一些 localStorage中一些你不知道的知识点。

  1. 优雅的 Storage 工具类如何封装(支持前缀key、加密存储、过期时间,ts封装等)

  2. localStorage 真实存储大小/存储统计

  3. localStorage 如何监听

  4. localStorage 同源问题与同源窗口通信

优雅的 Storage 工具如何封装(前缀、加密、过期时间等)

该工具函数设计

  1. 采用工厂方法+闭包设计模式,不直接实例化类,而是根据传入的参数来配置和返回一个 SmartStorage 的实例。

  2. 支持带前缀的键:通过 prefixKey 参数可以为存储的键名添加一个前缀,默认为空字符串。这个功能可以帮助避免键名冲突,特别是当在同一个域下的不同应用或组件中使用同一种存储方式时。

  3. 支持过期时间:在存储数据时,可以为每项数据设置一个过期时间(单位为秒),存储的数据结构中会包括实际的值、存储时间戳以及过期时间戳。在读取数据时,会检查数据是否过期,如果已经过期,则自动删除

  4. 支持加密存储:存储数据时根据参数配置可先进行加密,读取数据时再解密,加密使用的 crypto 模块

  5. 错误处理:在读取数据时,如果解密过程出错或数据格式不正确,会捕获异常并返回默认值,这提高了程序的健壮性。

  6. 支持常用的 apiset get remove clear

  7. 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 存储大小

  1. localStorage 的存储空间是 5M,但是单位是字符串的长度值, 或者 utf-16 的编码单元,也可以说是 10M 字节空间。

  2. localStorage 的 key 键也是占存储空间的。

  3. 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

相关文章

  • 封装C项目为dll
    这是头文件,定义了一个接口MyHeader.h。#ifndefMYHEADER_H#defineMYHEADER_H//定义导出DLL函数的宏#defineMY_API__declspec(dllexport)#ifdef__cplusplusextern"C"{ //告诉编译器下面是C语言代码#endif //函数声明 MY_APIint__stdcalladd(intx,inty......
  • Java面向对象程序三大特性:封装、继承、多态
    目录 引言一.封装二.继承三.多态四.结论 引言 在现代软件开发中,面向对象编程(OOP)已成为一种流行且有效的编程范式,其中Java语言以其高效性和灵活性深受开发者的喜爱。面向对象编程的核心在于其三大特性:封装、继承和多态。这些特性不仅提高了代码的重用性和可维......
  • 鸿蒙开发 03 封装 @ohos/axios (最新深度封装)
    鸿蒙开发03封装@ohos/axios(最新深度封装)1、安装@ohos/axios2、开始封装2.1新建utils文件夹和api文件夹2.2在utils文件夹里新建http.ts2.3在api文件夹里新建api.ets3、页面调用4、打印结果1、安装@ohos/axiosohpminstall@ohos/axiosTips:按......
  • 0197-封装相机逻辑
    环境Time2022-11-16WSL-Ubuntu22.04Rust1.65.0前言说明参考:https://raytracing.github.io/books/RayTracingInOneWeekend.html目标重构相机部分逻辑,将其单独提到一个类中,保持逻辑不变。camera.rsusesuper::ray::Ray;usesuper::vector3::{Point3,Vector3};pub......
  • 前端WebSocket的方法封装
    一、封装方法在项目根目录src下的utils中新增webSocketManager.js封装文件,代码内容如下://webSocketManager.js/**WebSocketMessenger封装类*/classWebSocketManager{constructor(url=null,userId=null,receiveMessageCallback=null){this.socket=nul......
  • 重生归来,从 996福报 到 N+1告别职场【如何封装一个支持图片和PDF在线预览的Vue组件】
    如何封装一个支持图片和PDF在线预览的Vue组件在本文中,我将介绍如何设计并实现一个Vue组件,该组件能够在线预览图片和PDF文件。我们将基于element-ui的elImageViewer组件进行改造,并使用vue-pdf插件来实现PDF预览功能。本文将详细介绍从设计思路到落地实现的全过程,完整代码在......
  • Airtest封装的Tidevice接口有多好用(一)
    此文章来源于项目官方公众号:“AirtestProject”版权声明:允许转载,但转载必须保留原链接;请勿用作商业或者非法用途一、前言很多同学都有在Windows电脑上连接本地iOS设备去进行测试的需求,其中tidevice库是大家在Windows上使用的最多的iOS通信库,其中有一些接口是我们比较常用的,所......
  • 使用ElementUI和element-china-area-data库实现省市区三级联动组件封装
    使用ElementUI和element-china-area-data库实现省市区三级联动组件封装在前端开发中,省市区三级联动是一个常见的需求。今天我们将使用Vue.js和ElementUI组件库,结合element-china-area-data库,来实现一个省市区三级联动的组件。这个组件不仅可以提高用户体验,还能大大简化我们的代码......
  • 二次封装antd的ProTable、EditableProTable,结合use-antd-resizable-header,做一个列可
    原先是一个项目模块内需求,迭代的时候领导要求项目全面翻新,所有表格都要可伸缩列如果一个一个页面写伸缩列的话,每个页面都要引用一次use-antd-resizable-header,有点累赘找了网上,暂时没看见这个有人整理这个组件。直接上代码importProTablefrom"@ant-design/pro-table";i......
  • cookie方法封装
    方法封装:/***获取cookie*/exportfunctiongetCookie(sKey:string):string{if(!sKey){returnnull;}returndecodeURIComponent(document.cookie.replace(newRegExp("(?:(?:^|.*;)\\s*"+encodeURIComponent(sKey).replace(/[\-\.\+\*]/g,......