electron-vite使用better-sqlite3

安装

pnpm install better-sqlite3 --save

配置

开发环境

在根目录下创建个文件夹用来存放生成的sql文件,这个sql文件加到gitignore中,不要把sql文件放到out目录里,out是开发输出,每次重启会清空。

生产环境

生产环境写到用户目录下(这样每次更新历史数据不会清空,同时会引入一个新的问题,当数据表和结构变化时需要做迁移操作)

app.getPath('userData');

不同操作系统下的路径
mac: ~/Library/ApplicationSupport/应用名称/
linux:~/.config/应用名称
windows: C:\Users\用户\AppData\Roaming\应用名称
打包的时候不要把本地的数据库文件和其他开发环境下的文件打到包里

使用

在主进程中

  1. 初始化文件目录
function createDatabaseDir(dirPath: string) {
  // Check if the directory exists, if not, create it
  if (!existsSync(dirPath)) {
    mkdirSync(dirPath, { recursive: true });
  }
}
  1. 初始化表结构
function initDatabase(db: Database.Database, schemaPath: string) {
  // 此处从schema.sql文件中初始化,也可以给字符串初始化
  const schema = readFileSync(schemaPath, 'utf8');
  db.exec(schema);
  logger.info('Database does not exist, creating...');
  return db;
}
  1. 细分数据操作
    例如 用户的数据操作,可以给个userservice文件用来处理用户表的操作
import Database from 'better-sqlite3';

export class UserService {
  private db: Database.Database;

  constructor(db: Database.Database) {
    this.db = db;
  }

  public getUsers() {
    const stmt = this.db.prepare('SELECT * FROM users WHERE is_deleted = 0');
    return stmt.all();
  }

  public addUser(name: string, email: string) {
    const stmt = this.db.prepare(`
        INSERT INTO users (name, email, created_at, updated_at)
        VALUES (?, ?, datetime('now'), datetime('now'))
    `);
    stmt.run(name, email);
  }
  public updateUser(id: string, newName: string, newEmail: string) {
    const stmt = this.db.prepare(`
        UPDATE users
        SET name = ?, email = ?, updated_at = datetime('now')
        WHERE id = ?
    `);
    stmt.run(newName, newEmail, id);
  }
  public deleteUserById(id: string) {
    const stmt = this.db.prepare(`
        UPDATE users 
        SET is_deleted = 1, updated_at = datetime('now')
        WHERE id = ?
    `);
    stmt.run(id);
  }
}
  1. 创建IPC通讯供renderer进程使用
    4.1 创建个userhandle.ts文件并在main/index.ts中初始化handle
import { ipcMain } from 'electron';
import databaseService from '..';

export function setupUserIpcHandlers() {
  // IPC 处理
  ipcMain.handle('get-users', async () => {
    return databaseService.userService.getUsers();
  });

  ipcMain.handle('add-user', async (_, name: string, email: string) => {
    databaseService.userService.addUser(name, email);
  });

  ipcMain.handle('update-user', async (_, id: string, newName: string, newEmail: string) => {
    databaseService.userService.updateUser(id, newName, newEmail);
  });

  ipcMain.handle('delete-user-by-id', async (_, id: string) => {
    databaseService.userService.deleteUserById(id);
  });
}
app.whenReady().then(() => {
  // Set app user model id for windows
  electronApp.setAppUserModelId('com.electron');

  // Default open or close DevTools by F12 in development
  // and ignore CommandOrControl + R in production.
  // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
  app.on('browser-window-created', (_, window) => {
    optimizer.watchWindowShortcuts(window);
  });

  createWindow();

  app.on('activate', function () {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
  setupLoggerIpcHandlers();// 初始化日志IPC
  setupDatabaseIpcHandlers();//初始化dbIPC
});

4.2 在preload中创建invoke
创建userinvoke.ts

import { ipcRenderer } from 'electron';

export const databasePreload = {
  getUsers: () => ipcRenderer.invoke('get-users'),
  addUser: (name: string, email: string) => ipcRenderer.invoke('add-user', name, email),
  deleteUserById: (id: string) => ipcRenderer.invoke('delete-user-by-id', id),
  updateUser: (id: string, name: string, email: string) => ipcRenderer.invoke('update-user', id, name, email)
};

4.3 在preload/index.ts引入

import { contextBridge } from 'electron';
import { electronAPI } from '@electron-toolkit/preload';
import { databasePreload } from './database';
import { loggerPreload } from './logger';

// Custom APIs for renderer
const api = {
  ...loggerPreload,
  ...databasePreload
};

// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
// just add to the DOM global.
if (process.contextIsolated) {
  try {
    contextBridge.exposeInMainWorld('electron', electronAPI);
    contextBridge.exposeInMainWorld('api', api);
  } catch (error) {
    console.error(2, error);
  }
} else {
  // @ts-ignore (define in dts)
  window.electron = electronAPI;
  // @ts-ignore (define in dts)
  window.api = api;
}

在renderer进程中使用

通过window.api的方式调用preload中定义的invoke方法

  try {
    await window.api.addUser(newUserName.value, newUserEmail.value);
    newUserName.value = '';
    newUserEmail.value = '';
    fetchUsers();
  } catch (error) {
    window.api.logError(error);
  }

其他

  1. 前文提到的数据库表结构变化涉及到客户端侧的数据库需要做迁移,目前想到的是用knex自动化迁移但是暂时没成功。
  2. 数据的备份也要加。
  3. 其他主进程中的操作要供renderer操作的方法都可以用上述思路。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值