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