Electron-开发实践:本地数据库SQLite
【前言】
本地存取数据的一些常见的手段:Cookie,LocalStorage,IndexedDB,SQLite等,
这里做下简单的对比
Cookie
|—存储大小:4k
|—设置有效期:可以
|—服务端获取:可以
|—特点:简单数据存取
LocalStorage
|—存储大小:5M
|—设置有效期:不行
|—服务端获取:不行
|—特点:本地键值对存取
IndexedDB
|—存储大小:250M+
|—设置有效期:不行
|—服务端获取:不行
|—特点:非关系型数据库
SQLite
|—存储大小:无限制
|—设置有效期:不行
|—服务端获取:不行
|—特点:本地关系型数据库
这里推荐两篇文章,分别介绍了LocalStorage和IndexedDB
【SQLite】
SQLite,著名的本地关系型数据库,官网: https://www.sqlite.org/index.html
SQLite支持各种操作系统使用,包括Linux,Mac,Windows,Android等,下载: https://www.sqlite.org/download.html
同样的也有对应的npm包可以使用,sqlite3: https://www.npmjs.com/package/sqlite3
安装
// install
npm i sqlite3
// m1
npm i sqlite3 --target_arch=arm64
起步
安装好后,按官网的示例跑一下代码,代码如下:
const sqlite3 = require("sqlite3").verbose();
const db = new sqlite3.Database(":memory:");
db.serialize(function () {
db.run("CREATE TABLE lorem (info TEXT)");
const stmt = db.prepare("INSERT INTO lorem VALUES (?)");
for (var i = 0; i < 10; i++) {
stmt.run("Ipsum " + i);
}
stmt.finalize();
db.each("SELECT rowid AS id, info FROM lorem", function (err, row) {
console.log(row.id + ": " + row.info);
});
});
db.close();
以上代码逻辑:
1.创建数据库,这个数据库比较特殊是在内存内的
2.创建表格
3.插入数据
4.查询数据
执行结果如下

