在浏览器中使用SQLite(官方sqlite3.wasm)

有人可能会问:既然浏览器里又内置得IndexedDB,而且在IndexedDB里存数据,关了浏览器数据也不会丢,为什么还要在浏览器里用SQLite?

实际上,当 IndexedDB 内的数据量增多,数据和数据之间的关系变得复杂,IndexedDB 的劣势就凸显出来了。我们在这方面踩过很多坑,这里就不细说了。

好了,言归正传:

我建议你不要使用这个工具:https://github.com/sqlite/sqlite-wasm,用起来麻烦的很,而且类型定义还有问题(看看 issue 就知道了)。

还是自己使用原始的、官方提供的 js 文件比较好。

首先到  SQLite Download Page 下载 WebAssembly & JavaScript 版本的SQLite。

下载解压后你将得到:

我们需要的文件都在 jswasm 目录中。

把 jswasm 目录中的如下文件拷贝到你的根目录下,(可以通过 https://domain.com/sqlite3.js 访问)

接着,在你的页面引入 sqlite3-worker1-promiser.js 脚本文件

<script src="sqlite3-worker1-promiser.js"></script>

现在我们封装一个 TypeScript 类

class Db {dbId: string;dbFunc: (cmd: string, param: object) => Promise<{ dbId: string; messageId: string; type: string; result: any }>;constructor() {}exec(sql: string): Promise<any[]> {return new Promise((resolve, reject) => {let rows = [];this.dbFunc("exec", {dbId: this.dbId,sql,callback: (result: { columnNames: string[]; row: any[]; rowNumber: number; type: string }) => {if (result.row) {let obj = {};for (let i = 0; i < result.columnNames.length; i++) {obj[result.columnNames[i]] = result.row[i];}rows.push(obj);} else {resolve(rows);}},});});}async open() {const dbFactory = globalThis.sqlite3Worker1Promiser.v2;delete globalThis.sqlite3Worker1Promiser;const config = {debug: (...args) => console.debug("db worker debug", ...args),onunhandled: (ev) => console.error("Unhandled db worker message:", ev.data),onerror: (ev) => console.error("db worker error:", ev),};this.dbFunc = await dbFactory(config);let { dbId } = await this.dbFunc("open", {filename: "file:db.sqlite3?vfs=opfs",simulateError: 0,});this.dbId = dbId;let rows = await this.exec(`SELECT name FROM sqlite_master WHERE type='table' AND name='Job';`);if (rows.length <= 0) {await this.exec(`CREATE TABLE Job(Id VARCHAR2(36) NOT NULL PRIMARY KEY, JobInfo TEXT, RepeatType INT, StartTime BIGINT, EndTime BIGINT, ColorIndex INT);
CREATE INDEX JobInfo_Index ON Job(JobInfo);
CREATE TABLE Setting(ViewDefault INT DEFAULT 0, ViewVal INT, LangDefault INT DEFAULT 0, SkinDefault INT DEFAULT 0, AlertBefore INT);
INSERT INTO Setting (ViewDefault, ViewVal, LangDefault, SkinDefault, AlertBefore) VALUES (0, 0, 0, 0, 5);`);}let data = await this.exec(`select * from Setting;`);console.log(data);}async delDb() {const opfsRoot = await navigator.storage.getDirectory();await opfsRoot.removeEntry("db.sqlite3");}
}
export let db = new Db();

先来看打开数据库方法: open 。

我们使用 SQLite3 的 V2 版本的方法,其他老方法一股脑删掉。

const dbFactory = globalThis.sqlite3Worker1Promiser.v2;
delete globalThis.sqlite3Worker1Promiser;

接着创建一个数据库访问方法:dbFunc,然后使用这个方法创建数据库:db.sqlite3

this.dbFunc = await dbFactory(config);
let { dbId } = await this.dbFunc("open", {filename: "file:db.sqlite3?vfs=opfs",simulateError: 0,
});

注意,filename的路径和参数,我们让数据库保存在浏览器的OPFS文件系统中。

OPFS 是浏览器提供的一种私有文件系统,属于 File System Access API 的一部分。它的特点包括:

私有性:每个网站拥有独立的文件系统,其他网站无法访问。

高性能:支持原地读写和同步访问,适合数据库等对性能要求高的场景。

持久化:数据不会因刷新或关闭浏览器而丢失。

 数据文件创建成功后,将得到数据库id:dbId,这是个字符串。

接下来,我们检查数据库中是否存在指定的表,没有的话,就给数据库建表。

此时就用到了SQL指令执行方法:exec

执行 SQL 指令,也是用 dbFunc 方法完成的。

如果执行的 SQL 语句本身不返回数据(例如 INSERT, UPDATE, DELETE, 或查询结果为空的 SELECT),回调方法 callback 只会被执行一次。callback 的传入参数 result 没有 row 属性。

如果执行的 SQL 语句本身返回多行数据,那么 callback 方法会被执行多次,每次都可能返回多行查询结果,查询结果被存储在 result 的 row 属性里,最后一次 callback 方法被执行时, result 没有 row 属性,表示查询结束。

下面这段代码用来根据列名构造数据对象:

if (result.row) {let obj = {};for (let i = 0; i < result.columnNames.length; i++) {obj[result.columnNames[i]] = result.row[i];}rows.push(obj);
} else {resolve(rows);
}

如果你想删除数据库,可以使用 delDb 方法

const opfsRoot = await navigator.storage.getDirectory() 用于获取 OPFS 文件系统的根目录实例

opfsRoot.removeEntry(name) 会从当前目录中删除名为 "db.sqlite3" 的文件或子目录。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/89878.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/89878.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构(Java)--位运算

前言 本文为本小白学习数据结构的笔记&#xff0c;将以算法题为导向&#xff0c;向大家更清晰的介绍数据结构相关知识&#xff08;算法题都出自B站马士兵教育——左老师的课程&#xff0c;讲的很好&#xff0c;对于想入门刷题的人很有帮助&#xff09; 为什么要使用为位运算 位…

秋招Day14 - Redis - 应用

Redis如何实现异步消息队列&#xff1f; List配合LPUSH和RPOP。 另外就是用 Redis 的 Pub/Sub 来实现简单的消息广播和订阅。 但是这两种方式都是不可靠的&#xff0c;因为没有 ACK 机制所以不能保证订阅者一定能收到消息&#xff0c;也不支持消息持久化。 Redis如何实现延时…

因果语言模型、自回归语言模型、仅解码器语言模型都是同一类模型

因果语言模型、自回归语言模型、仅解码器语言模型都是同一类模型 flyfish 因果语言模型&#xff08;causal Language Models&#xff09; 自回归语言模型&#xff08;autoregressive language models&#xff09; 仅解码器语言模型&#xff08;decoder-only language models&am…

jvm架构原理剖析篇

简单题&#xff08;5道&#xff09; 考查内容&#xff1a;JVM运行时数据区域 题干&#xff1a;Java虚拟机栈的主要作用是&#xff1f; A. 存储对象实例 B. 存储方法调用和局部变量 C. 存储静态字段 D. 存储字节码指令 正确答案&#xff1a;B 解析&#xff1a;虚拟机栈用于存储方…

智链万物:人工智能驱动的产业智能化革命

当生成式AI在艺术与创意领域掀起风暴&#xff0c;大型语言模型重塑信息交互方式时&#xff0c;一场更为基础、影响更为深远的变革&#xff0c;正在全球实体经济的根基处悄然发生并加速推进——这就是产业智能化。它并非简单的“机器换人”&#xff0c;而是人工智能&#xff08;…

python中上下文管理器 与 try finally有什么区别

目录 主要区别代码对比何时使用哪种方式 主要区别 语法简洁性 上下文管理器使用 with 语句&#xff0c;语法更简洁优雅try-finally 需要显式编写异常处理代码&#xff0c;更冗长 代码复用性 上下文管理器可以封装为类或函数&#xff0c;便于在多处复用try-finally 通常需要在每…

人体属性识别+跌倒检测:儿童行为监测与安全升级

智慧幼儿园的AI智能检测盒应用实践 背景&#xff1a;传统园区管理的三大痛点 传统幼儿园管理长期面临三大核心挑战&#xff1a;一是安全监控依赖人工巡查&#xff0c;存在视觉盲区与响应延迟&#xff0c;如某连锁幼儿园曾因人工巡查疏漏&#xff0c;导致3起儿童跌倒事故未能及…

【ESP32-IDF笔记】09-UART配置和使用

环境配置 Visual Studio Code &#xff1a;版本1.98.2 ESP32&#xff1a;ESP32-S3 ESP-IDF&#xff1a;V5.4 支持型号&#xff1a;ESP32、ESP32-C2、ESP32-C3、ESP32-C5、ESP32-C6、ESP32-C61、ESP32-H2、ESP32-P4、 ESP32-S2、ESP32-S3 简介 通用异步接收器/发送器 (UART) …

在 .NET Core 和 React 中使用 WebSockets 和 SignalR 进行实时数据传输

对于需要即时更新和通知的应用程序来说&#xff0c;实时数据传输至关重要。在 .NET Core 中&#xff0c;WebSocket 和 SignalR 提供了强大的工具来实现客户端和服务器之间的实时通信。在本指南中&#xff0c;我们将探讨如何在 .NET Core 应用程序中使用 WebSocket 和 SignalR 实…

第八十六篇 大数据排序算法:从厨房整理到分布式排序的智慧

目录一、基础排序算法&#xff1a;生活场景中的计算智慧1.1 冒泡排序&#xff1a;图书馆的书籍整理1.2 插入排序&#xff1a;厨房调料的整理艺术二、高效排序算法&#xff1a;大数据处理的利器2.1 快速排序&#xff1a;音乐APP的智能歌单2.2 归并排序&#xff1a;学校成绩单的合…

开源 | V3.1.1慧知开源重卡运营充电桩平台 - 重卡运营充电桩平台管理解决方案;企业级完整代码 多租户、模拟器、多运营商、多小程序;

【开源免费版】推荐一套企业级开源充电桩平台&#xff1a;完整代码包含多租户、硬件模拟器、多运营商、多小程序&#xff0c;汽车 电动自行车、云快充协议&#xff1b;——(慧哥)慧知开源充电桩平台&#xff1b;https://liwenhui.blog.csdn.net/article/details/148242725?spm…

ONLYOFFICE 协作空间 企业版使用秘籍-8.使用虚拟数据房间,处理机密文档更安全

在当今快节奏的社会中&#xff0c;信息已成为极其关键的资源&#xff0c;因此&#xff0c;保护敏感数据至关重要。ONLYOFFICE 协作空间中的虚拟数据房间&#xff08;VDR&#xff09;提供了一个安全便捷的工作空间&#xff0c;确保文档受到严密保护的同时&#xff0c;也能实现轻…

系统架构设计师论文分享-论软件架构复用

我的软考历程 摘要 2023年2月&#xff0c;我所在的公司通过了研发纱线MES系统的立项&#xff0c;该项目为国内纱线工厂提供SAAS服务&#xff0c;旨在提升纱线工厂的数字化和智能化水平。我在该项目中担任架构设计师&#xff0c;负责该项目的架构设计工作。本文结合我在该项目…

虚拟主机与独立服务器如何选择

在搭建和维护网站时&#xff0c;选择合适的服务器套餐至关重要。虚拟主机和独立服务器是两种常见的选择&#xff0c;它们各有优缺点&#xff0c;适用于不同需求的用户。本文将深入探讨这两种服务器类型的特点&#xff0c;以帮助您为您的网站选择最合适的服务器解决方案。虚拟主…

NFC的安全技术体系

NFC&#xff08;近场通信&#xff09;技术因广泛应用于移动支付、身份认证、门禁控制等敏感场景&#xff0c;其安全技术体系是保障用户数据与交易安全的核心。该体系涵盖数据传输安全、存储安全、身份认证、防攻击机制等多个维度&#xff0c;通过硬件隔离、加密算法、协议规范等…

Echarts3D柱状图-圆柱体-文字在柱体上垂直显示的实现方法

全部代码 <!DOCTYPE html> <html lang"en" style"height: 100%"> <head><meta charset"utf-8"><title>3D柱状图-圆柱体-文字竖排</title> </head> <body style"height: 100%; margin: 0"…

【算法训练营Day08】字符串part2

文章目录 反转字符串里的单词右旋字符串KMP算法双指针法总结 反转字符串里的单词 题目链接&#xff1a;151. 反转字符串中的单词 双指针法解题逻辑 head指针遍历字符串遍历到单词首单词&#xff0c;生成end指针移动到单词尾部遇到完整单词收集&#xff0c;压入栈中head指针移动…

如何使用backtrace定位Linux程序的崩溃位置

在嵌入式Linux开发中&#xff0c;特别是复杂软件&#xff0c;多人协作开发时&#xff0c;当某人无意间写了一个代码bug导致程序崩溃&#xff0c;但又不知道崩溃的具体位置时&#xff0c;单纯靠走读代码&#xff0c;很难快速的定位问题。 本篇就来介绍一种方法&#xff0c;使用…

十大排序算法汇总

好的&#xff0c;下面为你整理一篇面试全覆盖、极其深入的十大排序算法总结博客&#xff0c;涵盖算法原理、复杂度、稳定性、应用场景、工程实践、C与Python实现&#xff08;含详细注释&#xff09;&#xff0c;并对比分析各种排序的优缺点与适用情境。内容力求结构清晰、讲解透…

零基础 “入坑” Java--- 七、数组(二)

文章目录 一、数组转字符串二、数组的拷贝三、求数组中元素的平均值四、查找数组中指定元素&#xff08;顺序查找&#xff09;五、数组排序&#xff08;冒泡排序&#xff09;六、查找数组中指定元素&#xff08;二分查找&#xff09;七、判断两个数组中的元素是否相等八、填充数…