乐闻世界logo
搜索文章和话题

Electron 数据持久化方案

2月17日 23:54

在 Electron 应用中,数据持久化是常见需求,包括用户设置、应用数据、缓存等。本文将详细介绍 Electron 中的各种数据持久化方案。

本地存储方案

1. localStorage

localStorage 是最简单的存储方案,适合存储少量数据。

javascript
// renderer.js // 存储数据 localStorage.setItem('userSettings', JSON.stringify({ theme: 'dark', language: 'zh-CN', fontSize: 14 })) // 读取数据 const settings = JSON.parse(localStorage.getItem('userSettings')) console.log(settings) // 删除数据 localStorage.removeItem('userSettings') // 清空所有数据 localStorage.clear()

优点:

  • 简单易用
  • 同步 API
  • 数据持久化

缺点:

  • 存储容量限制(通常 5-10MB)
  • 只能存储字符串
  • 不适合大量数据

2. sessionStorage

sessionStorage 类似于 localStorage,但数据在页面会话结束时清除。

javascript
// renderer.js // 存储临时数据 sessionStorage.setItem('tempData', JSON.stringify({ token: 'abc123', timestamp: Date.now() })) // 读取数据 const tempData = JSON.parse(sessionStorage.getItem('tempData'))

适用场景:

  • 临时会话数据
  • 页面间传递数据
  • 不需要持久化的数据

3. IndexedDB

IndexedDB 是浏览器提供的强大数据库,适合存储大量结构化数据。

javascript
// renderer.js // 打开数据库 const request = indexedDB.open('MyAppDB', 1) request.onerror = (event) => { console.error('Database error:', event.target.error) } request.onsuccess = (event) => { const db = event.target.result console.log('Database opened successfully') } request.onupgradeneeded = (event) => { const db = event.target.result // 创建对象存储 const objectStore = db.createObjectStore('users', { keyPath: 'id', autoIncrement: true }) // 创建索引 objectStore.createIndex('name', 'name', { unique: false }) objectStore.createIndex('email', 'email', { unique: true }) } // 添加数据 function addUser(db, userData) { const transaction = db.transaction(['users'], 'readwrite') const objectStore = transaction.objectStore('users') const request = objectStore.add(userData) request.onsuccess = () => { console.log('User added successfully') } request.onerror = () => { console.error('Error adding user:', request.error) } } // 查询数据 function getUser(db, userId) { const transaction = db.transaction(['users'], 'readonly') const objectStore = transaction.objectStore('users') const request = objectStore.get(userId) request.onsuccess = () => { const user = request.result console.log('User:', user) } } // 更新数据 function updateUser(db, userData) { const transaction = db.transaction(['users'], 'readwrite') const objectStore = transaction.objectStore('users') const request = objectStore.put(userData) request.onsuccess = () => { console.log('User updated successfully') } } // 删除数据 function deleteUser(db, userId) { const transaction = db.transaction(['users'], 'readwrite') const objectStore = transaction.objectStore('users') const request = objectStore.delete(userId) request.onsuccess = () => { console.log('User deleted successfully') } }

优点:

  • 存储容量大(通常几百 MB)
  • 支持事务
  • 支持索引查询
  • 异步操作,不阻塞 UI

缺点:

  • API 相对复杂
  • 异步操作需要回调或 Promise

4. 使用 Dexie.js 简化 IndexedDB

Dexie.js 是 IndexedDB 的封装库,提供了更简洁的 API。

javascript
// renderer.js import Dexie from 'dexie' // 创建数据库 const db = new Dexie('MyAppDB') // 定义表结构 db.version(1).stores({ users: '++id, name, email', posts: '++id, title, userId', comments: '++id, content, postId' }) // 添加数据 async function addUser(name, email) { try { const id = await db.users.add({ name, email }) console.log('User added with ID:', id) return id } catch (error) { console.error('Error adding user:', error) } } // 查询数据 async function getAllUsers() { try { const users = await db.users.toArray() return users } catch (error) { console.error('Error fetching users:', error) } } // 条件查询 async function getUserByEmail(email) { try { const user = await db.users.where('email').equals(email).first() return user } catch (error) { console.error('Error fetching user:', error) } } // 更新数据 async function updateUser(id, updates) { try { await db.users.update(id, updates) console.log('User updated successfully') } catch (error) { console.error('Error updating user:', error) } } // 删除数据 async function deleteUser(id) { try { await db.users.delete(id) console.log('User deleted successfully') } catch (error) { console.error('Error deleting user:', error) } }

文件系统存储

1. 使用 Node.js fs 模块

在主进程中可以使用 Node.js 的 fs 模块进行文件操作。

