fxxy-indexeddb
v0.0.1
Published
indexedDB 封装:支持动态升级 stores、空闲自动关闭、批量原子操作
Maintainers
Readme
fxxy-indexeddb
IndexedDB 操作工具类(支持动态升级 stores),提供增删改查及数据库结构升级能力。
特性
- Promise 化 API:所有方法返回 Promise,原生支持
async/await - 动态结构升级:通过
upgradeDB在版本升级时创建新的 store / 索引 - 跨标签页安全:监听
onversionchange,其他标签页升级时自动让出连接,避免阻塞 - 空闲自动关闭:可配置
idleTimeout,长时间无操作自动关闭连接释放资源 - 懒打开:CRUD 方法自动保证连接已建立,无需手动
openDB - 批量原子操作:
bulkSave单事务执行,任一失败则整体 reject - 索引范围查询:
queryByIndex支持IDBKeyRange与数量限制 - 零依赖:纯原生 IndexedDB 封装,体积小
- 同构兼容:UMD 风格,CommonJS / ESM / 浏览器全局均可引入
安装
npm
npm install fxxy-indexeddbyarn / pnpm
yarn add fxxy-indexeddb
# 或
pnpm add fxxy-indexeddb引入方式
ES Modules(推荐)
import IndexedDBHelper from 'fxxy-indexeddb';CommonJS
const IndexedDBHelper = require('fxxy-indexeddb');浏览器直接引入(UMD)
通过 <script> 引入后,类挂载到全局 window.IndexedDBHelper。CDN 默认指向压缩版 dist/index.min.js(约 3.5KB):
<!-- CDN(推荐,自动使用压缩版) -->
<script src="https://unpkg.com/fxxy-indexeddb"></script>
<!-- 或 jsdelivr -->
<script src="https://cdn.jsdelivr.net/npm/fxxy-indexeddb"></script>
<script>
const helper = new IndexedDBHelper('appDB', 1, { users: { keyPath: 'id' } });
</script>本地引入时优先使用构建产物:
<!-- 压缩版(生产环境推荐) -->
<script src="./node_modules/fxxy-indexeddb/dist/index.min.js"></script>
<!-- 或源码(带注释,便于调试) -->
<script src="./node_modules/fxxy-indexeddb/index.js"></script>Node 环境
Node 中无原生 indexedDB,需提供 polyfill(如 fake-indexeddb):
require('fake-indexeddb/auto'); // 注入全局 indexedDB / IDBKeyRange
const IndexedDBHelper = require('fxxy-indexeddb');快速开始
const helper = new IndexedDBHelper('appDB', 1, {
users: {
keyPath: 'id',
autoIncrement: true,
indexes: [
{ name: 'idx_name', keyPath: 'name', options: { unique: false } },
{ name: 'idx_email', keyPath: 'email', options: { unique: true } }
]
}
}, { idleTimeout: 10 * 60 * 1000 }); // 10 分钟空闲关闭
// 增
const id = await helper.add('users', { name: 'Tom', email: '[email protected]' });
// 查
const tom = await helper.get('users', id);
const all = await helper.getAll('users');
// 改
await helper.put('users', { id, name: 'Tom2', email: '[email protected]' });
// 删
await helper.delete('users', id);
// 关闭
await helper.closeDB();构造参数
new IndexedDBHelper(dbName, version, stores, options)| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| dbName | string | 是 | 数据库名称(同源下唯一标识) |
| version | number | 是 | 数据库版本,正整数,只能递增 |
| stores | Object | 否 | 初始对象存储配置,见下表 |
| options | Object | 否 | 其他选项 |
stores 配置结构
{
<storeName>: {
keyPath: 'id', // 主键路径
autoIncrement: false, // 是否自增主键
indexes: [ // 索引列表(可选)
{
name: 'idx_name', // 索引名
keyPath: 'name', // 索引字段
options: { unique: false } // IDBIndex 参数
}
]
}
}options 选项
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| idleTimeout | number | 1800000(30 分钟) | 空闲自动关闭连接时长(毫秒);传 0 禁用;每次 CRUD 操作都会重置计时器 |
idleTimeout 三种用法
// 1) 默认 30 分钟空闲关闭
new IndexedDBHelper('appDB', 1, { users: { keyPath: 'id' } });
// 2) 自定义 10 分钟
new IndexedDBHelper('appDB', 1, { users: { keyPath: 'id' } },
{ idleTimeout: 10 * 60 * 1000 });
// 3) 禁用(常驻连接)
new IndexedDBHelper('appDB', 1, { users: { keyPath: 'id' } },
{ idleTimeout: 0 });API
连接管理
setStores(newStores)
更新 stores 配置(仅内存)。结构变更需配合 upgradeDB 才能生效。
helper.setStores({
users: { keyPath: 'id', indexes: [{ name: 'idx_name', keyPath: 'name' }] },
logs: { keyPath: 'id', autoIncrement: true }
});
await helper.upgradeDB(2);openDB() → Promise<IDBDatabase>
打开数据库连接,已打开则直接返回。内部触发 onupgradeneeded 创建缺失的 store / 索引。
const db = await helper.openDB();closeDB()
关闭连接并清理空闲计时器,幂等操作。
await helper.closeDB();upgradeDB(newVersion, newStores?) → Promise<void>
升级数据库版本并应用新配置。内部流程:closeDB → 更新 stores/version → openDB。
await helper.upgradeDB(2, {
users: { keyPath: 'id' },
logs: { keyPath: 'id', autoIncrement: true } // 新增 store
});基础 CRUD
| 方法 | 签名 | 返回值 | 说明 |
|------|------|--------|------|
| add | (storeName, data) → Promise<any> | 新记录主键 | 主键冲突会 reject |
| put | (storeName, data) → Promise<any> | 记录主键 | 存在则覆盖 |
| get | (storeName, key) → Promise<any> | 记录对象 / undefined | 未命中返回 undefined |
| getAll | (storeName) → Promise<Array> | 记录数组 | 空 store 返回 [] |
| delete | (storeName, key) → Promise<void> | - | 主键不存在也会 resolve |
| clear | (storeName) → Promise<void> | - | 清空记录,保留 store 结构与索引 |
// 新增
const id = await helper.add('users', { name: 'Tom', age: 20 });
// 查询
const user = await helper.get('users', 1);
if (!user) console.log('not found');
const all = await helper.getAll('users');
// 更新
await helper.put('users', { id: 1, name: 'Tom2', age: 21 });
// 删除
await helper.delete('users', 1);
// 清空
await helper.clear('users');索引查询
queryByIndex(storeName, indexName, range?, limit?) → Promise<Array>
通过索引游标查询,支持 IDBKeyRange 范围与数量限制。
| 参数 | 类型 | 默认 | 说明 |
|------|------|------|------|
| storeName | string | - | 对象存储名 |
| indexName | string | - | 索引名(必须已创建) |
| range | IDBKeyRange \| null | null | 键范围,null 表示全量遍历 |
| limit | number \| null | null | 最大返回条数,null 不限制 |
常用 IDBKeyRange 工厂方法:
IDBKeyRange.only(v)— 精确匹配IDBKeyRange.lowerBound(v)—>= vIDBKeyRange.upperBound(v)—<= vIDBKeyRange.bound(lower, upper)— 区间[lower, upper]
// 查询 age >= 18 的前 10 条
const adults = await helper.queryByIndex(
'users', 'idx_age',
IDBKeyRange.lowerBound(18),
10
);
// 查询 name == 'Tom' 的所有记录
const toms = await helper.queryByIndex(
'users', 'idx_name',
IDBKeyRange.only('Tom')
);批量操作
bulkSave(storeName, items, isPut?) → Promise<Array>
单事务批量新增/更新,任一失败则整体 reject。
| 参数 | 类型 | 默认 | 说明 |
|------|------|------|------|
| storeName | string | - | 对象存储名 |
| items | Array<Object> | - | 记录数组 |
| isPut | boolean | true | true=put 覆盖;false=add 主键冲突会失败 |
返回值:与 items 同序的主键数组;items 为空时返回 []。
// 1) 批量 put(存在则覆盖)
const keys = await helper.bulkSave('users', [
{ id: 1, name: 'A' },
{ id: 2, name: 'B' }
]);
// keys => [1, 2]
// 2) 批量 add(自增主键)
const ids = await helper.bulkSave('logs', [
{ msg: 'a' },
{ msg: 'b' }
], false);完整使用样例
import IndexedDBHelper from 'fxxy-indexeddb';
// 或:const IndexedDBHelper = require('fxxy-indexeddb');
async function main() {
// 1. 创建实例:定义 users store,含 name/email 两个索引
const helper = new IndexedDBHelper('appDB', 1, {
users: {
keyPath: 'id',
autoIncrement: true,
indexes: [
{ name: 'idx_name', keyPath: 'name', options: { unique: false } },
{ name: 'idx_email', keyPath: 'email', options: { unique: true } }
]
}
}, { idleTimeout: 5 * 60 * 1000 }); // 5 分钟空闲关闭
try {
// 2. 批量插入
const ids = await helper.bulkSave('users', [
{ name: 'Tom', email: '[email protected]', age: 20 },
{ name: 'Bob', email: '[email protected]', age: 25 },
{ name: 'Lucy', email: '[email protected]', age: 17 }
], false);
console.log('插入的 id:', ids);
// 3. 主键查询
const tom = await helper.get('users', ids[0]);
console.log('Tom:', tom);
// 4. 索引范围查询:age >= 18
const adults = await helper.queryByIndex(
'users', 'idx_age', // 注意:本样例未声明 idx_age,实际使用需在 stores 中配置
IDBKeyRange.lowerBound(18)
);
console.log('成年人:', adults);
// 5. 索引精确查询:name == 'Bob'
const bobs = await helper.queryByIndex(
'users', 'idx_name',
IDBKeyRange.only('Bob')
);
console.log('Bob:', bobs);
// 6. 更新
await helper.put('users', { id: ids[0], name: 'Tom2', email: '[email protected]', age: 21 });
// 7. 删除
await helper.delete('users', ids[2]);
// 8. 全量查询
const all = await helper.getAll('users');
console.log('剩余记录:', all);
// 9. 升级版本:新增 logs store
await helper.upgradeDB(2, {
users: {
keyPath: 'id', autoIncrement: true,
indexes: [
{ name: 'idx_name', keyPath: 'name' },
{ name: 'idx_email', keyPath: 'email', options: { unique: true } }
]
},
logs: { keyPath: 'id', autoIncrement: true }
});
// 10. 写入新 store
await helper.add('logs', { msg: 'app started', ts: Date.now() });
} catch (err) {
console.error('操作失败:', err);
} finally {
// 11. 显式关闭
await helper.closeDB();
}
}
main();注意事项
- 版本号只能递增:
upgradeDB(newVersion)中newVersion必须大于当前版本,否则抛错。 - stores 配置不会自动删除结构:
openDB/upgradeDB只创建缺失的 store / 索引,不会删除已有的,确保数据安全。 - 跨标签页冲突:当其他标签页要求升级版本时,本实例会自动关闭连接(
onversionchange),下次操作时自动重开。 idleTimeout语义:每次 CRUD 操作完成(无论成功或失败)都会重置计时器;仅在「长时间无任何操作」时才会触发关闭。bulkSave原子性:所有 items 在同一事务内执行,任一失败则整体 reject,已成功的也会回滚。addvsput:add主键冲突会失败;put存在则覆盖。批量场景按需选择isPut参数。- 索引必须先配置:
queryByIndex使用的索引名必须在stores配置中声明并通过openDB/upgradeDB创建。 get未命中:主键不存在时返回undefined,不会抛错。- Node 环境需 polyfill:浏览器原生支持
indexedDB;Node 中需先注入fake-indexeddb等全局 polyfill。
构建产物
本项目使用零依赖的 build.js 构建多格式产物(仅使用 Node 内置模块,兼容任意 Node 版本)。源码 index.js 为 UMD 风格(运行时判断 module.exports),构建后额外生成两种格式:
| 文件 | 格式 | 体积 | 用途 | 入口字段 |
|------|------|------|------|---------|
| index.js | UMD(源码) | ~20KB | CommonJS / 调试 | main |
| dist/index.mjs | ESM | ~20KB | 打包工具(Webpack/Rollup/Vite) | module |
| dist/index.min.js | IIFE(压缩) | ~5KB | 浏览器 <script> / CDN | browser / unpkg / jsdelivr |
本地构建
# 无需 npm install,零依赖直接构建
# 完整构建:清理 + ESM + 压缩
npm run build
# 单独构建 ESM
npm run build:esm
# 单独构建压缩版
npm run build:min
# 监听源文件变化,自动重建 ESM(开发模式)
npm run watch
# 清理构建产物
npm run clean构建脚本通过 prepublishOnly 钩子在 npm publish 前自动执行,确保发布时产物最新。
零依赖优势:无需安装任何 devDependencies,任意 Node 版本(含 Node 6+)均可直接
npm run build。压缩采用纯文本处理(移除注释 + 折叠空白 + 标点周边空格清理),体积虽不及 terser/esbuild 极致,但无版本兼容问题,对本工具类已足够。
作为 npm 包发布
本项目已按 npm 包规范组织,package.json 关键字段如下:
{
"name": "fxxy-indexeddb",
"version": "0.0.1",
"description": "indexedDB 封装:支持动态升级 stores、空闲自动关闭、批量原子操作",
"main": "index.js", // CommonJS 入口(源码 UMD)
"module": "dist/index.mjs", // ESM 入口(打包工具按此解析)
"browser": "dist/index.min.js", // 浏览器入口(压缩 IIFE)
"unpkg": "dist/index.min.js", // unpkg CDN 默认文件
"jsdelivr": "dist/index.min.js", // jsdelivr CDN 默认文件
"files": ["index.js", "build.js", "dist/", "README.md"], // 发布白名单
"scripts": {
"clean": "node build.js clean", // 清理 dist
"build:esm": "node build.js esm", // 构建 ESM
"build:min": "node build.js min", // 构建压缩版
"build": "node build.js", // 完整构建
"watch": "node build.js watch", // 监听模式
"prepublishOnly": "npm run build" // 发布前自动构建
},
"keywords": ["indexeddb", "database", "storage", "promise", "browser"],
"license": "ISC"
// 无 devDependencies —— 零依赖构建,任意 Node 版本通用
}发布流程
# 1. 登录 npm
npm login
# 2. 校验包名是否可用(未占用)
npm view fxxy-indexeddb
# 3. 检查即将发布的文件清单
npm pack --dry-run
# 4. 发布
npm publish
# 发布 beta 版本
npm publish --tag beta后续建议
- 补充
index.d.ts类型声明文件,便于 TS 项目使用 - 增加
.npmignore或在package.json中明确files字段,避免发布.history/等无关目录 - 添加单元测试(如
vitest+fake-indexeddb)并接入 CI - 体积优化:若需更小产物,可提供 ESM 独立构建(如
dist/index.mjs)
License
ISC
