设为首页收藏本站

安徽论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 12445|回复: 0

如何利用Typescript封装本地存储

[复制链接]

110

主题

0

回帖

342

积分

中级会员

Rank: 3Rank: 3

积分
342
发表于 2022-3-26 11:02:15 | 显示全部楼层 |阅读模式
网站内容均来自网络,本站只提供信息平台,如有侵权请联系删除,谢谢!
目录


前言

本地存储是前端开发过程中经常会用到的技术,但是官方api在使用上多有不便,且有些功能并没有提供给我们相应的api,比如设置过期时间等。本文无意于介绍关于本地存储概念相关的知识,旨在使用typescript封装一个好用的本地存储类。

本地存储使用场景


  • 用户登录后token的存储
  • 用户信息的存储
  • 不同页面之间的通信
  • 项目状态管理的持久化,如redux的持久化、vuex的持久化等
  • 性能优化等
  • ...

使用中存在的问题


  • 官方api不是很友好(过于冗长),且都是以字符串的形式存储,存取都要进行数据类型转换

    • localStorage.setItem(key, value)
    • ...

  • 无法设置过期时间
  • 以明文的形式存储,一些相对隐私的信息用户都能很轻松的在浏览器中查看到
  • 同源项目共享本地存储空间,可能会引起数据错乱

解决方案

将上述问题的解决方法封装在一个类中,通过简单接口的形式暴露给用户直接调用。 类中将会封装以下功能:

  • 数据类型的转换
  • 过期时间
  • 数据加密
  • 统一的命名规范