javascript
// main.js const fs = require('fs').promises const path = require('path') const { app } = require('electron') // 获取用户数据目录 const userDataPath = app.getPath('userData') const dataFilePath = path.join(userDataPath, 'data.json') // 保存数据 async function saveData(data) { try { await fs.writeFile(dataFilePath, JSON.stringify(data, null, 2), 'utf-8') console.log('Data saved successfully') } catch (error) { console.error('Error saving data:', error) } } // 读取数据 async function loadData() { try { const data = await fs.readFile(dataFilePath, 'utf-8') return JSON.parse(data) } catch (error) { if (error.code === 'ENOENT') { // 文件不存在,返回默认数据 return {} } console.error('Error loading data:', error) return null } } // 删除数据 async function deleteData() { try { await fs.unlink(dataFilePath) console.log('Data deleted successfully') } catch (error) { console.error('Error deleting data:', error) } }

2. 使用 electron-store

electron-store 是专门为 Electron 设计的简单数据存储库。

bash
npm install electron-store
javascript
// main.js const Store = require('electron-store') const store = new Store({ defaults: { settings: { theme: 'light', language: 'en', fontSize: 14 }, userData: {} } }) // 保存数据 store.set('settings.theme', 'dark') store.set('userData.name', 'John Doe') // 读取数据 const theme = store.get('settings.theme') const userData = store.get('userData') // 获取所有数据 const allData = store.store // 删除数据 store.delete('userData.name') // 清空数据 store.clear() // 检查数据是否存在 const hasTheme = store.has('settings.theme') // 监听数据变化 store.onDidChange('settings.theme', (newValue, oldValue) => { console.log('Theme changed from', oldValue, 'to', newValue) })

3. 使用 lowdb

lowdb 是一个轻量级的 JSON 数据库。

bash
npm install lowdb
javascript
// main.js const Low = require('lowdb') const FileSync = require('lowdb/adapters/FileSync') const path = require('path') const { app } = require('electron') // 创建数据库 const adapter = new FileSync(path.join(app.getPath('userData'), 'db.json')) const db = new Low(adapter) // 初始化默认数据 db.defaults({ users: [], posts: [], settings: { theme: 'light', language: 'en' } }).write() // 添加数据 db.get('users') .push({ id: 1, name: 'John', email: 'john@example.com' }) .write() // 查询数据 const users = db.get('users').value() const user = db.get('users').find({ id: 1 }).value() // 更新数据 db.get('users') .find({ id: 1 }) .assign({ name: 'John Doe' }) .write() // 删除数据 db.get('users') .remove({ id: 1 }) .write() // 链式查询 const activeUsers = db.get('users') .filter({ status: 'active' }) .orderBy('name') .value()

SQLite 数据库

1. 使用 better-sqlite3

better-sqlite3 是一个同步的 SQLite 绑定,性能优异。

bash
npm install better-sqlite3
javascript
// main.js const Database = require('better-sqlite3') const path = require('path') const { app } = require('electron') // 创建数据库连接 const dbPath = path.join(app.getPath('userData'), 'app.db') const db = new Database(dbPath) // 创建表 db.exec(` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT UNIQUE NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) CREATE TABLE IF NOT EXISTS posts ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT, user_id INTEGER, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ) `) // 插入数据 function insertUser(name, email) { const stmt = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)') const result = stmt.run(name, email) return result.lastInsertRowid } // 查询数据 function getAllUsers() { const stmt = db.prepare('SELECT * FROM users') return stmt.all() } function getUserById(id) { const stmt = db.prepare('SELECT * FROM users WHERE id = ?') return stmt.get(id) } function getUserByEmail(email) { const stmt = db.prepare('SELECT * FROM users WHERE email = ?') return stmt.get(email) } // 更新数据 function updateUser(id, updates) { const stmt = db.prepare('UPDATE users SET name = ?, email = ? WHERE id = ?') const result = stmt.run(updates.name, updates.email, id) return result.changes } // 删除数据 function deleteUser(id) { const stmt = db.prepare('DELETE FROM users WHERE id = ?') const result = stmt.run(id) return result.changes } // 事务处理 function transferFunds(fromUserId, toUserId, amount) { const insertStmt = db.prepare('INSERT INTO transactions (from_user, to_user, amount) VALUES (?, ?, ?)') const updateStmt = db.prepare('UPDATE users SET balance = balance - ? WHERE id = ?') const transaction = db.transaction((from, to, amt) => { updateStmt.run(amt, from) updateStmt.run(-amt, to) insertStmt.run(from, to, amt) }) transaction(fromUserId, toUserId, amount) }

2. 使用 Knex.js

Knex.js 是一个 SQL 查询构建器,支持多种数据库。

