前言
很多小伙伴比较喜欢直接使用 localStorage 或者 sessionStorage 。虽然可以满足大多数场景,但是在特殊需求情况下就需要进行二次封装处理,例如 设置过期时间、使用其他存储方式、每次存取需要序列化与反序列化。而且有些项目体量大、产品一直迭代、维护人员和开发人员一茬接一茬的这种项目,有时在不同模块 甚至不同页面 使用的缓存都是单独封装的 或者 是’裸奔’的。为方便项目使用,特对常规操作进行封装。此篇文章会带着你从零到一的封装一个本地缓存类,从需求分析到架构设计以及部分设计模式的讲解。
脑图设计
脑图就是进行头脑风暴。将自己的碎片式的 **idea **抽象出关键点,通过关键点在去思考如何去实现以及应该怎么做。
支持多种驱动
适配器模式
有时候也称包装样式或者包装(wrapper)。将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类能在一起工作,做法是将类自己的接口包裹在一个已存在的类中。
核心: 实现了相同入参,相同返回。
例如后台小伙伴经常说的JDBC其实就是 是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。这样就可以通过同样的方法操作不同的数据库了。
驱动的接口设计
本文代码并未使用 **ts **只是我认为这样的表达可能会相对清楚一些。(挖个坑 写一个后面使用 ts 重写这个小工具,然后发布为npm包的教程)
/** 接口设计 */
interface ZStorageInterface {
/**
* 驱动名字
*/
name: string;
/**
* getÏ
* 通过key获取值
*/
get: (key: string) => string;
/**
* set
* 通过key设置值
*/
set: (key: string, value: string) => void;
/**
* remove
* 通过key删除值
*/
remove: (key: string) => void;
/**
* clear
* 清除全部
*/
clear: () => void;
/**
* hasKey
* 判断key 是否存在
*/
hasKey: (key: string) => boolean;
}
实现LocalStorage驱动
export default {
name: "localStorage",
get: (key) => window.localStorage.getItem(key),
set: (key, value) => window.localStorage.setItem(key, value),
remove: (key) => window.localStorage.removeItem(key),
clear: () => window.localStorage.clear(),
hasKey: (key) => window.localStorage.hasOwnProperty(key),
};
实现过期时间
定义存储数据格式
存储数据格式伪代码
const payload = {
value: value,
time: Date.now(),
expire: expire,
};
校验是payload是否过期伪代码
const checkExpire = (payload) =>
payload.expire &&
payload.expire != -1 &&
payload.expire < (Date.now() - payload.time) / 1000;
实现存值
ZStorage 核心类实现
/** 驱动器引入 */
import driverMap from "./drivers/index.js";
const defaultOptions = {
/** sessionStorage localStorage cookie 及 custom 自定义存储 */
driver: "localStorage",
/** key前缀 */
prefix: "test_",
/** 编码类型 */
charset: "utf-8",
};
class ZStorage {
constructor(options) {
this.options = { ...defaultOptions, ...options };
this._driverInit();
}
/** 驱动初始化 */
_driverInit() {
const driver = this.options.driver;
if (Object.keys(driverMap).includes(driver)) {
this._store = driverMap[driver];
} else if (driver == "custom") {
this._store = this.options.customDriver;
} else {
throw new Error("驱动器不存在");
}
}
/**
* 获取驱动器
* @deprecated (废弃)
* @returns
*/
getDriver() {
return this._store;
}
/**
* 获取前缀
*/
getPrefix() {
return this.options.prefix;
}
/**
* 自动添加前缀
* @param {*} key
* @returns
*/
_autoAddPrefix(key) {
return this.options.prefix + key;
}
/**
* 校验是否过期
*/
_checkExpire = (payload) =>
payload.expire &&
payload.expire != -1 &&
payload.expire < (Date.now() - payload.time) / 1000;
/**
* 设置值
* @param {*} key
* @param {*} value
*/
set(key, value, expire = -1) {
const payload = {
value: value,
time: Date.now(),
expire: expire,
};
this._store.set(this._autoAddPrefix(key), JSON.stringify(payload));
}
/**
* 获取值
* @param {*} key
* @param {*} defaultValue
* @returns
*/
get(key, defaultValue = undefined) {
if (!this.hasKey(key)) return defaultValue;
const data = JSON.parse(this._store.get(this._autoAddPrefix(key)));
if (checkExpire(data)) {
this.remove(key);
return defaultValue;
}
return data.value;
}
/**
* 删除key
* @param {*} key
*/
remove(key) {
this._store.remove(this._autoAddPrefix(key));
}
/**
* 清空
*/
clear() {
this._store.clear();
}
/**
* 是否存在key
* @param {*} key
* @returns
*/
hasKey(key) {
return this._store.hasKey(this._autoAddPrefix(key));
}
}
驱动器
sessionStorage
export default {
name: "sessionStorage",
get: (key) => window.localStorage.getItem(key),
set: (key, value) => window.localStorage.setItem(key, value),
remove: (key) => window.localStorage.removeItem(key),
clear: () => window.localStorage.clear(),
hasKey: (key) => window.localStorage.hasOwnProperty(key),
};
localStorage
export default {
name: "localStorage",
get: (key) => window.localStorage.getItem(key),
set: (key, value) => window.localStorage.setItem(key, value),
remove: (key) => window.localStorage.removeItem(key),
clear: () => window.localStorage.clear(),
hasKey: (key) => window.localStorage.hasOwnProperty(key),
};
cookie
import Cookies from "js-cookie";
export default {
name: "cookie",
get: Cookies.get,
set: Cookies.set,
remove: (key) => Cookies.remove(key, { path: "" }),
clear: () => {
document.cookie = "";
},
hasKey: (key) => Cookies.get().hasOwnProperty(key),
};
CustomDriver 实现定制化驱动
class CustomDriver {
data = {};
name = "CustomDriver";
get(key) {
return this.data[key];
}
set(key, value) {
this.data[key] = value;
}
remove(key) {
delete this.data[key];
}
clear() {
this.data = {};
}
hasKey(key) {
return this.data.hasOwnProperty(key);
}
}
测试
const s = new ZStorage({
driver: "custom",
customDriver: new CustomDriver(),
});
s.set("key", "我是测试数据", 1);
console.log(s.get("key")); // 我是测试数据
setTimeout(() => {
console.log(s.get("key")); // 我是测试数据
}, 800);
setTimeout(() => {
console.log(s.get("key")); // undefined
}, 1000);