文档
SQLIte的文档: https://www.sqlite.org/docs.html
|—支持的数据类型: https://www.sqlite.org/datatype3.html
|—创建表格: https://www.sqlite.org/lang_createtable.html
|—更新表格: https://www.sqlite.org/lang_altertable.html
|—创建视图: https://www.sqlite.org/lang_createview.html
|—删除表格: https://www.sqlite.org/lang_droptable.html
|—插入数据: https://www.sqlite.org/lang_insert.html
|—查询数据: https://www.sqlite.org/lang_select.html
|—删除数据: https://www.sqlite.org/lang_delete.html
npm-sqlite3的文档: https://github.com/TryGhost/node-sqlite3/wiki/API
【qiao-sqlite】
这里为了方便使用,封装了一个npm包: https://code.insistime.com/#/qiao-sqlite
createDB
创建数据库
"use strict";
// q
const q = require("qiao-sqlite");
// db
const db = q.createDB("./test/test.db");
console.log(db);
createTable
创建表格
"use strict";
// q
const q = require("qiao-sqlite");
// db
const db = q.createDb("./__tests__/test.db");
// table
const sql =
"CREATE TABLE if not exists t_project (project_name TEXT, project_appid TEXT, project_icon_url TEXT)";
// test
async function test() {
try {
await q.createTable(db, sql);
} catch (e) {
console.log(e);
}
}
// run
test();
showTables
列出表格
"use strict";
// q
const q = require("qiao-sqlite");
// db
const db = q.createDB("./__tests__/test.db");
// test
async function test() {
try {
const rows = await q.showTables(db);
console.log(rows);
} catch (e) {
console.log(e);
}
}
// run
test();
dropTable
删除表格
"use strict";
// q
const q = require("qiao-sqlite");
// db
const db = q.createDB("./__tests__/test.db");
// test
async function test() {
try {
console.log(await q.showTables(db));
await q.dropTable(db, "t_project");
console.log(await q.showTables(db));
} catch (e) {
console.log(e);
}
}
// run
test();
insertData
插入数据
"use strict";
// q
const q = require("qiao-sqlite");
// db
const db = q.createDB("./__tests__/test.db");
// data
const sql = "insert into t_project values (?, ?, ?)";
// test
async function test() {
try {
await q.insertData(db, sql, ["name", "appid", "url"]);
} catch (e) {
console.log(e);
}
}
// run
test();
deleteData
删除数据
"use strict";
// q
const q = require("qiao-sqlite");
// db
const db = q.createDB("./__tests__/test.db");
// data
const sql = "delete from t_project where rowid=?";
// test
async function test() {
try {
await q.deleteData(db, sql, [1]);
} catch (e) {
console.log(e);
}
}
// run
test();
modifyData
修改数据
"use strict";
// q
const q = require("qiao-sqlite");
// db
const db = q.createDB("./__tests__/test.db");
// data
const sql = "update t_project set project_name=?";
// test
async function test() {
try {
await q.modifyData(db, sql, ["name1"]);
} catch (e) {
console.log(e);
}
}
// run
test();
selectData
查询数据
"use strict";
// q
const q = require("qiao-sqlite");
// db
const db = q.createDB("./__tests__/test.db");
// sql
const sql = "SELECT rowid,* FROM t_project";
// test
async function test() {
try {
const rows = await q.selectData(db, sql);
console.log(rows);
} catch (e) {
console.log(e);
}
}
// run
test();
【实践】
SQLite一般应用于大型的Android,iOS应用中,或者桌面应用中,
这里在electron环境下做一下实践,
实践项目的介绍:
这里会在以上基础上,用electron+sqlite3实现一个todo list
主进程封装sqlite方法
electron分为主进程和渲染进程,
sqlite的使用肯定在主进程上,
渲染进程通过ipc通信调用主进程封装好的sqlite方法,
由于需要ipc通信,封装了sqlite相关的方法,如下
"use strict";
// path
const path = require("path");
// electron
const { app } = require("electron");
// sqlite
const {
createDb,
createTable,
dropTable,
showTables,
insertData,
modifyData,
deleteData,
selectData,
} = require("qiao-sqlite");
// json
const { success, danger } = require("qiao-json");
/**
* sqlite
* @returns
*/
exports.sqlite = () => {
const userDataPath = app.getPath("userData");
const dbPath = path.resolve(userDataPath, "./electron.db");
const db = createDb(dbPath);
return db;
};
/**
* dbCreateTable
* @param {*} sql
* @returns
*/
exports.dbCreateTable = async (sql) => {
// check
if (!sql) return danger("need create table sql");
// db
const db = exports.sqlite();
// create table
try {
await createTable(db, sql);
return success("create table success");
} catch (e) {
// return danger('create table fail', e);
return success("create table success");
}
};
/**
* dbDropTable
* @param {*} tableName
* @returns
*/
exports.dbDropTable = async (tableName) => {
// check
if (!tableName) return danger("need tableName");
// db
const db = exports.sqlite();
// drop table
try {
await dropTable(db, tableName);
return success("drop table success");
} catch (e) {
return success("drop table success");
}
};
/**
* dbShowTables
* @returns
*/
exports.dbShowTables = async () => {
// db
const db = exports.sqlite();
// show tables
try {
const rows = await showTables(db);
return success("show table success", rows);
} catch (e) {
return success("show table success");
}
};
/**
* dbInsertData
* @param {*} sql
* @param {*} params
* @returns
*/
exports.dbInsertData = async (sql, params) => {
// check
if (!sql) return danger("need insert data sql");
// db
const db = exports.sqlite();
// insert data
try {
await insertData(db, sql, params);
return success("insert data success");
} catch (e) {
return danger("insert data fail", e);
}
};
/**
* dbDeleteData
* @param {*} sql
* @param {*} params
* @returns
*/
exports.dbDeleteData = async (sql, params) => {
// check
if (!sql) return danger("need delete data sql");
// db
const db = exports.sqlite();
// delete data
try {
await deleteData(db, sql, params);
return success("delete data success");
} catch (e) {
return danger("delete data fail", e);
}
};
/**
* dbModifyData
* @param {*} sql
* @param {*} params
* @returns
*/
exports.dbModifyData = async (sql, params) => {
// check
if (!sql) return danger("need modify data sql");
// db
const db = exports.sqlite();
// modify data
try {
await modifyData(db, sql, params);
return success("modify data success");
} catch (e) {
return danger("modify data fail", e);
}
};
/**
* dbSelectData
* @param {*} sql
* @param {*} params
* @returns
*/
exports.dbSelectData = async (sql, params) => {
// check
if (!sql) return danger("need select data sql");
// db
const db = exports.sqlite();
// select data
try {
const rows = await selectData(db, sql, params);
return success("select data success", rows);
} catch (e) {
return danger("select data fail", e);
}
};
主进程ipc监听
主进程封装好sqlite后,要监听ipc通信,并调用对应的sqlite方法,
如下监听创建表格的方法
"use strict";
// electron
const { ipcMain } = require("electron");
// sqlite
const { dbCreateTable } = require("./sqlite-main.js");
// const
const { IPC_SQLITE_CREATE_TABLE } = require("../../_util/constant.js");
/**
* ipc sqlite create table
*/
ipcMain.handle(IPC_SQLITE_CREATE_TABLE, (sql) => {
return dbCreateTable(sql);
});
渲染进程注入preload
渲染进程调用主进程方法,需要先注入preload文件
如下注入了ipc通信的方法
"use strict";
// electron
const { ipcRenderer } = require("electron");
// const
const { IPC_SQLITE_CREATE_TABLE } = require("../../_util/constant.js");
/**
* createTableIPC
*/
exports.createTableIPC = async () => {
return await ipcRenderer.invoke(IPC_SQLITE_CREATE_TABLE);
};
渲染进程调用ipc方法
注入preload后,即可在渲染进程,
本项目对应dishi-web文件夹下调用ipc方法,
const sql =
"CREATE TABLE if not exists t_project (project_name TEXT, project_appid TEXT, project_icon_url TEXT) ";
const rs = await window.electron.createTableIPC(sql);
console.log(rs);
最终效果
1.渲染进程中的js调用ipc方法
2.调用的ipc方式是创建window时注入的preload文件提供
3.主进程监听ipc调用,并执行对应的sqlite方法
如下

