📦 安装
-// npm -npm install @metona-team/MetonaSqlark ++ +MetonaSqlark 支持多种引入方式,覆盖 npm、CDN、ESM、CJS 所有常见场景。
-// ESM -import { MetonaSqlark, MeSqlark } from 'MetonaSqlark'; +npm 安装(推荐)
+# 配置 Gitea Registry(一次性) +npm config set @metona-team:registry https://git.metona.cn/api/packages/MetonaTeam/npm/ -// Browser -<script src="metona-sqlark.min.js"></script> -// → window.MetonaSqlark / window.MeSqlark+# 安装 +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) 工厂函数,返回初始化好的实例。
MetonaSqlark.create(config) — 工厂函数,自动创建并初始化数据库实例。
const db = await MetonaSqlark.create({ name: 'my-app', mode: 'hybrid', // 'memory' | 'disk' | 'hybrid' - diskEngine: 'indexeddb', // 'indexeddb' | 'opfs' + diskEngine: 'indexeddb', // 'indexeddb' | 'opfs'(仅 disk/hybrid 生效) version: 1, -});+ plugins: [], // MetonaPlugin[] + onReady: (db) => {}, // 就绪回调 + onError: (err) => {}, // 错误回调 +}); -
| 属性 | 类型 | 默认 | 说明 |
|---|---|---|---|
name | string | - | 数据库名称 |
mode | 'memory'|'disk'|'hybrid' | 'hybrid' | 存储模式 |
diskEngine | 'indexeddb'|'opfs' | 'indexeddb' | 磁盘引擎 |
version | number | 1 | 版本号 |
📋 定义表
+使用 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 }, - dept_id: { type: 'number', references: 'departments.id' }, -});+ 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'); // 删除表
| 字段 | 类型 | 说明 |
|---|---|---|
type | string|number|boolean|date|json | 数据类型 |
primaryKey | boolean | 主键 |
required | boolean | 必填 |
unique | boolean | 唯一约束 |
index | boolean | 创建索引 |
| ColumnDef 属性 | 类型 | 说明 |
type | 'string'|'number'|'boolean'|'date'|'json' | 数据类型 |
primaryKey | boolean | 主键(每表至少一个) |
required | boolean | 是否必填 |
unique | boolean | 唯一约束(自动建索引) |
index | boolean | 创建哈希索引,O(1) 加速查询 |
default | unknown | 默认值 |
references | string | 外键引用 |
maxLength | number | 字符串最大长度 |
min/max | number | 数值范围 |
references | string | 外键引用 'table.column' |
🔍 SQL 查询
-// INSERT ++ ++ +
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')"); -// SELECT -const rows = await db.query('SELECT * FROM users WHERE age > 18 ORDER BY name LIMIT 10'); +// UPDATE — 更新数据 +await db.query("UPDATE users SET age = 31, active = true WHERE id = '1'"); -// UPDATE -await db.query("UPDATE users SET age = 31 WHERE id = '1'"); - -// DELETE +// DELETE — 删除数据 await db.query("DELETE FROM users WHERE id = '1'"); -// DDL -await db.query('DROP TABLE users');+// 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.insert({ id: '1', name: 'Alice' }); -await users.insertMany([...]); +// 全量查询 +await users.select().execute(); -// 查询 +// 指定列 + 条件 + 排序 + 分页 const result = await users - .select(['name', 'age']) - .where({ age: { $gt: 18 }, name: { $like: 'A%' } }) + .select(['name', 'age', 'email']) + .where({ + age: { $gt: 18 }, + name: { $like: 'A%' }, + }) .orderBy('age', 'desc') - .limit(10).offset(0) - .execute(); + .limit(10) + .offset(0) + .execute();-// 更新/删除 -await users.update({ age: 31 }).where({ id: '1' }).execute(); -await users.delete().where({ id: '1' }).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 查询
-// SQL -await db.query(`SELECT u.name, d.name ++// CROSS JOIN — 笛卡尔积 +.crossJoin('metadata', 'm')支持 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 - WHERE d.name = 'Engineering'`); + LEFT JOIN orders o ON u.id = o.user_id + WHERE d.name = 'Engineering' + ORDER BY u.name`); -// QueryBuilder +// Query Builder 方式 await db.table('users').select() - .innerJoin('departments', { 'users.dept_id': { $col: 'departments.id' } }) + .as('u') + .innerJoin('departments', { 'u.dept_id': { $col: 'd.id' } }, 'd') + .leftJoin('orders', { 'u.id': { $col: 'o.user_id' } }, 'o') .execute(); -// LEFT JOIN / RIGHT JOIN / CROSS JOIN -.leftJoin('table', on) -.rightJoin('table', on) -.crossJoin('table')
📊 GROUP BY & 聚合
-await db.query(`SELECT dept, COUNT(*) as cnt, SUM(salary) as total ++// DISTINCT 去重 +await db.query('SELECT DISTINCT dept FROM employees'); + +// 聚合函数别名 +await db.query('SELECT COUNT(*) AS total_users, AVG(age) AS avg_age FROM users'); + +五大聚合函数 + 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`); + ORDER BY total DESC + LIMIT 5`); -// 支持的聚合函数:COUNT, SUM, AVG, MIN, MAX -// 支持别名:COUNT(*) AS cnt
🎯 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', { ... }); +按版本号管理表结构变更。
+ +// 注册迁移 +db.addMigration(2, async (db) => { + await db.defineTable('products', { + id: { type: 'string', primaryKey: true }, + name: { type: 'string', required: true }, + }); }); -await db.migrateTo(2); // 执行所有未执行的迁移+ +db.addMigration(3, async (db) => { + // 添加新列、数据迁移等 + await db.query("UPDATE users SET role = 'user' WHERE role IS NULL"); +}); + +// 执行迁移到目标版本 +await db.migrateTo(3); // 依次执行 v2, v3 的迁移函数
📤 导入导出
-// 导出单表 -const data = await db.exportTable('users'); +-// 导出单表 — 返回 JSON 数组 +const userData = await db.exportTable('users'); +// [{ id: '1', name: 'Alice', ... }, ...] -// 导出全库 -const all = await db.exportAll(); +// 导出全库 — 返回 { tableName: rows[] } +const allData = await db.exportAll(); +// { users: [...], orders: [...], products: [...] } -// 导入 -await db.importTable('users', data);+// 导入数据 — 返回主键列表 +const pks = await db.importTable('users', userData);
🧩 插件钩子
-// 14 个生命周期钩子 +🧩 插件 & 钩子
+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); + 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], });-
| 钩子 | 触发时机 |
|---|---|
beforeCreateTable / afterCreateTable | 创建表前后 |
beforeDropTable / afterDropTable | 删除表前后 |
beforeInsert / afterInsert | 插入前后 |
beforeUpdate / afterUpdate | 更新前后 |
beforeDelete / afterDelete | 删除前后 |
beforeQuery / afterQuery | 查询前后 |
beforeTransaction / afterTransaction | 事务前后 |
📡 发布订阅
// 订阅表变更 const unsubscribe = db.subscribe('users', (event) => { - // event: { type: 'insert'|'update'|'delete', row: {...} } + // 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 }); // ✅ 类型安全-
⚙️ 配置项
-import { MetonaSqlark, MeSqlark } from 'MetonaSqlark'; -// MeSqlark 是 MetonaSqlark 的别名,完全等价+// ✅ 类型检查通过 +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 | 配置错误 |