本文记录了如何从零开始开发一个统一的浏览器存储库
yu-storage,支持 localStorage、sessionStorage、Cookie 和 IndexedDB,并成功发布到 npm 的完整过程。
📖 前言
在前端开发中,我们经常需要处理各种浏览器存储:
- localStorage - 持久化存储
- sessionStorage - 会话级存储
- Cookie - 带过期时间的存储
- IndexedDB - 大容量存储
每个存储 API 都不相同,使用起来很麻烦。基于大佬的想法,实现此功能。
yu-storage/
├── src/
│ ├── base-storage.js # 基础存储抽象类
│ ├── local-storage.js # localStorage 实现
│ ├── session-storage.js # sessionStorage 实现
│ ├── cookie-storage.js # Cookie 实现
│ ├── indexeddb-storage.js # IndexedDB 实现
│ └── yu-storage.js # 主入口文件
├── dist/ # 构建输出
├── example.html # 使用示例
├── test.html # 功能测试
└── README.md # 文档
🔧 核心实现
1. localStorage 实现
export class LocalStorage extends BaseStorage {
constructor() {
super('localStorage');
}
isAvailable() {
try {
const test = '__localStorage_test__';
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch (e) {
return false;
}
}
set(key, value, options = {}) {
if (!this.isAvailable()) {
console.error('localStorage is not available');
return false;
}
try {
const data = JSON.stringify(value);
localStorage.setItem(key, data);
return true;
} catch (e) {
console.error('Error setting localStorage item:', e);
return false;
}
}
get(key, options = {}) {
if (!this.isAvailable()) {
console.error('localStorage is not available');
return null;
}
try {
const data = localStorage.getItem(key);
if (data === null) {
return null;
}
return JSON.parse(data);
} catch (e) {
console.error('Error getting localStorage item:', e);
return null;
}
}
// ... 其他方法
}
2. Cookie 实现(重点难点)
Cookie 的实现是最复杂的,需要处理路径、域名、过期时间等参数:
export class CookieStorage extends BaseStorage {
set(key, value, options = {}) {
if (!this.isAvailable()) {
console.error('Cookie is not available');
return false;
}
try {
const {
expires = 365,
path = window.location.pathname || '/',
domain = '',
secure = false,
sameSite = 'Lax'
} = options;
// 将值序列化为 JSON 字符串
const serializedValue = JSON.stringify(value);
let cookie = `${encodeURIComponent(key)}=${encodeURIComponent(serializedValue)}`;
if (expires) {
const date = new Date();
date.setTime(date.getTime() + (expires * 24 * 60 * 60 * 1000));
cookie += `; expires=${date.toUTCString()}`;
}
if (path) {
cookie += `; path=${path}`;
}
if (domain) {
cookie += `; domain=${domain}`;
}
if (secure) {
cookie += '; secure';
}
if (sameSite) {
cookie += `; samesite=${sameSite}`;
}
document.cookie = cookie;
return true;
} catch (e) {
console.error('Error setting cookie:', e);
return false;
}
}
get(key, options = {}) {
if (!this.isAvailable()) {
console.error('Cookie is not available');
return null;
}
try {
const name = `${encodeURIComponent(key)}=`;
const decodedCookie = decodeURIComponent(document.cookie);
const ca = decodedCookie.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) === ' ') {
c = c.substring(1);
}
if (c.indexOf(name) === 0) {
const value = c.substring(name.length, c.length);
// 尝试解析 JSON,如果失败则返回原始字符串
try {
return JSON.parse(value);
} catch (e) {
return value;
}
}
}
return null;
} catch (e) {
console.error('Error getting cookie:', e);
return null;
}
}
}
3. IndexedDB 实现(异步处理)
IndexedDB 是异步的,需要返回 Promise:
export class IndexedDBStorage extends BaseStorage {
constructor() {
super('indexedDB');
this.dbName = 'YuStorageDB';
this.storeName = 'YuStorageStore';
this.db = null;
}
async initIndexedDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve(this.db);
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(this.storeName)) {
db.createObjectStore(this.storeName);
}
};
});
}
async set(key, value, options = {}) {
if (!this.isAvailable()) {
console.error('IndexedDB is not available');
return false;
}
try {
if (!this.db) {
await this.initIndexedDB();
}
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], 'readwrite');
const store = transaction.objectStore(this.storeName);
const request = store.put(value, key);
request.onsuccess = () => resolve(true);
request.onerror = () => reject(request.error);
});
} catch (e) {
console.error('Error setting IndexedDB item:', e);
return false;
}
}
// ... 其他异步方法
}
4. 统一入口类
class YuStorage {
constructor(defaultType = 'local') {
this.types = {
local: 'localStorage',
session: 'sessionStorage',
cookie: 'cookie',
indexeddb: 'indexedDB'
};
this.storages = {
[this.types.local]: new LocalStorage(),
[this.types.session]: new SessionStorage(),
[this.types.cookie]: new CookieStorage(),
[this.types.indexeddb]: new IndexedDBStorage()
};
this.defaultType = this.types[defaultType] || this.types.local;
}
set(key, value, options = {}) {
const storage = this.getStorage(options.type);
return storage.set(key, value, options);
}
get(key, options = {}) {
const storage = this.getStorage(options.type);
return storage.get(key, options);
}
remove(key, options = {}) {
const storage = this.getStorage(options.type);
return storage.remove(key, options);
}
clear(options = {}) {
const storage = this.getStorage(options.type);
return storage.clear(options);
}
keys(options = {}) {
const storage = this.getStorage(options.type);
return storage.keys(options);
}
getStorage(type) {
const storageType = this.types[type] || this.defaultType;
const storage = this.storages[storageType];
if (!storage) {
throw new Error(`Unsupported storage type: ${type}`);
}
if (!storage.isAvailable()) {
console.warn(`Storage type ${storageType} is not available, falling back to localStorage`);
return this.storages[this.types.local];
}
return storage;
}
}
🧪 测试与调试
遇到的问题
- Cookie 在 file:// 协议下不可用
- 问题:直接打开 HTML 文件时 Cookie 无法工作
- 解决:使用 HTTP 服务器进行测试
📦 发布到 npm
# 检查登录状态
npm whoami
# 检查包名是否可用
npm view yu-storage
# 发布到 npm
npm publish
🎉 使用效果
安装
npm install yu-storage
使用示例
import yuStorage from 'yu-storage';
// 使用默认存储(localStorage)
yuStorage.set('name', '张三');
const name = yuStorage.get('name');
console.log(name); // 张三
// 指定存储类型
yuStorage.set('theme', 'dark', { type: 'session' });
yuStorage.set('token', 'abc123', { type: 'cookie', expires: 7 });
yuStorage.set('settings', { lang: 'zh' }, { type: 'indexeddb' });
// 获取数据
const theme = yuStorage.get('theme', { type: 'session' });
const token = yuStorage.get('token', { type: 'cookie' });
const settings = await yuStorage.get('settings', { type: 'indexeddb' });
🔗 相关链接
- npm 包: www.npmjs.com/package/yu-…
- 源码仓库: gitee.com/xcxsj/yu-st…
- 安装命令:
npm install yu-storage
如果觉得有用,请给个 ⭐️ 支持一下!