完善
按照如上步骤,继续完善基于sqlite的todo list
1.创建数据库和表格
2.新增todo
3.删除todo
4.新增done
5.查询todo
6.查询done
效果如下

以上代码详见: https://github.com/uikoo9/dishi-monorepo/tree/sqlite
【总结】
1.SQLite-介绍Cookie,LocalStorage,IndexedDB和SQLite的不同
2.SQLite-SQLite介绍
3.SQLite-SQLite文档
4.SQLite-sqlite3安装和起步
5.SQLite-sqlite3工具:qiao-sqlite, https://code.insistime.com/#/qiao-sqlite
6.SQLite-另一篇文章: https://uikoo9.blog.csdn.net/article/details/123924787
7.SQLite-实践electron+sqlite项目: https://github.com/uikoo9/dishi-monorepo/tree/sqlite
更多同类文章
- Electron-开发实践:注册苹果开发者账号
- Electron-开发实践:clipboard剪切板
- Electron-开发实践:crash上报及解析
- Electron-开发实践:DMG安装包定制
- Electron-开发实践:包体积精简
- Electron-开发实践:制作icns图标
- Electron-开发实践:本地日志
- Electron-开发实践:定制Mac菜单
- Electron-开发实践:Mac应用包签名和公证
- Electron-开发实践:Mac打包
- Electron-开发实践:使用Monorepo管理Electron项目
- Electron-开发实践:注册快捷键
- Electron-开发实践:几种更新方式
- Electron-开发实践:创建Window
- 一篇文章学会Electron