在桌面应用中,SQLite因其轻量、嵌入式特性成为本地存储的热门选择。但若重复初始化数据库,会导致数据覆盖或冗余。本文将详解如何让Electron应用仅在首次启动时创建SQLite数据库,后续启动直接连接现有库。
一、核心逻辑与实现原理
核心思路:通过检测数据库文件是否存在,决定是否执行建表操作。
关键技术点:
- 路径管理:使用Electron的
app.getPath('userData')
获取用户数据目录,确保数据库文件持久化存储。 - 文件存在性检查:通过Node.js的
fs.existsSync()
判断数据库文件是否已创建。 - 条件化初始化:仅当文件不存在时,执行建表SQL语句。
二、分步实现代码
以下以主进程(main.js
)为例,整合sqlite3
和path
模块:
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
const fs = require('fs');
const sqlite3 = require('sqlite3').verbose();
function createWindow() {
// 窗口创建逻辑
}
app.whenReady().then(() => {
// 定义数据库路径(用户数据目录下)
const userDataPath = app.getPath('userData');
const dbPath = path.join(userDataPath, 'app_database.db');
// 关键逻辑:仅在文件不存在时初始化数据库
if (!fs.existsSync(dbPath)) {
const db = new sqlite3.Database(dbPath, (err) => {
if (err) console.error('Database creation failed:', err);
else {
// 执行建表语句
db.run(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE
)
`, (err) => {
if (err) console.error('Table creation error:', err);
else console.log('Database & tables initialized!');
});
}
});
} else {
console.log('Existing database connected.');
}
createWindow();
});
三、关键优化与注意事项
- 避免重复初始化
- 使用
CREATE TABLE IF NOT EXISTS
代替CREATE TABLE
,防止后续运行时误删表。
- 使用
- 异步安全
- 数据库操作需封装在
app.whenReady()
内,确保Electron初始化完成后再访问文件系统。
- 数据库操作需封装在
- 路径动态生成
- 开发环境与生产环境的路径差异需通过
app.isPackaged
区分。
- 开发环境与生产环境的路径差异需通过
- 错误处理
- 封装Promise或
try/catch
捕获文件操作及SQL执行异常。
- 封装Promise或
四、进阶场景:封装为可复用模块
将数据库逻辑独立为database.js
模块:
// database.js
const initDatabase = () => {
const dbPath = path.join(app.getPath('userData'), 'app.db');
if (!fs.existsSync(dbPath)) {
const db = new sqlite3.Database(dbPath);
db.exec(`
CREATE TABLE settings (key TEXT PRIMARY KEY, value TEXT);
INSERT INTO settings (key, value) VALUES ('first_run', 'true');
`);
return db;
}
return new sqlite3.Database(dbPath); // 返回现有连接
};
module.exports = { initDatabase };
主进程中调用:
const { initDatabase } = require('./database');
app.whenReady().then(() => {
const db = initDatabase();
// 其他逻辑
});
五、常见问题排查
问题 | 解决方案 |
---|---|
安装sqlite3 编译失败 |
添加electron-rebuild 并指定target版本 |
打包后数据库文件未生成 | 在package.json 中配置extraResources 拷贝初始文件 |
渲染进程无法访问数据库 | 通过ipcMain 暴露接口,禁止直接跨进程操作 |
六、替代方案对比
方案 | 适用场景 | 缺点 |
---|---|---|
SQLite | 需复杂查询、事务支持 | 需处理原生模块编译 |
JSON文件存储 | 简单键值对、低数据量 | 性能差,无SQL能力 |
Browser IndexedDB | 纯前端存储,无Node依赖 | 容量限制,无复杂查询 |