
首次点击按钮执行 SQL 查询时出现明显延迟(约 1000ms),而后续执行极快(50–100ms),根本原因是未预编译查询语句,导致每次调用 db.exec() 都需重复解析、编译 SQL;通过 db.prepare() 提前编译可彻底消除该延迟。
首次点击按钮执行 sql 查询时出现明显延迟(约 1000ms),而后续执行极快(50–100ms),根本原因是未预编译查询语句,导致每次调用 `db.exec()` 都需重复解析、编译 sql;通过 `db.prepare()` 提前编译可彻底消除该延迟。
在使用 SQL.js(SQLite 的 WebAssembly 移植版)时,db.exec() 是一个便捷但非优化的接口:它每次调用都会完整经历 SQL 解析 → 编译 → 执行 → 清理的全流程。对于重复执行的查询(如按钮点击触发的固定 SELECT),这会造成显著的首执行开销——尤其在表结构复杂或数据量较大时(即使仅 3MB 数据库,SELECT * FROM tb 若涉及多列、索引或类型推断,编译成本仍不可忽视)。
✅ 正确做法是:在数据库加载完成后、事件监听器注册前,预先准备(prepare)语句,并将返回的 Statement 对象缓存复用:
(async () => {
const response = await fetch('data.db');
const buffer = await response.arrayBuffer();
const db = new SQL.Database(new Uint8Array(buffer));
console.log('Db loaded');
// ✅ 关键优化:提前 prepare 查询语句(仅执行一次)
const stmt = db.prepare('SELECT * FROM tb');
const btn = document.getElementById('btn');
btn.addEventListener('click', () => {
// ✅ 复用已编译的 statement,跳过解析/编译阶段
const result = stmt.all(); // 或 stmt.run() + stmt.get() 等
console.log(result);
});
})();? 注意事项:
- stmt.all() 返回全部结果数组(等价于 db.exec(...)[0].values),语义更清晰且性能更优;
- 若查询含参数(如 WHERE id = ?),可用 stmt.bind([value]).all() 安全传参,避免 SQL 注入;
- prepare() 后无需手动释放(SQL.js 自动管理),但大量长期缓存语句时建议在页面卸载前调用 stmt.free();
- exec() 适合一次性、动态拼接的查询;prepare + run/all/get 组合才是高频查询的标准实践。
? 总结:SQL.js 的性能瓶颈常不在执行层,而在编译层。将 db.exec() 替换为预编译语句,不仅解决首次延迟问题,还能提升整体稳定性与可维护性——这是 Web 端 SQLite 应用的必备优化习惯。

















