当前位置:   article > 正文

Storage 工具如何封装(前缀、加密、过期时间等)及localStorage的基础介绍

Storage 工具如何封装(前缀、加密、过期时间等)及localStorage的基础介绍

​​​​​​​

Storage 工具类封装

该工具函数设计

  • 采用工厂方法+闭包设计模式,不直接实例化类,而是根据传入的参数来配置和返回一个 SmartStorage 的实例。
  • 支持带前缀的键:通过 prefixKey 参数可以为存储的键名添加一个前缀,默认为空字符串。这个功能可以帮助避免键名冲突,特别是当在同一个域下的不同应用或组件中使用同一种存储方式时。
  • 支持过期时间:在存储数据时,可以为每项数据设置一个过期时间(单位为秒),存储的数据结构中会包括实际的值、存储时间戳以及过期时间戳。在读取数据时,会检查数据是否过期,如果已经过期,则自动删除
  • 支持加密存储:存储数据时根据参数配置可先进行加密,读取数据时再解密,加密使用的 crypto 模块
  • 错误处理:在读取数据时,如果解密过程出错或数据格式不正确,会捕获异常并返回默认值,这提高了程序的健壮性。
  • 支持常用的 api(set get remove clear)
  • TypeScript 实现

接下来是代码实现:在未进行代码实现前可以基于上面的设计自己实现一下,然后对照下我的代码实现

  1. /***
  2. * title: storage.ts
  3. * Desc: 对存储的简单封装
  4. */
  5. // 加密库
  6. import CryptoJS from "crypto-js";
  7. import { projectPrefix, expireTimes } from "../variable";
  8. // 十六位十六进制数作为密钥
  9. const SECRET_KEY = CryptoJS.enc.Utf8.parse("3333e66666111111");
  10. // 十六位十六进制数作为密钥偏移量
  11. const SECRET_IV = CryptoJS.enc.Utf8.parse("3333bb2222111111");
  12. // 类型 window.localStorage,window.sessionStorage,
  13. type Config = {
  14. type: any;
  15. prefix: string;
  16. expire: any;
  17. isEncrypt: Boolean;
  18. };
  19. const config: Config = {
  20. type: "localStorage", // 本地存储类型 sessionStorage
  21. prefix: projectPrefix, // 名称前缀 建议:项目名 + 项目版本
  22. expire: expireTimes, //过期时间 单位:秒
  23. isEncrypt: true, // 默认加密 为了调试方便, 开发过程中可以不加密
  24. };
  25. // 判断是否支持 Storage
  26. export const isSupportStorage = () => {
  27. return typeof Storage !== "undefined" ? true : false;
  28. };
  29. // 设置 setStorage
  30. export const setStorage = (key: string, value: any, expire = 0) => {
  31. if (value === "" || value === null || value === undefined) {
  32. value = null;
  33. }
  34. if (isNaN(expire) || expire < 0) throw new Error("Expire must be a number");
  35. expire = expire ? expire : config.expire;
  36. let data = {
  37. value: value, // 存储值
  38. time: Date.now(), //存值时间戳
  39. expire: expire, // 过期时间
  40. };
  41. console.log("shezhi ", data);
  42. const encryptString = config.isEncrypt
  43. ? encrypt(JSON.stringify(data))
  44. : JSON.stringify(data);
  45. (window[config.type] as any).setItem(autoAddPrefix(key), encryptString);
  46. };
  47. // 获取 getStorage
  48. export const getStorage = (key: string) => {
  49. key = autoAddPrefix(key);
  50. // key 不存在判断
  51. if (
  52. !(window[config.type] as any).getItem(key) ||
  53. JSON.stringify((window[config.type] as any).getItem(key)) === "null"
  54. ) {
  55. return null;
  56. }
  57. // 优化 持续使用中续期
  58. const storage = config.isEncrypt
  59. ? JSON.parse(decrypt((window[config.type] as any).getItem(key)))
  60. : JSON.parse((window[config.type] as any).getItem(key));
  61. let nowTime = Date.now();
  62. // 过期删除
  63. let setExpire = (storage.expire || config.expire) * 1000,
  64. expDiff = nowTime - storage.time;
  65. console.log("设置时间", setExpire, expDiff);
  66. if (setExpire < expDiff) {
  67. removeStorage(key);
  68. return null;
  69. } else {
  70. // 未过期期间被调用 则自动续期 进行保活
  71. setStorage(autoRemovePrefix(key), storage.value, storage.expire);
  72. return storage.value;
  73. }
  74. };
  75. // 是否存在 hasStorage
  76. export const hasStorage = (key: string) => {
  77. key = autoAddPrefix(key);
  78. let arr = getStorageAll().filter((item) => {
  79. return item.key === key;
  80. });
  81. return arr.length ? true : false;
  82. };
  83. // 获取所有key
  84. export const getStorageKeys = () => {
  85. let items = getStorageAll();
  86. let keys = [];
  87. for (let index = 0; index < items.length; index++) {
  88. keys.push(items[index].key);
  89. }
  90. return keys;
  91. };
  92. // 获取全部 getAllStorage
  93. export const getStorageAll = () => {
  94. let len = window[config.type].length; // 获取长度
  95. let arr = new Array(); // 定义数据集
  96. for (let i = 0; i < len; i++) {
  97. // 获取key 索引从0开始
  98. let getKey = (window[config.type] as any).key(i);
  99. // 获取key对应的值
  100. let getVal = (window[config.type] as any).getItem(getKey);
  101. // 放进数组
  102. arr[i] = { key: getKey, val: getVal };
  103. }
  104. return arr;
  105. };
  106. // 删除 removeStorage
  107. export const removeStorage = (key: string) => {
  108. (window[config.type] as any).removeItem(autoAddPrefix(key));
  109. };
  110. // 清空 clearStorage
  111. export const clearStorage = () => {
  112. (window[config.type] as any).clear();
  113. };
  114. // 名称前自动添加前缀
  115. const autoAddPrefix = (key: string) => {
  116. const prefix = config.prefix ? config.prefix + "_" : "";
  117. return prefix + key;
  118. };
  119. // 移除已添加的前缀
  120. const autoRemovePrefix = (key: string) => {
  121. const len: any = config.prefix ? config.prefix.length + 1 : "";
  122. return key.substr(len);
  123. // const prefix = config.prefix ? config.prefix + '_' : '';
  124. // return prefix + key;
  125. };
  126. /**
  127. * 加密方法
  128. * @param data
  129. * @returns {string}
  130. */
  131. const encrypt = (data: any) => {
  132. if (typeof data === "object") {
  133. try {
  134. data = JSON.stringify(data);
  135. } catch (error) {
  136. console.log("encrypt error:", error);
  137. }
  138. }
  139. const dataHex = CryptoJS.enc.Utf8.parse(data);
  140. const encrypted = CryptoJS.AES.encrypt(dataHex, SECRET_KEY, {
  141. iv: SECRET_IV,
  142. mode: CryptoJS.mode.CBC,
  143. padding: CryptoJS.pad.Pkcs7,
  144. });
  145. return encrypted.ciphertext.toString();
  146. };
  147. /**
  148. * 解密方法
  149. * @param data
  150. * @returns {string}
  151. */
  152. const decrypt = (data: any) => {
  153. const encryptedHexStr = CryptoJS.enc.Hex.parse(data);
  154. const str = CryptoJS.enc.Base64.stringify(encryptedHexStr);
  155. const decrypt = CryptoJS.AES.decrypt(str, SECRET_KEY, {
  156. iv: SECRET_IV,
  157. mode: CryptoJS.mode.CBC,
  158. padding: CryptoJS.pad.Pkcs7,
  159. });
  160. const decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);
  161. return decryptedStr.toString();
  162. };

