📦 安装
MetonaSqlark 支持多种引入方式,覆盖 npm、CDN、ESM、CJS 所有常见场景。
npm 安装(推荐)
# 配置 Gitea Registry(一次性) npm config set @metona-team:registry https://git.metona.cn/api/packages/MetonaTeam/npm/ # 安装 npm install @metona-team/metona-sqlark
CDN / UMD
<!-- UMD 格式,暴露 window.MetonaSqlark 和 window.MeSqlark --> <script src="https://git.metona.cn/.../metona-sqlark.min.js"></script> <script> const db = await window.MetonaSqlark.create({...}); // 或 window.MeSqlark.create(...) — 完全等价 </script>
ESM
import { MetonaSqlark, MeSqlark } from '@metona-team/metona-sqlark'; // MeSqlark 是 MetonaSqlark 的别名,行为完全一致
CJS
const { MetonaSqlark } = require('@metona-team/metona-sqlark');
输出文件说明:
| 文件 | 格式 | 用途 |
|---|---|---|
metona-sqlark.js | UMD | 浏览器开发版(含 sourcemap) |
metona-sqlark.min.js | UMD (minified) | 生产环境(~42KB / ~10KB gzip) |
metona-sqlark.esm.js | ES Module | 现代打包工具 / 浏览器 ESM |
metona-sqlark.cjs.js | CommonJS | Node.js require() |
metona-sqlark.d.ts | TypeScript 声明 | 类型提示 |
🏗 创建数据库
MetonaSqlark.create(config) — 工厂函数,自动创建并初始化数据库实例。
const db = await MetonaSqlark.create({ name: 'my-app', mode: 'hybrid', // 'memory' | 'disk' | 'hybrid' diskEngine: 'indexeddb', // 'indexeddb' | 'opfs'(仅 disk/hybrid 生效) version: 1, plugins: [], // MetonaPlugin[] onReady: (db) => {}, // 就绪回调 onError: (err) => {}, // 错误回调 }); // 也可手动实例化 const db2 = new MetonaSqlark(config); await db2.init(); // 检查状态 db.isReady(); // true // 关闭 await db.close();
📋 定义表
使用 db.defineTable(name, columns) 定义表结构。
await db.defineTable('users', { id: { type: 'string', primaryKey: true }, name: { type: 'string', required: true }, email: { type: 'string', unique: true, index: true }, age: { type: 'number', default: 0, min: 0, max: 150 }, active: { type: 'boolean', default: true }, birthday: { type: 'date' }, meta: { type: 'json' }, dept_id: { type: 'number', references: 'departments.id' }, }); // 表操作 await db.getTableNames(); // ['users'] await db.dropTable('users'); // 删除表
| ColumnDef 属性 | 类型 | 说明 |
|---|---|---|
type | 'string'|'number'|'boolean'|'date'|'json' | 数据类型 |
primaryKey | boolean | 主键(每表至少一个) |
required | boolean | 是否必填 |
unique | boolean | 唯一约束(自动建索引) |
index | boolean | 创建哈希索引,O(1) 加速查询 |
default | unknown | 默认值 |
maxLength | number | 字符串最大长度 |
min/max | number | 数值范围 |
references | string | 外键引用 'table.column' |
🔍 SQL 查询
db.query(sql) — 执行标准 SQL 字符串,返回查询结果。
完整 SQL 语法支持
// SELECT — 核心查询 const rows = await db.query(`SELECT * FROM users WHERE age > 18 ORDER BY name ASC LIMIT 10 OFFSET 0`); // INSERT — 插入数据 await db.query("INSERT INTO users (id, name, age) VALUES ('1', 'Alice', 30)"); await db.query("INSERT INTO users VALUES ('2', 'Bob', 25)"); // 支持多行插入 await db.query("INSERT INTO users VALUES ('3', 'C'), ('4', 'D')"); // UPDATE — 更新数据 await db.query("UPDATE users SET age = 31, active = true WHERE id = '1'"); // DELETE — 删除数据 await db.query("DELETE FROM users WHERE id = '1'"); // DDL — 表结构操作 await db.query(`CREATE TABLE products ( id STRING PRIMARY KEY, name STRING NOT NULL, price NUMBER DEFAULT 0 )`); await db.query('DROP TABLE products');
条件表达式
// 比较运算符 `WHERE age > 18 AND name LIKE 'A%'` `WHERE salary >= 5000 OR dept = 'Engineering'` // IN / NOT IN `WHERE dept IN ('Engineering', 'Sales')` // NULL 检查 `WHERE email IS NULL` `WHERE email IS NOT NULL` // NOT 取反 `WHERE NOT (age < 18 OR age > 65)` // 嵌套条件 `WHERE (age > 18 AND active = true) OR role = 'admin'`
⛓ Query Builder
链式 API,TypeScript 友好,享受 IDE 自动补全。
SELECT
const users = db.table('users'); // 全量查询 await users.select().execute(); // 指定列 + 条件 + 排序 + 分页 const result = await users .select(['name', 'age', 'email']) .where({ age: { $gt: 18 }, name: { $like: 'A%' }, }) .orderBy('age', 'desc') .limit(10) .offset(0) .execute();
INSERT
// 单行插入 — 返回主键值 const pk = await users.insert({ id: '1', name: 'Alice', age: 30 }); // 批量插入 — 返回主键值数组 const pks = await users.insertMany([ { id: '2', name: 'Bob', age: 25 }, { id: '3', name: 'Charlie', age: 35 }, ]);
UPDATE / DELETE
// 更新 — 返回影响行数 const updated = await users .update({ age: 31, active: false }) .where({ id: '1' }) .execute(); // 1 // 删除 — 返回影响行数 const deleted = await users .delete() .where({ id: '1' }) .execute(); // 1 // 全表删除 await users.delete().execute();
🔗 JOIN 查询
支持 INNER / LEFT / RIGHT / CROSS JOIN,SQL 和 Query Builder 两种方式。
// SQL 方式 — 支持表别名 await db.query(`SELECT u.name, d.name AS dept_name FROM users u INNER JOIN departments d ON u.dept_id = d.id LEFT JOIN orders o ON u.id = o.user_id WHERE d.name = 'Engineering' ORDER BY u.name`); // Query Builder 方式 await db.table('users').select() .as('u') .innerJoin('departments', { 'u.dept_id': { $col: 'd.id' } }, 'd') .leftJoin('orders', { 'u.id': { $col: 'o.user_id' } }, 'o') .execute(); // CROSS JOIN — 笛卡尔积 .crossJoin('metadata', 'm')
📊 GROUP BY & 聚合
五大聚合函数 + GROUP BY + HAVING + DISTINCT,完整的数据分析能力。
// GROUP BY + 聚合 await db.query(`SELECT dept, COUNT(*) as cnt, SUM(salary) as total, AVG(salary) as avg_sal, MIN(age), MAX(age) FROM employees GROUP BY dept HAVING COUNT(*) > 1 ORDER BY total DESC LIMIT 5`); // DISTINCT 去重 await db.query('SELECT DISTINCT dept FROM employees'); // 聚合函数别名 await db.query('SELECT COUNT(*) AS total_users, AVG(age) AS avg_age FROM users');
🎯 WHERE 操作符
Query Builder 使用 $ 前缀操作符,支持逻辑组合。
| 操作符 | 含义 | 示例 |
|---|---|---|
直接值 / $eq | 等于 | { age: 30 } / { age: { $eq: 30 } } |
$ne | 不等于 | { age: { $ne: 30 } } |
$gt / $gte | 大于 / 大于等于 | { age: { $gt: 18 } } |
$lt / $lte | 小于 / 小于等于 | { age: { $lt: 65 } } |
$in / $nin | 在列表中 / 不在 | { dept: { $in: ['IT','HR'] } } |
$like | 模糊匹配 | { name: { $like: 'A%' } } |
$and | 逻辑与 | { $and: [{...}, {...}] } |
$or | 逻辑或 | { $or: [{...}, {...}] } |
$not | 逻辑非 | { age: { $not: { $gt: 18 } } } |
$col | 列引用(JOIN ON) | { 'a.id': { $col: 'b.a_id' } } |
// 复合条件 .where({ $or: [ { age: { $lt: 18 } }, { age: { $gt: 65 } }, ], active: true, name: { $like: 'A%', $ne: 'Admin' }, })
🔒 事务
保证原子性,事务内步骤失败自动报错。
await db.transaction(async (trx) => { // trx.table() 获取事务内表操作对象 await trx.table('users').insert({ id: '3', name: 'Charlie' }); await trx.table('orders').insert({ id: 'o1', userId: '3', amount: 99 }); await trx.table('accounts').update({ balance: 1 }) .where({ userId: '3' }) .execute(); // 也可返回结果 return 'success'; });
🔄 数据迁移
按版本号管理表结构变更。
// 注册迁移 db.addMigration(2, async (db) => { await db.defineTable('products', { id: { type: 'string', primaryKey: true }, name: { type: 'string', required: true }, }); }); db.addMigration(3, async (db) => { // 添加新列、数据迁移等 await db.query("UPDATE users SET role = 'user' WHERE role IS NULL"); }); // 执行迁移到目标版本 await db.migrateTo(3); // 依次执行 v2, v3 的迁移函数
📤 导入导出
// 导出单表 — 返回 JSON 数组 const userData = await db.exportTable('users'); // [{ id: '1', name: 'Alice', ... }, ...] // 导出全库 — 返回 { tableName: rows[] } const allData = await db.exportAll(); // { users: [...], orders: [...], products: [...] } // 导入数据 — 返回主键列表 const pks = await db.importTable('users', userData);
🧩 插件 & 钩子
14 种生命周期钩子,支持插件机制。
| 钩子名称 | 触发时机 | 参数 |
|---|---|---|
beforeCreateTable | 创建表前 | schema |
afterCreateTable | 创建表后 | schema |
beforeDropTable | 删除表前 | tableName |
afterDropTable | 删除表后 | tableName |
beforeInsert | 插入前 | rows[] |
afterInsert | 插入后 | rows[] |
beforeUpdate | 更新前 | query, updates |
afterUpdate | 更新后 | query, updates, count |
beforeDelete | 删除前 | query |
afterDelete | 删除后 | query, count |
beforeQuery | SQL 查询前 | sql |
afterQuery | SQL 查询后 | sql, result |
beforeTransaction | 事务开始前 | - |
afterTransaction | 事务完成后 | - |
// 注册钩子 db.on('beforeInsert', async (row) => { console.log('即将插入:', row); // 可在此校验、转换数据 }); db.on('afterQuery', async (sql, result) => { console.log(`查询完成 [${sql}] → ${(result as any[]).length} 行`); }); // 注册自定义插件 const loggerPlugin = { name: 'logger', version: '1.0.0', description: '记录所有数据库操作', priority: 100, install(db) { db.on('beforeQuery', (sql) => console.log('SQL:', sql)); }, destroy() { /* 清理 */ }, }; // 在 create 配置中注册 const db = await MetonaSqlark.create({ name: 'my-app', plugins: [loggerPlugin], });
📡 发布订阅
// 订阅表变更 const unsubscribe = db.subscribe('users', (event) => { // event.type: 'insert' | 'update' | 'delete' // event.row: 被操作的行数据 console.log(`users 表 ${event.type}`, event.row); }); // 手动触发变更 db.emit('users', { type: 'insert', row: { id: '1', name: 'Alice' } }); // 取消订阅 unsubscribe();
⚛️ React 集成
import { useQuery, useTable, useDatabase } from '@metona-team/metona-sqlark/react'; import { db } from './db'; function UserList() { // 执行 SQL 查询,自动响应 db 变化 const { data, loading, error, refresh } = useQuery( db, 'SELECT * FROM users WHERE age > 18', [/* deps */] ); if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; return ( <div> {data.map(u => <div key={u.id}>{u.name} ({u.age})</div>)} <button onClick={refresh}>刷新</button> </div> ); } // 便捷 hook — 查询整张表 const { data, loading, refresh } = useTable(db, 'users'); // 管理数据库生命周期 function App() { const { db, ready, error } = useDatabase({ name: 'my-app', mode: 'hybrid', }); if (!ready) return <div>Initializing...</div>; return <UserList />; }
🟢 Vue 集成
import { useSqlarkQuery, useSqlarkTable, useSqlarkDatabase } from '@metona-team/metona-sqlark/vue'; import { db } from './db'; // useSqlarkQuery — 执行 SQL 查询 const { data, loading, error, refresh } = useSqlarkQuery( db, 'SELECT * FROM users WHERE age > 18' ); // useSqlarkTable — 获取全表数据 const { data, loading, refresh } = useSqlarkTable(db, 'users'); // useSqlarkDatabase — 管理数据库生命周期 const { db, ready, error } = useSqlarkDatabase({ name: 'my-app', mode: 'hybrid', });
🔷 TypeScript 泛型
interface User { id: string; name: string; age: number; email?: string; } // 泛型表操作 — 类型安全的 insert/select const users = db.table<User>('users'); // ✅ 类型检查通过 await users.insert({ id: '1', name: 'Alice', age: 30 }); // ❌ TypeScript 报错:缺少 name // await users.insert({ id: '2', age: 25 });
⚙️ 完整配置项
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
name | string | 'metona-sqlark' | 数据库名称(必填) |
mode | 'memory'|'disk'|'hybrid' | 'hybrid' | 存储模式 |
diskEngine | 'indexeddb'|'opfs' | 'indexeddb' | 磁盘引擎类型 |
version | number | 1 | 数据库版本号 |
plugins | MetonaPlugin[] | [] | 初始插件列表 |
onReady | (db) => void | - | 初始化完成回调 |
onError | (err) => void | - | 错误回调 |
💾 存储引擎
| 引擎 | 模式 | 持久化 | 性能 | 适用场景 |
|---|---|---|---|---|
MemoryEngine | memory | ❌ 否 | ⚡ 极快 | 临时数据、缓存、测试 |
IndexedDBEngine | disk | ✅ 是 | 🚀 快 | 通用持久化,兼容性最好 |
OPFSEngine | disk | ✅ 是 | 🚀 快 | 现代浏览器,文件级存储 |
HybridEngine | hybrid | ✅ 是 | ⚡ 极快 | 生产推荐,读写均走内存 |
Hybrid 引擎采用 write-through 策略:所有写操作同时写入内存和磁盘,所有读操作直接从内存返回,启动时从磁盘加载数据到内存。
⚠️ 错误处理
所有错误抛出 DatabaseError 实例。
try { await db.query('SELECT * FROM nonexistent'); } catch (err) { if (err instanceof DatabaseError) { console.log(err.message); // 'Table "nonexistent" does not exist' console.log(err.code); // 'TABLE_NOT_FOUND' console.log(err.details); // 附加信息 } }
| 错误码 | 触发场景 |
|---|---|
TABLE_NOT_FOUND | 表不存在 |
TABLE_EXISTS | 表已存在 |
DUPLICATE_KEY | 主键重复 |
UNIQUE_VIOLATION | 唯一约束冲突 |
VALIDATION_ERROR | 数据校验失败 |
TYPE_ERROR | 字段类型错误 |
SCHEMA_ERROR | 表结构定义错误 |
DB_NOT_READY | 数据库未初始化 |
PARSE_ERROR | SQL 语法错误 |
TRANSACTION_ERROR | 事务执行失败 |
COMPILE_ERROR | 编译 AST 到查询计划失败 |
CONFIG_ERROR | 配置错误 |