在客户端开发中,无论是 PC 端,还是手机端,为了能够访问离线数据,数据经常需要保存到本地,IndexDB 可以用于存储本地数据,IndexDB 是一个对象存储,数据是以 key:value 的形式进行存储和访问的,相对于关系型数据库,访问速度快,但是结构简单。对于一些复杂的模型,多表关联关系,用 IndexDB 实现会相对复杂,需要通过查询实现。所以在业务项目中,通过关系型数据库对业务数据进行建模,采用 SQL进行数据访问更加方便。
SQLite是客户端常用的文件型关系数据库,主流的移动端、桌面应用大多使用的 SQLite进行离线数据存储,例如微信。SQLite 是一个轻量级基于文件关系型数据库,主要有以下特点:
- 支持标准 SQL
- 文件无需服务器支持
- 跨平台,Android、IOS、PC(Windows、Mac、Linux)
- 不需要任何配置,安装上依赖直接可以使用
安装,默认 SQLite 是x86 编译的,在Mac M1 上试用会报错,所以我们需要对 SQLLite 进行重新编译。
npm install sqlite3 --build-from-source --target_arch=arm64 --fallback-to-build
Electron + SQLite 创建用户的例子:
index.html
<!DOCTYPE html>
<html>
<head>
<title>Electron SQLite CRUD</title>
</head>
<body>
<h1>User Management</h1>
<input id="username" type="text" placeholder="Username">
<input id="age" type="number" placeholder="Age">
<button onclick="createUser()">Create User</button>
<button onclick="getUsers()">Load Users</button>
<div id="users"></div>
<script>
function createUser() {
const username = document.getElementById('username').value;
const age = document.getElementById('age').value;
window.api.createUser(username, parseInt(age)); #进程间互相调用
}
async function getUsers() {
const users = await window.api.getUsers();
const usersDiv = document.getElementById('users');
usersDiv.innerHTML = users.map(user => `<p>${user.username}, ${user.age} years old</p>`).join('');
}
</script>
</body>
</html>
main.js 主线程
const { ipcMain, app, BrowserWindow } = require('electron');
const path = require('path');
const sqlite3 = require('sqlite3').verbose();
function createWindow() {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
}
});
mainWindow.loadFile('index.html');
}
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
const db = new sqlite3.Database('userdata.db', (err) => {
if (err) {
console.error(err.message);
}
console.log('Connected to the userdata database.');
db.run(`CREATE TABLE IF NOT EXISTS tb_user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT,
age INTEGER
)`);
});
ipcMain.on('create-user', (event, username, age) => {
db.run(`INSERT INTO tb_user(username, age) VALUES(?, ?)`, [username, age]);
});
ipcMain.handle('get-users', async (event) => {
return new Promise((resolve, reject) => {
db.all(`SELECT * FROM tb_user`, [], (err, rows) => {
if (err) {
reject(err);
}
resolve(rows);
});
});
});
ipcMain.on('update-user', (event, id, username, age) => {
db.run(`UPDATE tb_user SET username = ?, age = ? WHERE id = ?`, [username, age, id]);
});
ipcMain.on('delete-user', (event, id) => {
db.run(`DELETE FROM tb_user WHERE id = ?`, id);
});
IPC API
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld(
'api', {
createUser: (username, age) => ipcRenderer.send('create-user', username, age),
getUsers: () => ipcRenderer.invoke('get-users'),
updateUser: (id, username, age) => ipcRenderer.send('update-user', id, username, age),
deleteUser: (id) => ipcRenderer.send('delete-user', id)
}
);