在组件中使用:

  1. <template>
  2. <div>
  3. layout
  4. <el-button type="primary" @click="getLocal">get</el-button>
  5. <el-button type="primary" @click="setLocal">set</el-button>
  6. </div>
  7. </template>
  8. <script setup lang="ts">
  9. import { setStorage, getStorage, getStorageAll,removeStorage, clearStorage } from "../../utils/modules/storage";
  10. let setLocal = (): void => {
  11. let testLocal = "token123123123";
  12. setStorage("testLocal", testLocal);
  13. };
  14. let getLocal = (): void => {
  15. let localVal = getStorage("testLocal");
  16. let all = getStorageAll()
  17. console.log("测试存储", localVal,all);
  18. };
  19. </script>
  20. <style scoped lang="scss">
  21. </style>
localStorlocalStorage 存储大小

localStorage 的存储大小因浏览器而异,但通常有以下限制:

  • 大多数浏览器提供了5MB左右的存储空间,但是单位是字符串的长度值, 或者 utf-16 的编码单元,也可以说是 10M 字节空间。
  • localStorage 的 key 键也是占存储空间的。
  • localStorage 如何统计已使用空间
  • 在移动设备上,iOS 和某些版本的 Android 浏览器可能会对 localStorage 的大小有所限制,甚至完全禁用它。

要检查 localStorage 的实际存储限制,可以使用以下代码片段尝试使用存储空间:

  1. function checkLocalStorageSpace() {
  2. var testKey = 'storageTest',
  3. max = 2 * 1024 * 1024, // 2MB
  4. i = 0,
  5. value = '';
  6. // 生成一个足够大的字符串
  7. for (i = 0; i < max; i++) {
  8. value += 'a';
  9. }
  10. // 尝试存储这个大字符串
  11. localStorage.setItem(testKey, value);
  12. // 检查是否能成功存储,如果不能,则减半大小继续测试
  13. try {
  14. localStorage.setItem(testKey, value);
  15. max *= 2;
  16. } catch (e) {
  17. max /= 2;
  18. }
  19. localStorage.removeItem(testKey);
  20. console.log('Estimated localStorage limit: ' + max + ' bytes');
  21. }
  22. checkLocalStorageSpace();

关于工具函数的封装:

  1. function sieOfLS() {
  2. return Object.entries(localStorage).map(v => v.join('')).join('').length;
  3. }

这个函数也可以加到storage工具函数中

  1. localStorage.clear();
  2. localStorage.setItem("
    声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/468594
    推荐阅读
    相关标签