·vincent

封装LocalStorage并支持过期时间

在浏览器开发中,我们经常需要在客户端存储数据,以便在不同页面或会话之间进行持久化。本文介绍了常用的客户端存储机制cookie与localStorage,并探讨了它们在一些方面存在的限制和差异。此外,还提供了一种解决方案,通过对localStorage进行封装,实现了为数据设置有效期的功能。

Javascriptweb安全缓存

在浏览器开发中,我们经常需要在客户端存储数据,以便在不同页面或会话之间进行持久化。cookielocalStorage是常用的客户端存储机制。然而,它们在一些方面存在一些限制和差异。

cookie

  • cookie是存储在客户端的小型文本文件,每当用户与服务器进行通信时都会在请求和响应中传递。
  • cookie可以设置过期时间,可以是会话级别(浏览器关闭后过期)或持久性(在指定时间之后过期)。
  • cookie可以在不同页面之间共享,并且可以用于实现用户身份验证、记住用户偏好等功能。
  • 但是,每个请求都会将cookie发送到服务器,这可能会增加网络流量,并且对于每个请求都需要服务器进行处理。

localStorage

  • localStorage是HTML5中提供的一个客户端存储机制,用于存储较大量级的数据。
  • localStorage使用key-value的方式存储数据,并且数据存储在浏览器的本地。
  • localStorage没有过期时间的设置机制,存储的数据会一直存在于浏览器中,直到被手动删除。
  • localStorage的数据可以在不同页面之间共享,并且在浏览器关闭后仍然保留。
  • localStorage提供了简单的API来存储、获取和删除数据。

思考

在使用cookie时,我们可以设置有效期限,但localStorage本身并不具备该机制,它只能手动删除数据,否则数据将一直保存在浏览器中。那么,我们能否像cookie一样为localStorage设置有效期限呢?为了解决这个问题,我们可以对localStorage进行二次封装,以实现这一功能。

实现思路

在存储数据时,我们可以设置一个过期时间,并对存储的数据进行格式化,以便进行统一校验。在读取数据时,我们可以获取当前时间,并判断数据是否过期。如果数据过期了,我们可以将其删除。

代码实现

目录结构

image.png

enum.ts:定义枚举

typescript
1// 字典 Dictionaries
2// expire过期时间key
3// permanent永久不过期
4export enum Dictionaries {
5  expire = '__expire__',
6  permanent = 'permanent'
7}

type.ts:定义类型

typescript
1import { Dictionaries } from "../enum";
2
3export type Key = string; // key类型
4export type Expire = Dictionaries.permanent | number; // 有效期类型
5
6export interface Data<T> {  // 格式化data类型
7  value: T;
8  [Dictionaries.expire]: Expire;
9}
10
11export interface Result<T> { // 返回值类型
12  message: string;
13  value: T | null;
14}
15
16export interface StorageCls { // class方法约束
17  set: <T>(key: Key, value: T, expire: Expire) => void;
18  get: <T>(key: Key) => Result<T | null>;
19  remove: (key: Key) => void;
20  clear: () => void;
21}

index.ts:主要逻辑实现

typescript
1import { StorageCls, Key, Expire, Data, Result } from "./type";
2import { Dictionaries } from "./enum";
3
4export class Storage implements StorageCls {
5  // 存储接受 key value 和过期时间 默认永久
6  public set<T = any>(key: Key, value: T, expire: Expire = Dictionaries.permanent) {
7    // 格式化数据
8    const data: Data<T> = {
9      value,
10      [Dictionaries.expire]: expire
11    };
12    // 存入localStorage
13    localStorage.setItem(key, JSON.stringify(data));
14  }
15
16  public get<T = any>(key: Key): Result<T | null> {
17    const value = localStorage.getItem(key);
18
19    // 检查读取的数据是否有效
20    if (value) {
21      const obj: Data<T> = JSON.parse(value);
22      const now = new Date().getTime();
23
24      // 检查是否过期,如果过期则删除并返回提示信息
25      if (typeof obj[Dictionaries.expire] === 'number' && obj[Dictionaries.expire] < now) {
26        this.remove(key);
27        return {
28          message: `您的${key}已过期`,
29          value: null
30        };
31      } else {
32        // 数据有效,返回成功读取的值
33        return {
34          message: "成功读
35
36取",
37          value: obj.value
38        };
39      }
40    } else {
41      // 无效的key值
42      console.warn('无效的key值');
43      return {
44        message: `key值无效`,
45        value: null
46      };
47    }
48  }
49
50  // 删除某一项
51  public remove(key: Key) {
52    localStorage.removeItem(key);
53  }
54
55  // 清空所有值
56  public clear() {
57    localStorage.clear();
58  }
59}

rollup.js:简易打包配置(依赖:rolluprollup-plugin-typescript2typescript

旧版写法:

typescript
1import ts from 'rollup-plugin-typescript2';
2import path from 'path';
3
4export default {
5  input: './src/index.ts',
6  output: {
7    file: path.resolve(__dirname, './dist/index.js')
8  },
9  plugins: [
10    ts()
11  ]
12};

新版写法:

typescript
1import ts from 'rollup-plugin-typescript2';
2import path from 'path';
3import { fileURLToPath } from 'url';
4
5const metaUrl = fileURLToPath(import.meta.url);
6const dirName = path.dirname(metaUrl);
7
8export default {
9  input: './src/index.ts',
10  output: {
11    file: path.resolve(dirName, './dist/index.js')
12  },
13  plugins: [
14    ts()
15  ]
16};

代码测试

html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4  <meta charset="UTF-8">
5  <meta http-equiv="X-UA-Compatible" content="IE=edge">
6  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7  <title>Document</title>
8</head>
9<body>
10  <script type="module">
11    import { Storage } from './dist/index.js';
12
13    const sl = new Storage();
14
15    // 五秒后过期
16    sl.set('a', 123, new Date().getTime() + 5000);
17
18    setInterval(() => {
19      const a = sl.get('a');
20      console.log(a);
21    }, 500);
22  </script>
23</body>
24</html>

通过设置定时器,在五秒后观察值是否过期并删除,测试成功。 image.png