封装LocalStorage并支持过期时间
2023-06-28 Web
在浏览器开发中,我们经常需要在客户端存储数据,以便在不同页面或会话之间进行持久化。cookie
和localStorage
是常用的客户端存储机制。然而,它们在一些方面存在一些限制和差异。
cookie #
cookie
是存储在客户端的小型文本文件,每当用户与服务器进行通信时都会在请求和响应中传递。cookie
可以设置过期时间,可以是会话级别(浏览器关闭后过期)或持久性(在指定时间之后过期)。cookie
可以在不同页面之间共享,并且可以用于实现用户身份验证、记住用户偏好等功能。- 但是,每个请求都会将
cookie
发送到服务器,这可能会增加网络流量,并且对于每个请求都需要服务器进行处理。
localStorage #
localStorage
是HTML5中提供的一个客户端存储机制,用于存储较大量级的数据。localStorage
使用key-value
的方式存储数据,并且数据存储在浏览器的本地。localStorage
没有过期时间的设置机制,存储的数据会一直存在于浏览器中,直到被手动删除。localStorage
的数据可以在不同页面之间共享,并且在浏览器关闭后仍然保留。localStorage
提供了简单的API来存储、获取和删除数据。
思考 #
在使用cookie
时,我们可以设置有效期限,但localStorage
本身并不具备该机制,它只能手动删除数据,否则数据将一直保存在浏览器中。那么,我们能否像cookie
一样为localStorage
设置有效期限呢?为了解决这个问题,我们可以对localStorage
进行二次封装,以实现这一功能。
实现思路 #
在存储数据时,我们可以设置一个过期时间,并对存储的数据进行格式化,以便进行统一校验。在读取数据时,我们可以获取当前时间,并判断数据是否过期。如果数据过期了,我们可以将其删除。
代码实现 #
目录结构
enum.ts
:定义枚举
// 字典 Dictionaries
// expire过期时间key
// permanent永久不过期
export enum Dictionaries {
expire = '__expire__',
permanent = 'permanent'
}
// 字典 Dictionaries
// expire过期时间key
// permanent永久不过期
export enum Dictionaries {
expire = '__expire__',
permanent = 'permanent'
}
type.ts
:定义类型
import { Dictionaries } from "../enum";
export type Key = string; // key类型
export type Expire = Dictionaries.permanent | number; // 有效期类型
export interface Data<T> { // 格式化data类型
value: T;
[Dictionaries.expire]: Expire;
}
export interface Result<T> { // 返回值类型
message: string;
value: T | null;
}
export interface StorageCls { // class方法约束
set: <T>(key: Key, value: T, expire: Expire) => void;
get: <T>(key: Key) => Result<T | null>;
remove: (key: Key) => void;
clear: () => void;
}
import { Dictionaries } from "../enum";
export type Key = string; // key类型
export type Expire = Dictionaries.permanent | number; // 有效期类型
export interface Data<T> { // 格式化data类型
value: T;
[Dictionaries.expire]: Expire;
}
export interface Result<T> { // 返回值类型
message: string;
value: T | null;
}
export interface StorageCls { // class方法约束
set: <T>(key: Key, value: T, expire: Expire) => void;
get: <T>(key: Key) => Result<T | null>;
remove: (key: Key) => void;
clear: () => void;
}
index.ts
:主要逻辑实现
import { StorageCls, Key, Expire, Data, Result } from "./type";
import { Dictionaries } from "./enum";
export class Storage implements StorageCls {
// 存储接受 key value 和过期时间 默认永久
public set<T = any>(key: Key, value: T, expire: Expire = Dictionaries.permanent) {
// 格式化数据
const data: Data<T> = {
value,
[Dictionaries.expire]: expire
};
// 存入localStorage
localStorage.setItem(key, JSON.stringify(data));
}
public get<T = any>(key: Key): Result<T | null> {
const value = localStorage.getItem(key);
// 检查读取的数据是否有效
if (value) {
const obj: Data<T> = JSON.parse(value);
const now = new Date().getTime();
// 检查是否过期,如果过期则删除并返回提示信息
if (typeof obj[Dictionaries.expire] === 'number' && obj[Dictionaries.expire] < now) {
this.remove(key);
return {
message: `您的${key}已过期`,
value: null
};
} else {
// 数据有效,返回成功读取的值
return {
message: "成功读
取",
value: obj.value
};
}
} else {
// 无效的key值
console.warn('无效的key值');
return {
message: `key值无效`,
value: null
};
}
}
// 删除某一项
public remove(key: Key) {
localStorage.removeItem(key);
}
// 清空所有值
public clear() {
localStorage.clear();
}
}
import { StorageCls, Key, Expire, Data, Result } from "./type";
import { Dictionaries } from "./enum";
export class Storage implements StorageCls {
// 存储接受 key value 和过期时间 默认永久
public set<T = any>(key: Key, value: T, expire: Expire = Dictionaries.permanent) {
// 格式化数据
const data: Data<T> = {
value,
[Dictionaries.expire]: expire
};
// 存入localStorage
localStorage.setItem(key, JSON.stringify(data));
}
public get<T = any>(key: Key): Result<T | null> {
const value = localStorage.getItem(key);
// 检查读取的数据是否有效
if (value) {
const obj: Data<T> = JSON.parse(value);
const now = new Date().getTime();
// 检查是否过期,如果过期则删除并返回提示信息
if (typeof obj[Dictionaries.expire] === 'number' && obj[Dictionaries.expire] < now) {
this.remove(key);
return {
message: `您的${key}已过期`,
value: null
};
} else {
// 数据有效,返回成功读取的值
return {
message: "成功读
取",
value: obj.value
};
}
} else {
// 无效的key值
console.warn('无效的key值');
return {
message: `key值无效`,
value: null
};
}
}
// 删除某一项
public remove(key: Key) {
localStorage.removeItem(key);
}
// 清空所有值
public clear() {
localStorage.clear();
}
}
rollup.js
:简易打包配置(依赖:rollup
、rollup-plugin-typescript2
、typescript
)
旧版写法:
import ts from 'rollup-plugin-typescript2';
import path from 'path';
export default {
input: './src/index.ts',
output: {
file: path.resolve(__dirname, './dist/index.js')
},
plugins: [
ts()
]
};
import ts from 'rollup-plugin-typescript2';
import path from 'path';
export default {
input: './src/index.ts',
output: {
file: path.resolve(__dirname, './dist/index.js')
},
plugins: [
ts()
]
};
新版写法:
import ts from 'rollup-plugin-typescript2';
import path from 'path';
import { fileURLToPath } from 'url';
const metaUrl = fileURLToPath(import.meta.url);
const dirName = path.dirname(metaUrl);
export default {
input: './src/index.ts',
output: {
file: path.resolve(dirName, './dist/index.js')
},
plugins: [
ts()
]
};
import ts from 'rollup-plugin-typescript2';
import path from 'path';
import { fileURLToPath } from 'url';
const metaUrl = fileURLToPath(import.meta.url);
const dirName = path.dirname(metaUrl);
export default {
input: './src/index.ts',
output: {
file: path.resolve(dirName, './dist/index.js')
},
plugins: [
ts()
]
};
代码测试 #
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="module">
import { Storage } from './dist/index.js';
const sl = new Storage();
// 五秒后过期
sl.set('a', 123, new Date().getTime() + 5000);
setInterval(() => {
const a = sl.get('a');
console.log(a);
}, 500);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="module">
import { Storage } from './dist/index.js';
const sl = new Storage();
// 五秒后过期
sl.set('a', 123, new Date().getTime() + 5000);
setInterval(() => {
const a = sl.get('a');
console.log(a);
}, 500);
</script>
</body>
</html>
通过设置定时器,在五秒后观察值是否过期并删除,测试成功。
版权属于: vincent
转载时须注明出处及本声明
Tags:# Javascript # web安全 # 缓存