功能实现
  1. // storage.ts

  2. enum StorageType {
  3.   l = 'localStorage',
  4.   s = 'sessionStorage'
  5. }

  6. class MyStorage {
  7.   storage: Storage

  8.   constructor(type: StorageType) {
  9.     this.storage = type === StorageType.l ? window.localStorage : window.sessionStorage
  10.   }

  11.   set(
  12.     key: string,
  13.     value: any
  14.   ) {
  15.     const data = JSON.stringify(value)
  16.     this.storage.setItem(key, data)
  17.   }

  18.   get(key: string) {
  19.     const value = this.storage.getItem(key)
  20.     if (value) {
  21.       return JSON.parse(value)
  22.   }

  23.   delete(key: string) {
  24.     this.storage.removeItem(key)
  25.   }

  26.   clear() {
  27.     this.storage.clear()
  28.   }
  29. }

  30. const LStorage = new MyStorage(StorageType.l)
  31. const SStorage = new MyStorage(StorageType.s)

  32. export { LStorage, SStorage }
复制代码
以上代码简单的实现了本地存储的基本功能,内部完成了存取时的数据类型转换操作,使用方式如下:
  1. import { LStorage, SStorage } from './storage'

  2. ...

  3. LStorage.set('data', { name: 'zhangsan' })
  4. LStorage.get('data') // { name: 'zhangsan' }
复制代码
加入过期时间

设置过期时间的思路为:在set的时候在数据中加入expires的字段,记录数据存储的时间,get的时候将取出的expires与当前时间进行比较,如果当前时间大于expires,则表示已经过期,此时清除该数据记录,并返回null,expires类型可以是boolean类型和number类型,默认为false,即不设置过期时间,当用户设置为true时,默认过期时间为1年,当用户设置为具体的数值时,则过期时间为用户设置的数值,代码实现如下:
  1. interface IStoredItem {
  2.   value: any
  3.   expires?: number
  4. }
  5. ...
  6. set(
  7.     key: string,
  8.     value: any,
  9.     expires: boolean | number = false,
  10.   ) {
  11.     const source: IStoredItem = { value: null }
  12.     if (expires) {
  13.     // 默认设置过期时间为1年,这个可以根据实际情况进行调整
  14.       source.expires =
  15.         new Date().getTime() +
  16.         (expires === true ? 1000 * 60 * 60 * 24 * 365 : expires)
  17.     }
  18.     source.value = value
  19.     const data = JSON.stringify(source)
  20.     this.storage.setItem(key, data)
  21.   }
  22.   
  23.   get(key: string) {
  24.     const value = this.storage.getItem(key)
  25.     if (value) {
  26.       const source: IStoredItem = JSON.parse(value)
  27.       const expires = source.expires
  28.       const now = new Date().getTime()
  29.       if (expires && now > expires) {
  30.         this.delete(key)
  31.         return null
  32.       }

  33.       return source.value
  34.     }
  35.   }
复制代码
加入数据加密

加密用到了crypto-js包,在类中封装encrypt,decrypt两个私有方法来处理数据的加密和解密,当然,用户也可以通过encryption字段设置是否对数据进行加密,默认为true,即默认是有加密的。另外可通过process.env.NODE_ENV获取当前的环境,如果是开发环境则不予加密,以方便开发调试,代码实现如下:
  1. import CryptoJS from 'crypto-js'

  2. const SECRET_KEY = 'nkldsx@#45#VDss9'
  3. const IS_DEV = process.env.NODE_ENV === 'development'
  4. ...
  5. class MyStorage {
  6.   ...
  7.   
  8.   private encrypt(data: string) {
  9.     return CryptoJS.AES.encrypt(data, SECRET_KEY).toString()
  10.   }

  11.   private decrypt(data: string) {
  12.     const bytes = CryptoJS.AES.decrypt(data, SECRET_KEY)
  13.     return bytes.toString(CryptoJS.enc.Utf8)
  14.   }
  15.   
  16.   set(
  17.     key: string,
  18.     value: any,
  19.     expires: boolean | number = false,
  20.     encryption = true
  21.   ) {
  22.     const source: IStoredItem = { value: null }
  23.     if (expires) {
  24.       source.expires =
  25.         new Date().getTime() +
  26.         (expires === true ? 1000 * 60 * 60 * 24 * 365 : expires)
  27.     }
  28.     source.value = value
  29.     const data = JSON.stringify(source)
  30.     this.storage.setItem(key, IS_DEV ? data : encryption ? this.encrypt(data) : data
  31.     )
  32.   }
  33.   
  34.   get(key: string, encryption = true) {
  35.     const value = this.storage.getItem(key)
  36.     if (value) {
  37.       const source: IStoredItem = JSON.parse(value)
  38.       const expires = source.expires
  39.       const now = new Date().getTime()
  40.       if (expires && now > expires) {
  41.         this.delete(key)
  42.         return null
  43.       }

  44.       return IS_DEV
  45.         ? source.value
  46.         : encryption
  47.         ? this.decrypt(source.value)
  48.         : source.value
  49.     }
  50.   }
  51.   
  52. }
复制代码
加入命名规范

可以通过在key前面加上一个前缀来规范命名,如项目名_版本号_key类型的合成key,这个命名规范可自由设定,可以通过一个常量设置,也可以通过获取package.json中的name和version进行拼接,代码实现如下:
  1. const config = require('../../package.json')

  2. const PREFIX = config.name + '_' + config.version + '_'

  3. ...
  4. class MyStorage {

  5.   // 合成key
  6.   private synthesisKey(key: string) {
  7.     return PREFIX + key
  8.   }
  9.   
  10.   ...
  11.   
  12. set(
  13.     key: string,
  14.     value: any,
  15.     expires: boolean | number = false,
  16.     encryption = true
  17.   ) {
  18.     ...
  19.     this.storage.setItem(
  20.       this.synthesisKey(key),
  21.       IS_DEV ? data : encryption ? this.encrypt(data) : data
  22.     )
  23.   }
  24.   
  25.   get(key: string, encryption = true) {
  26.     const value = this.storage.getItem(this.synthesisKey(key))
  27.     ...
  28.   }

  29. }
复制代码
完整代码
  1. import CryptoJS from 'crypto-js'
  2. const config = require('../../package.json')

  3. enum StorageType {
  4.   l = 'localStorage',
  5.   s = 'sessionStorage'
  6. }

  7. interface IStoredItem {
  8.   value: any
  9.   expires?: number
  10. }

  11. const SECRET_KEY = 'nkldsx@#45#VDss9'
  12. const PREFIX = config.name + '_' + config.version + '_'
  13. const IS_DEV = process.env.NODE_ENV === 'development'

  14. class MyStorage {
  15.   storage: Storage

  16.   constructor(type: StorageType) {
  17.     this.storage =
  18.       type === StorageType.l ? window.localStorage : window.sessionStorage
  19.   }

  20.   private encrypt(data: string) {
  21.     return CryptoJS.AES.encrypt(data, SECRET_KEY).toString()
  22.   }

  23.   private decrypt(data: string) {
  24.     const bytes = CryptoJS.AES.decrypt(data, SECRET_KEY)
  25.     return bytes.toString(CryptoJS.enc.Utf8)
  26.   }

  27.   private synthesisKey(key: string) {
  28.     return PREFIX + key
  29.   }

  30.   set(
  31.     key: string,
  32.     value: any,
  33.     expires: boolean | number = false,
  34.     encryption = true
  35.   ) {
  36.     const source: IStoredItem = { value: null }
  37.     if (expires) {
  38.       source.expires =
  39.         new Date().getTime() +
  40.         (expires === true ? 1000 * 60 * 60 * 24 * 365 : expires)
  41.     }
  42.     source.value = value
  43.     const data = JSON.stringify(source)
  44.     this.storage.setItem(
  45.       this.synthesisKey(key),
  46.       IS_DEV ? data : encryption ? this.encrypt(data) : data
  47.     )
  48.   }

  49.   get(key: string, encryption = true) {
  50.     const value = this.storage.getItem(this.synthesisKey(key))
  51.     if (value) {
  52.       const source: IStoredItem = JSON.parse(value)
  53.       const expires = source.expires
  54.       const now = new Date().getTime()
  55.       if (expires && now > expires) {
  56.         this.delete(key)
  57.         return null
  58.       }

  59.       return IS_DEV
  60.         ? source.value
  61.         : encryption
  62.         ? this.decrypt(source.value)
  63.         : source.value
  64.     }
  65.   }

  66.   delete(key: string) {
  67.     this.storage.removeItem(this.synthesisKey(key))
  68.   }

  69.   clear() {
  70.     this.storage.clear()
  71.   }
  72. }

  73. const LStorage = new MyStorage(StorageType.l)
  74. const SStorage = new MyStorage(StorageType.s)

  75. export { LStorage, SStorage }
复制代码
总结

到此这篇关于如何利用Typescript封装本地存储的文章就介绍到这了,更多相关Typescript封装本地存储内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
                                                        
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
免责声明
1. 本论坛所提供的信息均来自网络,本网站只提供平台服务,所有账号发表的言论与本网站无关。
2. 其他单位或个人在使用、转载或引用本文时,必须事先获得该帖子作者和本人的同意。
3. 本帖部分内容转载自其他媒体,但并不代表本人赞同其观点和对其真实性负责。
4. 如有侵权,请立即联系,本网站将及时删除相关内容。
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表