Files
MetonaSqlark/site/docs.html
T
thzxx b21cbfd544
CI / test (18.x) (push) Successful in 9m56s
CI / test (20.x) (push) Successful in 9m53s
CI / test (22.x) (push) Successful in 9m51s
CI / test (24.x) (push) Successful in 9m47s
test: v0.1.14 硬固化测试(幂等init/flush原子性/versionchange)+ site版本号修复
2026-07-26 17:33:42 +08:00

671 lines
39 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API 文档 — MetonaSqlark v0.1.14</title>
<style>
:root {
--bg:#0a0a0f; --surface:#14141f; --surface2:#1a1a2e; --border:#2a2a45;
--primary:#6366f1; --primary-glow:#818cf8; --accent:#06b6d4; --accent2:#ec4899;
--text:#e2e8f0; --text2:#94a3b8; --gradient:linear-gradient(135deg,#6366f1 0%,#06b6d4 50%,#ec4899 100%);
--radius:14px;
}
* { margin:0; padding:0; box-sizing:border-box; }
body { font-family:'Inter',-apple-system,BlinkMacSystemFont,system-ui,sans-serif; background:var(--bg); color:var(--text); line-height:1.7; }
header {
position:fixed; top:0; left:0; right:0; z-index:100; backdrop-filter:blur(20px);
background:rgba(10,10,15,0.85); border-bottom:1px solid var(--border); padding:16px 32px;
}
header .inner { max-width:1400px; margin:0 auto; display:flex; align-items:center; justify-content:space-between; }
.logo { font-size:1.3rem; font-weight:800; display:flex; align-items:center; gap:10px; }
.logo .icon { width:32px; height:32px; border-radius:8px; background:var(--gradient); display:flex; align-items:center; justify-content:center; font-size:1rem; font-weight:900; color:#fff; }
.logo span { background:var(--gradient); -webkit-background-clip:text; -webkit-text-fill-color:transparent; }
nav { display:flex; gap:28px; }
nav a { color:var(--text2); text-decoration:none; font-weight:500; font-size:0.92rem; transition:.2s; }
nav a:hover,.nav-active { color:var(--text); }
.layout { display:flex; max-width:1400px; margin:0 auto; padding-top:80px; min-height:100vh; }
.sidebar {
width:260px; min-width:260px; padding:32px 24px; position:sticky; top:80px; height:calc(100vh - 80px);
overflow-y:auto; border-right:1px solid var(--border); background:var(--bg);
}
.sidebar h4 { font-size:0.75rem; text-transform:uppercase; letter-spacing:1px; color:var(--text2); margin:20px 0 8px; }
.sidebar h4:first-child { margin-top:0; }
.sidebar a { display:block; padding:7px 12px; border-radius:6px; color:var(--text2); text-decoration:none; font-size:0.9rem; transition:.15s; }
.sidebar a:hover,.sidebar a.active { color:var(--text); background:var(--surface2); }
.sidebar a.active { border-left:3px solid var(--primary); }
.content { flex:1; padding:32px 48px 80px; max-width:950px; }
.content h2 { font-size:1.8rem; font-weight:800; margin:48px 0 16px; padding-top:24px; border-top:1px solid var(--border); }
.content h2:first-of-type { margin-top:0; padding-top:0; border-top:none; }
.content h3 { font-size:1.25rem; font-weight:700; margin:32px 0 12px; color:var(--primary-glow); }
.content p { color:var(--text2); margin-bottom:16px; }
.content code {
background:var(--surface2); padding:2px 8px; border-radius:5px; font-family:'JetBrains Mono',monospace;
font-size:0.88rem; color:var(--accent);
}
.content pre {
background:var(--surface2); border:1px solid var(--border); border-radius:var(--radius);
padding:20px 24px; overflow-x:auto; font-family:'JetBrains Mono',monospace; font-size:0.85rem;
line-height:1.65; margin:16px 0; color:#e2e8f0;
}
.content pre .k { color:#c084fc; } .content pre .s { color:#34d399; }
.content pre .f { color:#60a5fa; } .content pre .c { color:#64748b; }
.content pre .n { color:#fbbf24; } .content pre .t { color:#f472b6; }
.content table { width:100%; border-collapse:collapse; margin:16px 0; }
.content th,.content td { text-align:left; padding:12px 16px; border-bottom:1px solid var(--border); font-size:0.9rem; }
.content th { color:var(--text); font-weight:600; background:var(--surface); }
.content td { color:var(--text2); }
.content td code { font-size:0.82rem; }
::-webkit-scrollbar { width:6px; } ::-webkit-scrollbar-track { background:transparent; } ::-webkit-scrollbar-thumb { background:var(--border); border-radius:3px; }
</style>
</head>
<body>
<header>
<div class="inner">
<div class="logo"><div class="icon"></div><span>MetonaSqlark</span></div>
<nav>
<a href="index.html">首页</a>
<a href="docs.html" class="nav-active">文档</a>
<a href="demo.html">演示</a>
</nav>
</div>
</header>
<div class="layout">
<aside class="sidebar">
<h4>快速开始</h4>
<a href="#install" class="active">安装</a>
<a href="#create">创建数据库</a>
<a href="#define-table">定义表</a>
<h4>查询 API</h4>
<a href="#sql-query">SQL 查询</a>
<a href="#query-builder">Query Builder</a>
<a href="#join">JOIN 查询</a>
<a href="#groupby">GROUP BY & 聚合</a>
<a href="#where-ops">WHERE 操作符</a>
<h4>高级特性</h4>
<a href="#transaction">事务 & 回滚</a>
<a href="#subquery">子查询</a>
<a href="#foreign-key">外键级联</a>
<a href="#connection-pool">连接池</a>
<a href="#migration">数据迁移</a>
<a href="#export">导入导出</a>
<a href="#plugin">插件 & 钩子</a>
<a href="#subscribe">发布订阅</a>
<h4>框架集成</h4>
<a href="#react">React 集成</a>
<a href="#vue">Vue 集成</a>
<h4>类型参考</h4>
<a href="#types">TypeScript 类型</a>
<a href="#config">完整配置项</a>
<a href="#engine">存储引擎</a>
<a href="#errors">错误处理</a>
</aside>
<main class="content">
<h2 id="install">📦 安装</h2>
<p>MetonaSqlark 支持多种引入方式,覆盖 npm、CDN、ESM、CJS 所有常见场景。</p>
<h3>npm 安装(推荐)</h3>
<pre><span class="c"># 配置 Gitea Registry(一次性)</span>
npm config set @metona-team:registry https://git.metona.cn/api/packages/MetonaTeam/npm/
<span class="c"># 安装</span>
npm install @metona-team/metona-sqlark</pre>
<h3>CDN / UMD</h3>
<pre><span class="c">&lt;!-- UMD 格式,暴露 window.MetonaSqlark 和 window.MeSqlark --&gt;</span>
&lt;script src=<span class="s">"https://git.metona.cn/.../metona-sqlark.min.js"</span>&gt;&lt;/script&gt;
&lt;script&gt;
<span class="k">const</span> db = <span class="k">await</span> window.<span class="f">MetonaSqlark</span>.create({...});
<span class="c">// 或 window.MeSqlark.create(...) — 完全等价</span>
&lt;/script&gt;</pre>
<h3>ESM</h3>
<pre><span class="k">import</span> { MetonaSqlark, MeSqlark } <span class="k">from</span> <span class="s">'@metona-team/metona-sqlark'</span>;
<span class="c">// MeSqlark 是 MetonaSqlark 的别名,行为完全一致</span></pre>
<h3>CJS</h3>
<pre><span class="k">const</span> { MetonaSqlark } = <span class="f">require</span>(<span class="s">'@metona-team/metona-sqlark'</span>);</pre>
<p>输出文件说明:</p>
<table>
<tr><th>文件</th><th>格式</th><th>用途</th></tr>
<tr><td><code>metona-sqlark.js</code></td><td>UMD</td><td>浏览器开发版(含 sourcemap</td></tr>
<tr><td><code>metona-sqlark.min.js</code></td><td>UMD (minified)</td><td>生产环境(~42KB / ~10KB gzip</td></tr>
<tr><td><code>metona-sqlark.esm.js</code></td><td>ES Module</td><td>现代打包工具 / 浏览器 ESM</td></tr>
<tr><td><code>metona-sqlark.cjs.js</code></td><td>CommonJS</td><td>Node.js require()</td></tr>
<tr><td><code>metona-sqlark.d.ts</code></td><td>TypeScript 声明</td><td>类型提示</td></tr>
</table>
<h2 id="create">🏗 创建数据库</h2>
<p><code>MetonaSqlark.create(config)</code> — 工厂函数,自动创建并初始化数据库实例。</p>
<pre><span class="k">const</span> db = <span class="k">await</span> <span class="f">MetonaSqlark.create</span>({
<span class="s">name</span>: <span class="s">'my-app'</span>,
<span class="s">mode</span>: <span class="s">'hybrid'</span>, <span class="c">// 'memory' | 'disk' | 'hybrid'</span>
<span class="s">diskEngine</span>: <span class="s">'indexeddb'</span>, <span class="c">// 'indexeddb' | 'opfs'(仅 disk/hybrid 生效)</span>
<span class="s">version</span>: <span class="n">1</span>,
<span class="s">plugins</span>: [], <span class="c">// MetonaPlugin[]</span>
<span class="s">onReady</span>: (db) => {}, <span class="c">// 就绪回调</span>
<span class="s">onError</span>: (err) => {}, <span class="c">// 错误回调</span>
});
<span class="c">// 也可手动实例化</span>
<span class="k">const</span> db2 = <span class="k">new</span> <span class="f">MetonaSqlark</span>(config);
<span class="k">await</span> db2.<span class="f">init</span>();
<span class="c">// 检查状态</span>
db.<span class="f">isReady</span>(); <span class="c">// true</span>
<span class="c">// 关闭</span>
<span class="k">await</span> db.<span class="f">close</span>();</pre>
<h2 id="define-table">📋 定义表</h2>
<p>使用 <code>db.defineTable(name, columns)</code> 定义表结构。</p>
<pre><span class="k">await</span> db.<span class="f">defineTable</span>(<span class="s">'users'</span>, {
<span class="s">id</span>: { <span class="s">type</span>: <span class="s">'string'</span>, <span class="s">primaryKey</span>: <span class="k">true</span> },
<span class="s">name</span>: { <span class="s">type</span>: <span class="s">'string'</span>, <span class="s">required</span>: <span class="k">true</span> },
<span class="s">email</span>: { <span class="s">type</span>: <span class="s">'string'</span>, <span class="s">unique</span>: <span class="k">true</span>, <span class="s">index</span>: <span class="k">true</span> },
<span class="s">age</span>: { <span class="s">type</span>: <span class="s">'number'</span>, <span class="s">default</span>: <span class="n">0</span>, <span class="s">min</span>: <span class="n">0</span>, <span class="s">max</span>: <span class="n">150</span> },
<span class="s">active</span>: { <span class="s">type</span>: <span class="s">'boolean'</span>, <span class="s">default</span>: <span class="k">true</span> },
<span class="s">birthday</span>: { <span class="s">type</span>: <span class="s">'date'</span> },
<span class="s">meta</span>: { <span class="s">type</span>: <span class="s">'json'</span> },
<span class="s">dept_id</span>: { <span class="s">type</span>: <span class="s">'number'</span>, <span class="s">references</span>: <span class="s">'departments.id'</span> },
});
<span class="c">// 表操作</span>
<span class="k">await</span> db.<span class="f">getTableNames</span>(); <span class="c">// ['users']</span>
<span class="k">await</span> db.<span class="f">dropTable</span>(<span class="s">'users'</span>); <span class="c">// 删除表</span></pre>
<table>
<tr><th>ColumnDef 属性</th><th>类型</th><th>说明</th></tr>
<tr><td><code>type</code></td><td><code>'string'|'number'|'boolean'|'date'|'json'</code></td><td>数据类型</td></tr>
<tr><td><code>primaryKey</code></td><td><code>boolean</code></td><td>主键(每表至少一个)</td></tr>
<tr><td><code>required</code></td><td><code>boolean</code></td><td>是否必填</td></tr>
<tr><td><code>unique</code></td><td><code>boolean</code></td><td>唯一约束(自动建索引)</td></tr>
<tr><td><code>index</code></td><td><code>boolean</code></td><td>创建哈希索引,O(1) 加速查询</td></tr>
<tr><td><code>default</code></td><td><code>unknown</code></td><td>默认值</td></tr>
<tr><td><code>maxLength</code></td><td><code>number</code></td><td>字符串最大长度</td></tr>
<tr><td><code>min</code>/<code>max</code></td><td><code>number</code></td><td>数值范围</td></tr>
<tr><td><code>references</code></td><td><code>string</code></td><td>外键引用 <code>'table.column'</code></td></tr>
<tr><td><code>onDelete</code></td><td><code>'CASCADE'\|'SET NULL'\|'RESTRICT'</code></td><td>删除级联 🆕</td></tr>
<tr><td><code>onUpdate</code></td><td><code>'CASCADE'\|'SET NULL'\|'RESTRICT'</code></td><td>更新级联 🆕</td></tr>
</table>
<h2 id="sql-query">🔍 SQL 查询</h2>
<p><code>db.query(sql)</code> — 执行标准 SQL 字符串,返回查询结果。</p>
<h3>完整 SQL 语法支持</h3>
<pre><span class="c">// SELECT — 核心查询</span>
<span class="k">const</span> rows = <span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT * FROM users
WHERE age > 18
ORDER BY name ASC
LIMIT 10 OFFSET 0`</span>);
<span class="c">// INSERT — 插入数据</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"INSERT INTO users (id, name, age) VALUES ('1', 'Alice', 30)"</span>);
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"INSERT INTO users VALUES ('2', 'Bob', 25)"</span>);
<span class="c">// 支持多行插入</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"INSERT INTO users VALUES ('3', 'C'), ('4', 'D')"</span>);
<span class="c">// UPDATE — 更新数据</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"UPDATE users SET age = 31, active = true WHERE id = '1'"</span>);
<span class="c">// DELETE — 删除数据</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"DELETE FROM users WHERE id = '1'"</span>);
<span class="c">// DDL — 表结构操作</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`CREATE TABLE products (
id STRING PRIMARY KEY,
name STRING NOT NULL,
price NUMBER DEFAULT 0
)`</span>);
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">'DROP TABLE products'</span>);</pre>
<h3>条件表达式</h3>
<pre><span class="c">// 比较运算符</span>
<span class="s">`WHERE age > 18 AND name LIKE 'A%'`</span>
<span class="s">`WHERE salary >= 5000 OR dept = 'Engineering'`</span>
<span class="c">// IN / NOT IN</span>
<span class="s">`WHERE dept IN ('Engineering', 'Sales')`</span>
<span class="c">// NULL 检查</span>
<span class="s">`WHERE email IS NULL`</span>
<span class="s">`WHERE email IS NOT NULL`</span>
<span class="c">// NOT 取反</span>
<span class="s">`WHERE NOT (age < 18 OR age > 65)`</span>
<span class="c">// 嵌套条件</span>
<span class="s">`WHERE (age > 18 AND active = true) OR role = 'admin'`</span></pre>
<h2 id="query-builder">⛓ Query Builder</h2>
<p>链式 APITypeScript 友好,享受 IDE 自动补全。</p>
<h3>SELECT</h3>
<pre><span class="k">const</span> users = db.<span class="f">table</span>(<span class="s">'users'</span>);
<span class="c">// 全量查询</span>
<span class="k">await</span> users.<span class="f">select</span>().<span class="f">execute</span>();
<span class="c">// 指定列 + 条件 + 排序 + 分页</span>
<span class="k">const</span> result = <span class="k">await</span> users
.<span class="f">select</span>([<span class="s">'name'</span>, <span class="s">'age'</span>, <span class="s">'email'</span>])
.<span class="f">where</span>({
<span class="s">age</span>: { <span class="s">$gt</span>: <span class="n">18</span> },
<span class="s">name</span>: { <span class="s">$like</span>: <span class="s">'A%'</span> },
})
.<span class="f">orderBy</span>(<span class="s">'age'</span>, <span class="s">'desc'</span>)
.<span class="f">limit</span>(<span class="n">10</span>)
.<span class="f">offset</span>(<span class="n">0</span>)
.<span class="f">execute</span>();</pre>
<h3>INSERT</h3>
<pre><span class="c">// 单行插入 — 返回主键值</span>
<span class="k">const</span> pk = <span class="k">await</span> users.<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'1'</span>, <span class="s">name</span>: <span class="s">'Alice'</span>, <span class="s">age</span>: <span class="n">30</span> });
<span class="c">// 批量插入 — 返回主键值数组</span>
<span class="k">const</span> pks = <span class="k">await</span> users.<span class="f">insertMany</span>([
{ <span class="s">id</span>: <span class="s">'2'</span>, <span class="s">name</span>: <span class="s">'Bob'</span>, <span class="s">age</span>: <span class="n">25</span> },
{ <span class="s">id</span>: <span class="s">'3'</span>, <span class="s">name</span>: <span class="s">'Charlie'</span>, <span class="s">age</span>: <span class="n">35</span> },
]);</pre>
<h3>UPDATE / DELETE</h3>
<pre><span class="c">// 更新 — 返回影响行数</span>
<span class="k">const</span> updated = <span class="k">await</span> users
.<span class="f">update</span>({ <span class="s">age</span>: <span class="n">31</span>, <span class="s">active</span>: <span class="k">false</span> })
.<span class="f">where</span>({ <span class="s">id</span>: <span class="s">'1'</span> })
.<span class="f">execute</span>(); <span class="c">// 1</span>
<span class="c">// 删除 — 返回影响行数</span>
<span class="k">const</span> deleted = <span class="k">await</span> users
.<span class="f">delete</span>()
.<span class="f">where</span>({ <span class="s">id</span>: <span class="s">'1'</span> })
.<span class="f">execute</span>(); <span class="c">// 1</span>
<span class="c">// 全表删除</span>
<span class="k">await</span> users.<span class="f">delete</span>().<span class="f">execute</span>();</pre>
<h2 id="join">🔗 JOIN 查询</h2>
<p>支持 INNER / LEFT / RIGHT / CROSS JOINSQL 和 Query Builder 两种方式。</p>
<pre><span class="c">// SQL 方式 — 支持表别名</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`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`</span>);
<span class="c">// Query Builder 方式</span>
<span class="k">await</span> db.<span class="f">table</span>(<span class="s">'users'</span>).<span class="f">select</span>()
.<span class="f">as</span>(<span class="s">'u'</span>)
.<span class="f">innerJoin</span>(<span class="s">'departments'</span>, { <span class="s">'u.dept_id'</span>: { <span class="s">$col</span>: <span class="s">'d.id'</span> } }, <span class="s">'d'</span>)
.<span class="f">leftJoin</span>(<span class="s">'orders'</span>, { <span class="s">'u.id'</span>: { <span class="s">$col</span>: <span class="s">'o.user_id'</span> } }, <span class="s">'o'</span>)
.<span class="f">execute</span>();
<span class="c">// CROSS JOIN — 笛卡尔积</span>
.<span class="f">crossJoin</span>(<span class="s">'metadata'</span>, <span class="s">'m'</span>)</pre>
<h2 id="groupby">📊 GROUP BY & 聚合</h2>
<p>五大聚合函数 + GROUP BY + HAVING + DISTINCT,完整的数据分析能力。</p>
<pre><span class="c">// GROUP BY + 聚合</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`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`</span>);
<span class="c">// DISTINCT 去重</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">'SELECT DISTINCT dept FROM employees'</span>);
<span class="c">// 聚合函数别名</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">'SELECT COUNT(*) AS total_users, AVG(age) AS avg_age FROM users'</span>);</pre>
<h2 id="where-ops">🎯 WHERE 操作符</h2>
<p>Query Builder 使用 <code>$</code> 前缀操作符,支持逻辑组合。</p>
<table>
<tr><th>操作符</th><th>含义</th><th>示例</th></tr>
<tr><td>直接值 / <code>$eq</code></td><td>等于</td><td><code>{ age: 30 }</code> / <code>{ age: { $eq: 30 } }</code></td></tr>
<tr><td><code>$ne</code></td><td>不等于</td><td><code>{ age: { $ne: 30 } }</code></td></tr>
<tr><td><code>$gt</code> / <code>$gte</code></td><td>大于 / 大于等于</td><td><code>{ age: { $gt: 18 } }</code></td></tr>
<tr><td><code>$lt</code> / <code>$lte</code></td><td>小于 / 小于等于</td><td><code>{ age: { $lt: 65 } }</code></td></tr>
<tr><td><code>$in</code> / <code>$nin</code></td><td>在列表中 / 不在</td><td><code>{ dept: { $in: ['IT','HR'] } }</code></td></tr>
<tr><td><code>$like</code></td><td>模糊匹配</td><td><code>{ name: { $like: 'A%' } }</code></td></tr>
<tr><td><code>$and</code></td><td>逻辑与</td><td><code>{ $and: [{...}, {...}] }</code></td></tr>
<tr><td><code>$or</code></td><td>逻辑或</td><td><code>{ $or: [{...}, {...}] }</code></td></tr>
<tr><td><code>$not</code></td><td>逻辑非</td><td><code>{ age: { $not: { $gt: 18 } } }</code></td></tr>
<tr><td><code>$col</code></td><td>列引用(JOIN ON</td><td><code>{ 'a.id': { $col: 'b.a_id' } }</code></td></tr>
</table>
<pre><span class="c">// 复合条件</span>
.<span class="f">where</span>({
<span class="s">$or</span>: [
{ <span class="s">age</span>: { <span class="s">$lt</span>: <span class="n">18</span> } },
{ <span class="s">age</span>: { <span class="s">$gt</span>: <span class="n">65</span> } },
],
<span class="s">active</span>: <span class="k">true</span>,
<span class="s">name</span>: { <span class="s">$like</span>: <span class="s">'A%'</span>, <span class="s">$ne</span>: <span class="s">'Admin'</span> },
})</pre>
<h2 id="transaction">🔒 事务 & 回滚</h2>
<p>v0.1.13 起支持真正的自动回滚:事务内任何一步失败,所有变更自动撤销。</p>
<pre><span class="k">await</span> db.<span class="f">transaction</span>(<span class="k">async</span> (trx) => {
<span class="c">// trx.table() 获取事务内表操作对象</span>
<span class="k">await</span> trx.<span class="f">table</span>(<span class="s">'users'</span>).<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'3'</span>, <span class="s">name</span>: <span class="s">'Charlie'</span> });
<span class="k">await</span> trx.<span class="f">table</span>(<span class="s">'orders'</span>).<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'o1'</span>, <span class="s">userId</span>: <span class="s">'3'</span>, <span class="s">amount</span>: <span class="n">99</span> });
<span class="c">// ✅ 全部成功 → 自动 commit</span>
<span class="c">// ❌ 任何一步失败 → 自动 rollback,数据恢复原状</span>
<span class="c">// Memory 引擎:快照回滚 | IndexedDB 引擎:延迟写入</span>
});</pre>
<h2 id="subquery">🔍 子查询</h2>
<p>v0.1.13 新增子查询支持,可在 WHERE 条件中嵌套 SELECT。</p>
<pre><span class="c">// IN 子查询 — 查询有高额订单的用户</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE amount > 100)`</span>);
<span class="c">// 标量子查询 — 查询年龄等于平均年龄的用户</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT * FROM users
WHERE age = (SELECT AVG(age) FROM users)`</span>);
<span class="c">// NOT IN 子查询</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT * FROM users
WHERE id NOT IN (SELECT user_id FROM orders)`</span>);</pre>
<h2 id="foreign-key">🔗 外键级联</h2>
<p>v0.1.13 支持外键级联操作,定义表时可指定 ON DELETE / ON UPDATE 行为。</p>
<pre><span class="c">// 定义时指定外键 + 级联策略</span>
<span class="k">await</span> db.<span class="f">defineTable</span>(<span class="s">'orders'</span>, {
<span class="s">id</span>: { <span class="s">type</span>: <span class="s">'string'</span>, <span class="s">primaryKey</span>: <span class="k">true</span> },
<span class="s">user_id</span>: {
<span class="s">type</span>: <span class="s">'string'</span>,
<span class="s">references</span>: <span class="s">'users.id'</span>,
<span class="s">onDelete</span>: <span class="s">'CASCADE'</span>, <span class="c">// 删除用户时级联删除订单</span>
<span class="s">onUpdate</span>: <span class="s">'RESTRICT'</span>, <span class="c">// 禁止更新被引用的用户 ID</span>
},
<span class="s">amount</span>: { <span class="s">type</span>: <span class="s">'number'</span> },
});
<span class="c">// SQL DDL 同样支持</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`CREATE TABLE orders (
id STRING PRIMARY KEY,
user_id STRING REFERENCES users(id) ON DELETE CASCADE ON UPDATE RESTRICT,
amount NUMBER
)`</span>);
<span class="c">// 删除用户 → 其所有订单自动删除</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"DELETE FROM users WHERE id = '1'"</span>);</pre>
<table>
<tr><th>级联选项</th><th>行为</th></tr>
<tr><td><code>CASCADE</code></td><td>级联删除/更新子表中的匹配行</td></tr>
<tr><td><code>SET NULL</code></td><td>将子表中的外键列设为 NULL</td></tr>
<tr><td><code>RESTRICT</code></td><td>禁止操作(默认行为)</td></tr>
</table>
<h2 id="connection-pool">🏊 连接池</h2>
<p>v0.1.13 新增连接池管理器,避免重复创建同名数据库实例。</p>
<pre><span class="c">// connect() — 获取或创建实例(单例复用)</span>
<span class="k">const</span> db1 = <span class="k">await</span> <span class="f">MetonaSqlark.connect</span>({ <span class="s">name</span>: <span class="s">'my-app'</span>, <span class="s">mode</span>: <span class="s">'hybrid'</span> });
<span class="k">const</span> db2 = <span class="k">await</span> <span class="f">MetonaSqlark.connect</span>({ <span class="s">name</span>: <span class="s">'my-app'</span> });
<span class="c">// db1 === db2 — 复用已有实例,避免重复 open IndexedDB</span>
<span class="c">// disconnect() — 释放连接(引用计数 -1)</span>
<span class="k">await</span> db2.<span class="f">disconnect</span>(); <span class="c">// 引用计数: 2 → 1</span>
<span class="k">await</span> db1.<span class="f">disconnect</span>(); <span class="c">// 引用计数: 1 → 0,自动 close()</span>
<span class="c">// disconnectAll() — 强制关闭所有连接</span>
<span class="k">await</span> <span class="f">MetonaSqlark.disconnectAll</span>();
<span class="c">// getActiveConnections() — 查看活跃连接</span>
<span class="f">MetonaSqlark.getActiveConnections</span>(); <span class="c">// ['my-app']</span></pre>
<h2 id="migration">🔄 数据迁移</h2>
<p>按版本号管理表结构变更。</p>
<pre><span class="c">// 注册迁移</span>
db.<span class="f">addMigration</span>(<span class="n">2</span>, <span class="k">async</span> (db) => {
<span class="k">await</span> db.<span class="f">defineTable</span>(<span class="s">'products'</span>, {
<span class="s">id</span>: { <span class="s">type</span>: <span class="s">'string'</span>, <span class="s">primaryKey</span>: <span class="k">true</span> },
<span class="s">name</span>: { <span class="s">type</span>: <span class="s">'string'</span>, <span class="s">required</span>: <span class="k">true</span> },
});
});
db.<span class="f">addMigration</span>(<span class="n">3</span>, <span class="k">async</span> (db) => {
<span class="c">// 添加新列、数据迁移等</span>
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"UPDATE users SET role = 'user' WHERE role IS NULL"</span>);
});
<span class="c">// 执行迁移到目标版本</span>
<span class="k">await</span> db.<span class="f">migrateTo</span>(<span class="n">3</span>); <span class="c">// 依次执行 v2, v3 的迁移函数</span></pre>
<h2 id="export">📤 导入导出</h2>
<pre><span class="c">// 导出单表 — 返回 JSON 数组</span>
<span class="k">const</span> userData = <span class="k">await</span> db.<span class="f">exportTable</span>(<span class="s">'users'</span>);
<span class="c">// [{ id: '1', name: 'Alice', ... }, ...]</span>
<span class="c">// 导出全库 — 返回 { tableName: rows[] }</span>
<span class="k">const</span> allData = <span class="k">await</span> db.<span class="f">exportAll</span>();
<span class="c">// { users: [...], orders: [...], products: [...] }</span>
<span class="c">// 导入数据 — 返回主键列表</span>
<span class="k">const</span> pks = <span class="k">await</span> db.<span class="f">importTable</span>(<span class="s">'users'</span>, userData);</pre>
<h2 id="plugin">🧩 插件 & 钩子</h2>
<p>14 种生命周期钩子,支持插件机制。</p>
<table>
<tr><th>钩子名称</th><th>触发时机</th><th>参数</th></tr>
<tr><td><code>beforeCreateTable</code></td><td>创建表前</td><td>schema</td></tr>
<tr><td><code>afterCreateTable</code></td><td>创建表后</td><td>schema</td></tr>
<tr><td><code>beforeDropTable</code></td><td>删除表前</td><td>tableName</td></tr>
<tr><td><code>afterDropTable</code></td><td>删除表后</td><td>tableName</td></tr>
<tr><td><code>beforeInsert</code></td><td>插入前</td><td>rows[]</td></tr>
<tr><td><code>afterInsert</code></td><td>插入后</td><td>rows[]</td></tr>
<tr><td><code>beforeUpdate</code></td><td>更新前</td><td>query, updates</td></tr>
<tr><td><code>afterUpdate</code></td><td>更新后</td><td>query, updates, count</td></tr>
<tr><td><code>beforeDelete</code></td><td>删除前</td><td>query</td></tr>
<tr><td><code>afterDelete</code></td><td>删除后</td><td>query, count</td></tr>
<tr><td><code>beforeQuery</code></td><td>SQL 查询前</td><td>sql</td></tr>
<tr><td><code>afterQuery</code></td><td>SQL 查询后</td><td>sql, result</td></tr>
<tr><td><code>beforeTransaction</code></td><td>事务开始前</td><td>-</td></tr>
<tr><td><code>afterTransaction</code></td><td>事务完成后</td><td>-</td></tr>
</table>
<pre><span class="c">// 注册钩子</span>
db.<span class="f">on</span>(<span class="s">'beforeInsert'</span>, <span class="k">async</span> (row) => {
<span class="f">console.log</span>(<span class="s">'即将插入:'</span>, row);
<span class="c">// 可在此校验、转换数据</span>
});
db.<span class="f">on</span>(<span class="s">'afterQuery'</span>, <span class="k">async</span> (sql, result) => {
<span class="f">console.log</span>(<span class="s">`查询完成 [</span><span class="k">${sql}</span><span class="s">] → </span><span class="k">${(result as any[]).length}</span><span class="s"> 行`</span>);
});
<span class="c">// 注册自定义插件</span>
<span class="k">const</span> loggerPlugin = {
<span class="s">name</span>: <span class="s">'logger'</span>,
<span class="s">version</span>: <span class="s">'1.0.0'</span>,
<span class="s">description</span>: <span class="s">'记录所有数据库操作'</span>,
<span class="s">priority</span>: <span class="n">100</span>,
<span class="f">install</span>(db) {
db.<span class="f">on</span>(<span class="s">'beforeQuery'</span>, (sql) => <span class="f">console.log</span>(<span class="s">'SQL:'</span>, sql));
},
<span class="f">destroy</span>() { <span class="c">/* 清理 */</span> },
};
<span class="c">// 在 create 配置中注册</span>
<span class="k">const</span> db = <span class="k">await</span> <span class="f">MetonaSqlark.create</span>({
<span class="s">name</span>: <span class="s">'my-app'</span>,
<span class="s">plugins</span>: [loggerPlugin],
});</pre>
<h2 id="subscribe">📡 发布订阅</h2>
<pre><span class="c">// 订阅表变更</span>
<span class="k">const</span> unsubscribe = db.<span class="f">subscribe</span>(<span class="s">'users'</span>, (event) => {
<span class="c">// event.type: 'insert' | 'update' | 'delete'</span>
<span class="c">// event.row: 被操作的行数据</span>
<span class="f">console.log</span>(<span class="s">`users 表 </span><span class="k">${event.type}</span><span class="s">`</span>, event.row);
});
<span class="c">// 手动触发变更</span>
db.<span class="f">emit</span>(<span class="s">'users'</span>, { <span class="s">type</span>: <span class="s">'insert'</span>, <span class="s">row</span>: { <span class="s">id</span>: <span class="s">'1'</span>, <span class="s">name</span>: <span class="s">'Alice'</span> } });
<span class="c">// 取消订阅</span>
<span class="f">unsubscribe</span>();</pre>
<h2 id="react">⚛️ React 集成</h2>
<pre><span class="k">import</span> { useQuery, useTable, useDatabase } <span class="k">from</span> <span class="s">'@metona-team/metona-sqlark/react'</span>;
<span class="k">import</span> { db } <span class="k">from</span> <span class="s">'./db'</span>;
<span class="k">function</span> <span class="f">UserList</span>() {
<span class="c">// 执行 SQL 查询,自动响应 db 变化</span>
<span class="k">const</span> { data, loading, error, refresh } = <span class="f">useQuery</span>(
db,
<span class="s">'SELECT * FROM users WHERE age > 18'</span>,
[<span class="c">/* deps */</span>]
);
<span class="k">if</span> (loading) <span class="k">return</span> &lt;div&gt;Loading...&lt;/div&gt;;
<span class="k">if</span> (error) <span class="k">return</span> &lt;div&gt;Error: {error.message}&lt;/div&gt;;
<span class="k">return</span> (
&lt;div&gt;
{data.<span class="f">map</span>(u =&gt; &lt;div key={u.id}&gt;{u.name} ({u.age})&lt;/div&gt;)}
&lt;button onClick={refresh}&gt;刷新&lt;/button&gt;
&lt;/div&gt;
);
}
<span class="c">// 便捷 hook — 查询整张表</span>
<span class="k">const</span> { data, loading, refresh } = <span class="f">useTable</span>(db, <span class="s">'users'</span>);
<span class="c">// 管理数据库生命周期</span>
<span class="k">function</span> <span class="f">App</span>() {
<span class="k">const</span> { db, ready, error } = <span class="f">useDatabase</span>({
<span class="s">name</span>: <span class="s">'my-app'</span>,
<span class="s">mode</span>: <span class="s">'hybrid'</span>,
});
<span class="k">if</span> (!ready) <span class="k">return</span> &lt;div&gt;Initializing...&lt;/div&gt;;
<span class="k">return</span> &lt;UserList /&gt;;
}</pre>
<h2 id="vue">🟢 Vue 集成</h2>
<pre><span class="k">import</span> { useSqlarkQuery, useSqlarkTable, useSqlarkDatabase }
<span class="k">from</span> <span class="s">'@metona-team/metona-sqlark/vue'</span>;
<span class="k">import</span> { db } <span class="k">from</span> <span class="s">'./db'</span>;
<span class="c">// useSqlarkQuery — 执行 SQL 查询</span>
<span class="k">const</span> { data, loading, error, refresh } = <span class="f">useSqlarkQuery</span>(
db,
<span class="s">'SELECT * FROM users WHERE age > 18'</span>
);
<span class="c">// useSqlarkTable — 获取全表数据</span>
<span class="k">const</span> { data, loading, refresh } = <span class="f">useSqlarkTable</span>(db, <span class="s">'users'</span>);
<span class="c">// useSqlarkDatabase — 管理数据库生命周期</span>
<span class="k">const</span> { db, ready, error } = <span class="f">useSqlarkDatabase</span>({
<span class="s">name</span>: <span class="s">'my-app'</span>,
<span class="s">mode</span>: <span class="s">'hybrid'</span>,
});</pre>
<h2 id="types">🔷 TypeScript 泛型</h2>
<pre><span class="k">interface</span> <span class="t">User</span> {
<span class="s">id</span>: <span class="t">string</span>;
<span class="s">name</span>: <span class="t">string</span>;
<span class="s">age</span>: <span class="t">number</span>;
<span class="s">email</span>?: <span class="t">string</span>;
}
<span class="c">// 泛型表操作 — 类型安全的 insert/select</span>
<span class="k">const</span> users = db.<span class="f">table</span>&lt;<span class="t">User</span>&gt;(<span class="s">'users'</span>);
<span class="c">// ✅ 类型检查通过</span>
<span class="k">await</span> users.<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'1'</span>, <span class="s">name</span>: <span class="s">'Alice'</span>, <span class="s">age</span>: <span class="n">30</span> });
<span class="c">// ❌ TypeScript 报错:缺少 name</span>
<span class="c">// await users.insert({ id: '2', age: 25 });</span></pre>
<h2 id="config">⚙️ 完整配置项</h2>
<table>
<tr><th>属性</th><th>类型</th><th>默认值</th><th>说明</th></tr>
<tr><td><code>name</code></td><td><code>string</code></td><td><code>'metona-sqlark'</code></td><td>数据库名称(必填)</td></tr>
<tr><td><code>mode</code></td><td><code>'memory'|'disk'|'hybrid'</code></td><td><code>'hybrid'</code></td><td>存储模式</td></tr>
<tr><td><code>diskEngine</code></td><td><code>'indexeddb'|'opfs'</code></td><td><code>'indexeddb'</code></td><td>磁盘引擎类型</td></tr>
<tr><td><code>version</code></td><td><code>number</code></td><td><code>1</code></td><td>数据库版本号</td></tr>
<tr><td><code>plugins</code></td><td><code>MetonaPlugin[]</code></td><td><code>[]</code></td><td>初始插件列表</td></tr>
<tr><td><code>onReady</code></td><td><code>(db) => void</code></td><td>-</td><td>初始化完成回调</td></tr>
<tr><td><code>onError</code></td><td><code>(err) => void</code></td><td>-</td><td>错误回调</td></tr>
</table>
<h2 id="engine">💾 存储引擎</h2>
<table>
<tr><th>引擎</th><th>模式</th><th>持久化</th><th>性能</th><th>适用场景</th></tr>
<tr><td><code>MemoryEngine</code></td><td>memory</td><td>❌ 否</td><td>⚡ 极快</td><td>临时数据、缓存、测试</td></tr>
<tr><td><code>IndexedDBEngine</code></td><td>disk</td><td>✅ 是</td><td>🚀 快</td><td>通用持久化,兼容性最好</td></tr>
<tr><td><code>OPFSEngine</code></td><td>disk</td><td>✅ 是</td><td>🚀 快</td><td>现代浏览器,文件级存储</td></tr>
<tr><td><code>HybridEngine</code></td><td>hybrid</td><td>✅ 是</td><td>⚡ 极快</td><td>生产推荐,读写均走内存</td></tr>
</table>
<p>Hybrid 引擎采用 <strong>write-through</strong> 策略:所有写操作同时写入内存和磁盘,所有读操作直接从内存返回,启动时从磁盘加载数据到内存。</p>
<h2 id="errors">⚠️ 错误处理</h2>
<p>所有错误抛出 <code>DatabaseError</code> 实例。</p>
<pre><span class="k">try</span> {
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">'SELECT * FROM nonexistent'</span>);
} <span class="k">catch</span> (err) {
<span class="k">if</span> (err <span class="k">instanceof</span> <span class="t">DatabaseError</span>) {
<span class="f">console.log</span>(err.message); <span class="c">// 'Table "nonexistent" does not exist'</span>
<span class="f">console.log</span>(err.code); <span class="c">// 'TABLE_NOT_FOUND'</span>
<span class="f">console.log</span>(err.details); <span class="c">// 附加信息</span>
}
}</pre>
<table>
<tr><th>错误码</th><th>触发场景</th></tr>
<tr><td><code>TABLE_NOT_FOUND</code></td><td>表不存在</td></tr>
<tr><td><code>TABLE_EXISTS</code></td><td>表已存在</td></tr>
<tr><td><code>DUPLICATE_KEY</code></td><td>主键重复</td></tr>
<tr><td><code>UNIQUE_VIOLATION</code></td><td>唯一约束冲突</td></tr>
<tr><td><code>VALIDATION_ERROR</code></td><td>数据校验失败</td></tr>
<tr><td><code>TYPE_ERROR</code></td><td>字段类型错误</td></tr>
<tr><td><code>SCHEMA_ERROR</code></td><td>表结构定义错误</td></tr>
<tr><td><code>DB_NOT_READY</code></td><td>数据库未初始化</td></tr>
<tr><td><code>PARSE_ERROR</code></td><td>SQL 语法错误</td></tr>
<tr><td><code>TRANSACTION_ERROR</code></td><td>事务执行失败</td></tr>
<tr><td><code>COMPILE_ERROR</code></td><td>编译 AST 到查询计划失败</td></tr>
<tr><td><code>CONFIG_ERROR</code></td><td>配置错误</td></tr>
</table>
</main>
</div>
<script>
document.querySelectorAll('.sidebar a').forEach(a => {
a.addEventListener('click', function() {
document.querySelectorAll('.sidebar a').forEach(x => x.classList.remove('active'));
this.classList.add('active');
});
});
</script>
</body>
</html>