点击关注公众号,“技术干货” 及时达!
B站【手把手前端项目接入sqlite数据库sql】 https://www.bilibili.com/video/BV1B2SdYXEEn/?share_source=copy_web&vd_source=60643e0be9c389e9626916661281d724
❝sql.js 是一个强大的JavaScript库,它使得SQLite数据库能够在网页浏览器中运行。这个开源项目提供了一种方式,让开发者可以在前端环境中实现轻量级的数据库操作,无需依赖服务器端数据存储,极大地增强了Web应用的数据处理能力。通过将SQLite编译成WebAssembly(wasm),sql.js实现了高效的数据库操作,支持标准的SQL查询语言。
sql.js 是 SQLite 的 Webassembly 版,使用上和 SQLite 基本没有区别
如果你需要在浏览器中处理「大量数据」,并且希望使用 SQL 语法来操作这些数据,那么 SQL.js 将是一个不错的选择。
如果您不想在主应用程序线程中运行 CPU 密集型 SQL 查询, 您可以使用更有限的 WebWorker API。
详细api请移步官方文档sql.js sqlite 官网:https://sql.js.org/#/?id=inside-the-browser
sqlite 不会持久化缓存数据,只会存在运行内存中,需要自行导入导出文件。
❞
我们的目标
❝本指南将sqlite数据集成到网页项目中,并学习相应api操作,
实现案例效果 导入.sqlite文件,执行操作,导出.sqlite文件
本教程使用版本*"sql.js": "^1.11.0"*
❞
项目中加载sql.js
❝实现的最终效果,浏览器无任何错误,且,会在控制打印
{a:1,b:'world'}
您始终可以在 https://github.com/sql-js/sql.js/releases/latest 上找到最新发布的工件。
对于每个版本,您都会在发布资产中找到一个名为的文件。它将包含:
sqljs.zip
❞
sql-wasm.js
:Sql.js 的 Web Assembly 版本。缩小并适合生产。使用这个。如果您使用此项,您还需要包含/发货。sql-wasm.wasm
sql-wasm-debug.js
:Web 程序集,Sql.js. Larger 的调试版本,打开了断言。对本地开发有用。如果您使用这个,您将需要包含/发货。sql-wasm-debug.wasm
sql-asm.js
:较旧的 asm.js Sql.js 版本。速度较慢且较大。出于兼容性原因提供。sql-asm-memory-growth.js
:默认情况下,Asm.js不允许内存增长,因为它速度较慢且会取消优化。如果您使用的是 sql-asm.js 并看到此错误 (),请使用此文件。Cannot enlarge memory arrays
sql-asm-debug.js
:Sql.js的 Debug asm.js 版本。用于本地开发。worker.*
- 上述库的 Web Worker 版本。更有限的 API。参见 examples/GUI/gui.js 这是一个很好的例子。
有两个版本,一个是在网页中直接加载的版本,一个是electron等桌面端应用使用的版本
1.在网页中加载的版本(本教程的版本),有两种加载方式
可以使用npm包安装(推荐),使用构建工具「webpack」或「vite」等
❝
库下载地址:sql.js - npm (npmjs.com)
❞npm i sql.js
❝
sql.js需要依赖sql-wasm.wasm文件,我们需要添加到自己的目录下面引用
❞node_modules
- sql.js
- sql-wasm.wasm将这个文件放在自己的dist目录下面❝
新建dist目录/
❞dist
- sql-wasm.wasm❝
运行下面的代码,没有报错,且控制台打印对应数据
❞import initSqlJs from 'sql.js';
const SQL = await initSqlJs({
// 这里会加载dist/sql-wasm.wasm
locateFile: file => `./dist/${file}`
});
// Create a database
const db = new SQL.Database();
let sqlstr = "CREATE TABLE hello (a int, b char); \
INSERT INTO hello VALUES (0, 'hello'); \
INSERT INTO hello VALUES (1, 'world');";
db.run(sqlstr); // Run the query without returning anything
// Prepare an sql statement
const stmt = db.prepare("SELECT * FROM hello WHERE a=:aval AND b=:bval");
// Bind values to the parameters and fetch the results of the query
const result = stmt.getAsObject({':aval' : 1, ':bval' : 'world'});
console.log(result); // Will print {a:1, b:'world'}
也可以直接在网页中加入(没有使用任何构建工具),需要在 web 静态服务器下访问本文件(推荐 vscode 插件 liveserver)
❝
库下载地址:sql.js - Libraries - cdnjs - The #1 free and open source CDN built to make life easier for developers
❞一种是直接在网页头部引入(不推荐)
❝
如果cdn出现问题,会导致不可用
❞<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<script lay-src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.11.0/sql-wasm.js" integrity="sha512-tz0jOZaOg9RtWWB6AdxSkINQwIs7S5obj1Dlml9KewZLPTblTWCux5eLtnexBb8kbLUo5crPmjsi8/vI17Vw0w==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<div id="app"></div>
<script>
config = {
// 从云端加载https://sql.js.org/distsql-wasm.wasm
locateFile: file => `https://sql.js.org/dist/${file}`
};
initSqlJs(config).then(function (SQL) {
//Create the database
const db = new SQL.Database();
// Run a query without reading the results
db.run('CREATE TABLE test (col1, col2);');
// Insert two rows: (1,111) and (2,222)
db.run('INSERT INTO test VALUES (?,?), (?,?)', [1, 111, 2, 222]);
// Prepare a statement
const stmt = db.prepare('SELECT * FROM test WHERE col1 BETWEEN $start AND $end');
stmt.getAsObject({ $start: 1, $end: 1 }); // {col1:1, col2:111}
// Bind new values
stmt.bind({ $start: 1, $end: 2 });
while (stmt.step()) {
//
const row = stmt.getAsObject();
console.log('Here is a row: ' + JSON.stringify(row));
}
});
</script>
</body>
</html>
一种是将文件下载在自己的文件目录中,然后引入(推荐)
❝
库下载地址:sql.js - Libraries - cdnjs - The #1 free and open source CDN built to make life easier for developers
❞❝
从下载地址下载两个文件,存在dist目录
❞dist
- sql-wasm.wasm
- sql-wasm.js<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<script lay-src="./dist/sql-wasm.js"></script>
</head>
<body>
<div id="app"></div>
<script>
config = {
// 这里请求自己目录下面的.dist/sql-wasm.wasm
locateFile: file => `./dist/${file}`
};
initSqlJs(config).then(function (SQL) {
//Create the database
const db = new SQL.Database();
// Run a query without reading the results
db.run('CREATE TABLE test (col1, col2);');
// Insert two rows: (1,111) and (2,222)
db.run('INSERT INTO test VALUES (?,?), (?,?)', [1, 111, 2, 222]);
// Prepare a statement
const stmt = db.prepare('SELECT * FROM test WHERE col1 BETWEEN $start AND $end');
stmt.getAsObject({ $start: 1, $end: 1 }); // {col1:1, col2:111}
// Bind new values
stmt.bind({ $start: 1, $end: 2 });
while (stmt.step()) {
//
const row = stmt.getAsObject();
console.log('Here is a row: ' + JSON.stringify(row));
}
});
</script>
</body>
</html>
2.在node版本(electron等本机桌面应用程序使用)
❝库下载地址:sqlite3 - npm (npmjs.com)
❞
npm install sqlite3
案例
手动导入导出sqlite文件
import initSqlJs from 'sql.js';
const importDBFromFile = (event) => {
const file = event.target.files[0];
if (!file) {
return;
}
const reader = new FileReader();
reader.onload = async (e) => {
const data = toBinArray(e.target.result);
db = new SQL.Database(data);
console.log('数据库已导入');
// let sqlstr = "CREATE TABLE hello (a int, b char); \
// INSERT INTO hello VALUES (0, 'hello'); \
// INSERT INTO hello VALUES (1, 'red润'); \
// INSERT INTO hello VALUES (2, 'world');";
// db.run(sqlstr); // Run the query without returning anything
// Prepare an sql statement
const stmt = db.prepare("SELECT * FROM hello WHERE a=:aval");
// Bind values to the parameters and fetch the results of the query
const result = stmt.getAsObject({ ':aval': 0 });
console.log(result); // Will print {a:2, b:'world'}
};
reader.readAsText(file);
};
const exportDBToFile = () => {
if (!db) {
console.log("请先导入sqlite数据")
return
}
const exportData = db.export();
const blob = new Blob([toBinString(exportData)], { type: 'application/octet-stream' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'database.sqlite';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
// or if you are in a browser:
// const initSqlJs = window.initSqlJs;
const toBinArray = (str) => {
var l = str.length,
arr = new Uint8Array(l);
for (var i = 0; i < l; i++) arr[i] = str.charCodeAt(i);
return arr;
}
const toBinString = (arr) => {
var uarr = new Uint8Array(arr);
var strings = [], chunksize = 0xffff;
// There is a maximum stack size. We cannot call String.fromCharCode with as many arguments as we want
for (var i = 0; i * chunksize < uarr.length; i++) {
strings.push(String.fromCharCode.apply(null, uarr.subarray(i * chunksize, (i + 1) * chunksize)));
}
return strings.join('');
}
// 在 HTML 中添加文件输入元素
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = '.sqlite';
fileInput.addEventListener('change', importDBFromFile);
document.body.appendChild(fileInput);
// 添加导出按钮
const button = document.createElement('button');
button.textContent = '导出.sqlite文件';
button.addEventListener('click', exportDBToFile);
document.body.appendChild(button);
const SQL = await initSqlJs({
// Required to load the wasm binary asynchronously. Of course, you can host it wherever you want
// You can omit locateFile completely when running in node
locateFile: file => `./dist/${file}`
});
let db = null;
持久化缓存
<!doctype html>
<html>
<head>
<meta charset="utf8">
<title>Persistent sqlite</title>
<script lay-src="../dist/sql-wasm.js"></script>
</head>
<body>
<p>You have seen this page <span id="views">0</span> times.</p>
<div>
You have been here on the following dates: <ol id="dates"></ol>
</div>
<script>
var baseUrl = '../dist/';
function toBinArray(str) {
var l = str.length,
arr = new Uint8Array(l);
for (var i = 0; i < l; i++) arr[i] = str.charCodeAt(i);
return arr;
}
function toBinString(arr) {
var uarr = new Uint8Array(arr);
var strings = [], chunksize = 0xffff;
// There is a maximum stack size. We cannot call String.fromCharCode with as many arguments as we want
for (var i = 0; i * chunksize < uarr.length; i++) {
strings.push(String.fromCharCode.apply(null, uarr.subarray(i * chunksize, (i + 1) * chunksize)));
}
return strings.join('');
}
// Normally Sql.js tries to load sql-wasm.wasm relative to the page, not relative to the javascript
// doing the loading. So, we help it find the .wasm file with this function.
var config = {
locateFile: filename => `${baseUrl}/${filename}`
}
initSqlJs(config).then(function (SQL) {
var dbstr = window.localStorage.getItem("viewcount.sqlite");
if (dbstr) {
var db = new SQL.Database(toBinArray(dbstr));
} else {
var db = new SQL.Database();
db.run("CREATE TABLE views (date INTEGER PRIMARY KEY)");
}
db.run("INSERT INTO views(date) VALUES (?)", [Date.now()]);
document.getElementById('views').textContent = db.exec("SELECT COUNT(*) FROM views")[0].values[0][0];
var count = 0,
dates = document.getElementById("dates");
db.each("SELECT date FROM views ORDER BY date ASC",
function callback(row) {
var li = document.createElement("li");
li.textContent = new Date(row.date);
dates.appendChild(li);
}, function done() {
var dbstr = toBinString(db.export());
window.localStorage.setItem("viewcount.sqlite", dbstr);
}
);
});
</script>
</body>
</html>
交互案例
<!doctype html>
<html>
<!--Simple Read eval print loop for SQL-->
<head>
<meta charset="utf8">
<title>SQL REPL</title>
<script lay-src="../dist/sql-wasm.js"></script>
</head>
<body>
<input type='text' id='input' placeholder="ENTER SOME SQL" size='50'
value="CREATE TABLE test(val);INSERT INTO test VALUES (666); SELECT * FROM test">
<button id='submit'>Execute</button>
<pre id='result'></pre>
<pre id='error'></pre>
<script>
//Open a blank database
var db;
initSqlJs({ locateFile: filename => `../dist/${filename}` }).then(function (SQL) {
db = new SQL.Database();
});
document.getElementById('submit').onclick = function () {
var sql = document.getElementById('input').value;
var result = '', error = '';
try { result = db.exec(sql); }
catch (e) { error = e; }
document.getElementById('result').innerHTML = JSON.stringify(result, null, ' ');
document.getElementById('error').innerHTML = error;
};
</script>
</body>
代码解析
initSqlJs 函数
API 中的根对象是 initSqlJs 函数,它接受一个 SqlJsConfig 参数,并返回一个 SqlJs 对象
SqlJs 对象
initSqlJs返回主 sql.js 对象 SqlJs 模块,其中包含:
Database
「Database」 是主类,表示 SQLite 数据库。
db.run(
"INSERT INTO test VALUES (:age, :name)",
{ ':age' : 18, ':name' : 'John' }
);const stmt = db.prepare('SELECT * FROM test WHERE col1 BETWEEN $start AND $end');
stmt.getAsObject({ $start: 1, $end: 1 }); // {col1:1, col2:111}
// Bind new values
stmt.bind({ $start: 1, $end: 2 });
while (stmt.step()) {
//
const row = stmt.getAsObject();
console.log('Here is a row: ' + JSON.stringify(row));
}let res = db.export()
console.log(res)var db = new SQL.Database();
var res = db.exec(
"DROP TABLE IF EXISTS test;\n"
+ "CREATE TABLE test (id INTEGER, age INTEGER, name TEXT);"
+ "INSERT INTO test VALUES ($id1, :age1, @name1);"
+ "INSERT INTO test VALUES ($id2, :age2, @name2);"
+ "SELECT id FROM test;"
+ "SELECT age,name FROM test WHERE id=$id1",
{
"$id1": 1, ":age1": 1, "@name1": "Ling",
"$id2": 2, ":age2": 18, "@name2": "Paul"
}
);db.each("SELECT name,age FROM users WHERE age >= $majority", {$majority:18},
function (row){console.log(row.name + " is a grown-up.")}
);db.create_function("addOne", function (x) {return x+1;})
db.exec("SELECT addOne(1)") // = 2db.create_aggregate("js_sum", {
init: () => 0,
step: (state, value) => state + value,
finalize: state => state
});
db.exec("SELECT js_sum(column1) FROM (VALUES (1), (2))"); // = 3关闭数据库 const db = new SQL.Database();
let data =
const db = new SQL.Database(data);构造函数
close()
注册自定义aggregate
创建函数
执行 sql 语句,并为每行 result 调用回调。
执行sql
将数据库的内容导出到二进制数组。此操作将关闭并重新打开数据库,这将导致程序设置回其默认值。
准备sql
执行sql
Statement
「Statement」 类用于准备好的语句。
var stmt = db.prepare(
"UPDATE test SET a=@newval WHERE id BETWEEN $mini AND $maxi"
);
stmt.bind({$mini:10, $maxi:20, '@newval':5});
常用操作
详细api请移步官方文档sql.js
创建数据库/导入数据
// 创建一个新的数据库实例
const db = new SQL.Database();
// 创建并加载数据(见案例章节)
var db = new SQL.Database(toBinArray(dbstr));
查询
// 执行一个 INSERT 语句
db.run("INSERT INTO tasks (name, priority) VALUES ('Buy groceries', 'high')");
// 执行一个 SELECT 语句
const result = db.exec("SELECT * FROM tasks WHERE priority='high'");
// 输出结果
console.log(result);
导出
// 导出数据库
const databaseBlob = db.export();
可能遇到的问题
1. 配置 Vite 以包括 .db
文件
在 vite.config.js
中,您可以添加以下配置:
import { defineConfig } from 'vite';
export default defineConfig({
assetsInclude: ['**/*.db'],
});
2. 确保文件路径正确
确保您在项目中引用 .db
文件的路径是正确的。例如:
const dbFilePath = './path/to/your.db'; // 确保路径正确
3. 处理 .db
文件
在代码中,确保您正确加载和处理 .db
文件。例如,使用 fetch
来获取文件内容:
const response = await fetch(dbFilePath);
// 转换成buffer
const data = await response.arrayBuffer();
const db = new SQL.Database(new Uint8Array(data)); // 从文件加载数据库
完结
sqlite确实好用,如无「必要」,勿「增实体」
点击关注公众号,“技术干货” 及时达!