bash
npm install knex sqlite3
javascript
// main.js const knex = require('knex')({ client: 'sqlite3', connection: { filename: path.join(app.getPath('userData'), 'app.db') }, useNullAsDefault: true }) // 创建表 knex.schema .createTableIfNotExists('users', (table) => { table.increments('id').primary() table.string('name').notNullable() table.string('email').unique().notNullable() table.timestamps(true, true) }) .createTableIfNotExists('posts', (table) => { table.increments('id').primary() table.string('title').notNullable() table.text('content') table.integer('user_id').references('id').inTable('users') table.timestamps(true, true) }) .then(() => { console.log('Tables created successfully') }) // 插入数据 async function insertUser(name, email) { try { const [id] = await knex('users').insert({ name, email }) return id } catch (error) { console.error('Error inserting user:', error) } } // 查询数据 async function getAllUsers() { try { const users = await knex('users').select('*') return users } catch (error) { console.error('Error fetching users:', error) } } async function getUserById(id) { try { const user = await knex('users').where({ id }).first() return user } catch (error) { console.error('Error fetching user:', error) } } // 更新数据 async function updateUser(id, updates) { try { const count = await knex('users').where({ id }).update(updates) return count } catch (error) { console.error('Error updating user:', error) } } // 删除数据 async function deleteUser(id) { try { const count = await knex('users').where({ id }).del() return count } catch (error) { console.error('Error deleting user:', error) } } // 关联查询 async function getUserWithPosts(userId) { try { const user = await knex('users') .leftJoin('posts', 'users.id', 'posts.user_id') .where('users.id', userId) .select('users.*', 'posts.title as post_title') return user } catch (error) { console.error('Error fetching user with posts:', error) } }

数据同步方案

1. 本地数据同步到云端

javascript
// main.js const axios = require('axios') async function syncDataToCloud(localData) { try { const response = await axios.post('https://api.example.com/sync', { data: localData, timestamp: Date.now() }) return response.data } catch (error) { console.error('Sync failed:', error) throw error } } async function syncDataFromCloud() { try { const response = await axios.get('https://api.example.com/sync') return response.data } catch (error) { console.error('Sync failed:', error) throw error } }

2. 冲突解决

javascript
// main.js function resolveConflict(localData, remoteData) { const localTimestamp = localData.timestamp || 0 const remoteTimestamp = remoteData.timestamp || 0 if (localTimestamp > remoteTimestamp) { return localData } else if (remoteTimestamp > localTimestamp) { return remoteData } else { // 时间戳相同,使用合并策略 return { ...localData, ...remoteData } } }

最佳实践

1. 数据加密

javascript
// main.js const crypto = require('crypto') function encryptData(data, key) { const algorithm = 'aes-256-cbc' const iv = crypto.randomBytes(16) const cipher = crypto.createCipheriv(algorithm, key, iv) let encrypted = cipher.update(JSON.stringify(data), 'utf8', 'hex') encrypted += cipher.final('hex') return iv.toString('hex') + ':' + encrypted } function decryptData(encryptedData, key) { const algorithm = 'aes-256-cbc' const parts = encryptedData.split(':') const iv = Buffer.from(parts.shift(), 'hex') const encrypted = parts.join(':') const decipher = crypto.createDecipheriv(algorithm, key, iv) let decrypted = decipher.update(encrypted, 'hex', 'utf8') decrypted += decipher.final('utf8') return JSON.parse(decrypted) }

2. 数据备份

javascript
// main.js const fs = require('fs').promises const path = require('path') const { app } = require('electron') async function backupData() { const userDataPath = app.getPath('userData') const backupPath = path.join(userDataPath, 'backups', Date.now().toString()) try { await fs.mkdir(backupPath, { recursive: true }) // 复制数据文件 const files = await fs.readdir(userDataPath) for (const file of files) { if (!file.startsWith('backup')) { await fs.copyFile( path.join(userDataPath, file), path.join(backupPath, file) ) } } console.log('Backup created successfully') } catch (error) { console.error('Backup failed:', error) } }

3. 数据迁移

javascript
// main.js const migrations = [ { version: 1, migrate: (data) => { // 版本 1 的迁移逻辑 return { ...data, version: 1 } } }, { version: 2, migrate: (data) => { // 版本 2 的迁移逻辑 data.users = data.users || [] return { ...data, version: 2 } } } ] async function runMigrations(data) { const currentVersion = data.version || 0 for (const migration of migrations) { if (migration.version > currentVersion) { data = migration.migrate(data) } } return data }

常见问题

Q: 如何选择合适的数据持久化方案?A: 根据数据量和复杂度选择:

  • 少量简单数据: localStorage
  • 中等结构化数据: IndexedDB 或 electron-store
  • 大量复杂数据: SQLite 或其他数据库

Q: 如何处理数据加密?A: 使用 Node.js 的 crypto 模块进行加密,确保密钥安全存储。

Q: 如何实现数据同步?A: 使用时间戳和版本号实现冲突检测和解决,定期同步本地和云端数据。

Q: 如何优化数据库性能?A: 使用索引、批量操作、事务处理、连接池等技术优化数据库性能。

标签:Electron