网络请求
使用 Node.js 原生 fetch
Node.js 18+ 原生支持 fetch:
// 主进程网络请求
async function fetchData(url) {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const data = await response.json();
return data;
} catch (err) {
console.error('Network error:', err);
throw err;
}
}
// 带认证的请求
async function fetchWithAuth(url, token) {
const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
return response.json();
}
// POST 请求
async function postData(url, data) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
return response.json();
}
使用 axios(推荐)
axios 是最常用的 HTTP 客户端:
const axios = require('axios');
// 安装:npm install axios
async function getData() {
const response = await axios.get('https://api.example.com/data', {
timeout: 10000, // 10秒超时
headers: {
'User-Agent': 'MyElectronApp/1.0'
}
});
return response.data;
}
async function postData(url, data) {
const response = await axios.post(url, data, {
timeout: 10000,
headers: {
'Authorization': 'Bearer token'
}
});
return response.data;
}
// 请求拦截器
axios.interceptors.request.use(config => {
console.log('Request:', config.url);
return config;
});
// 响应拦截器
axios.interceptors.response.use(
response => response,
error => {
console.error('Response error:', error.message);
return Promise.reject(error);
}
);
代理设置
如果需要通过代理访问网络:
const axios = require('axios');
const proxyAgent = new (require('https-proxy-agent'))('http://proxy.example.com:8080');
const response = await axios.get('https://api.example.com/data', {
httpAgent: proxyAgent,
httpsAgent: proxyAgent
});
本地数据库
SQLite 与 better-sqlite3
better-sqlite3 是同步的 SQLite 绑定,性能优秀:
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);
// 启用 WAL 模式,提升并发性能
db.pragma('journal_mode = WAL');
创建表
function initializeDatabase() {
// 创建用户表
db.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
// 创建设置表
db.exec(`
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
)
`);
// 创建文章表
db.exec(`
CREATE TABLE IF NOT EXISTS articles (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT,
status TEXT DEFAULT 'draft',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
}
插入数据
// 插入单条
const insertUser = db.prepare(`
INSERT INTO users (name, email) VALUES (?, ?)
`);
const result = insertUser.run('张三', '[email protected]');
console.log('Inserted user ID:', result.lastInsertRowid);
// 插入多条(事务)
function insertUsersBatch(users) {
const insert = db.prepare(`
INSERT INTO users (name, email) VALUES (?, ?)
`);
const transaction = db.transaction((items) => {
for (const user of items) {
insert.run(user.name, user.email);
}
});
transaction(users);
console.log(`Inserted ${users.length} users`);
}
查询数据
// 查询单条
const getUser = db.prepare(`
SELECT * FROM users WHERE id = ?
`);
const user = getUser.get(1);
console.log(user);
// 查询多条
const getAllUsers = db.prepare('SELECT * FROM users');
const users = getAllUsers.all();
// 带条件查询
const searchUsers = db.prepare(`
SELECT * FROM users WHERE name LIKE ? ORDER BY created_at DESC
`);
const results = searchUsers.all('%张%');
// 聚合查询
const countUsers = db.prepare('SELECT COUNT(*) as count FROM users');
const { count } = countUsers.get();
更新和删除
// 更新
const updateUser = db.prepare(`
UPDATE users SET email = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?
`);
updateUser.run('[email protected]', 1);
// 删除
const deleteUser = db.prepare('DELETE FROM users WHERE id = ?');
deleteUser.run(1);
数据库迁移
const migrations = [
{
version: 1,
up: `
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT
)
`
},
{
version: 2,
up: `
ALTER TABLE users ADD COLUMN email TEXT;
`
},
{
version: 3,
up: `
CREATE TABLE settings (
key TEXT PRIMARY KEY,
value TEXT
)
`
}
];
function runMigrations() {
// 创建版本表
db.exec(`
CREATE TABLE IF NOT EXISTS schema_migrations (
version INTEGER PRIMARY KEY
)
`);
const currentVersion = db.prepare('SELECT MAX(version) as v FROM schema_migrations').get().v || 0;
for (const migration of migrations) {
if (migration.version > currentVersion) {
console.log(`Running migration ${migration.version}`);
db.exec(migration.up);
db.prepare('INSERT INTO schema_migrations (version) VALUES (?)').run(migration.version);
}
}
}
electron-store(键值存储)
electron-store 是轻量级的 JSON 存储方案:
const Store = require('electron-store');
// 定义 schema
const store = new Store({
name: 'config',
defaults: {
window: {
width: 1200,
height: 800,
x: undefined,
y: undefined
},
theme: 'light',
autoSave: true,
recentFiles: []
}
});
// 使用
store.get('window.width'); // 1200
store.set('theme', 'dark');
store.get('theme'); // 'dark'
// 获取嵌套属性
store.get('window.bounds.x');
// 删除
store.delete('recentFiles');
// 检查是否存在
store.has('theme'); // true
electron-store 高级用法
const Store = require('electron-store');
const schema = {
settings: {
type: 'object',
properties: {
theme: {
type: 'string',
enum: ['light', 'dark', 'system'],
default: 'system'
},
fontSize: {
type: 'number',
minimum: 10,
maximum: 24,
default: 14
}
},
default: {}
}
};
const store = new Store({ schema });
// 监听变化
store.onDidChange('settings.theme', (newValue, oldValue) => {
console.log(`Theme changed from ${oldValue} to ${newValue}`);
});
// 清除所有
store.clear();
IndexedDB(浏览器存储)
在渲染进程中使用 IndexedDB:
// renderer.js - 打开数据库
function openDatabase() {
return new Promise((resolve, reject) => {
const request = indexedDB.open('MyAppDB', 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
request.onupgradeneeded = (event) => {
const db = event.target.result;
// 创建对象存储
if (!db.objectStoreNames.contains('documents')) {
const store = db.createObjectStore('documents', {
keyPath: 'id',
autoIncrement: true
});
store.createIndex('title', 'title', { unique: false });
store.createIndex('createdAt', 'createdAt', { unique: false });
}
if (!db.objectStoreNames.contains('cache')) {
db.createObjectStore('cache', { keyPath: 'url' });
}
};
});
}
IndexedDB CRUD 操作
// 添加
async function addDocument(db, doc) {
return new Promise((resolve, reject) => {
const tx = db.transaction('documents', 'readwrite');
const store = tx.objectStore('documents');
const request = store.add({ ...doc, createdAt: new Date() });
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
// 查询
async function getDocument(db, id) {
return new Promise((resolve, reject) => {
const tx = db.transaction('documents', 'readonly');
const store = tx.objectStore('documents');
const request = store.get(id);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
// 更新
async function updateDocument(db, doc) {
return new Promise((resolve, reject) => {
const tx = db.transaction('documents', 'readwrite');
const store = tx.objectStore('documents');
const request = store.put({ ...doc, updatedAt: new Date() });
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
// 删除
async function deleteDocument(db, id) {
return new Promise((resolve, reject) => {
const tx = db.transaction('documents', 'readwrite');
const store = tx.objectStore('documents');
const request = store.delete(id);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
// 查询所有
async function getAllDocuments(db) {
return new Promise((resolve, reject) => {
const tx = db.transaction('documents', 'readonly');
const store = tx.objectStore('documents');
const request = store.getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
缓存策略
内存缓存
class MemoryCache {
constructor(maxSize = 100, ttl = 3600000) {
this.cache = new Map();
this.maxSize = maxSize;
this.ttl = ttl; // 毫秒
}
set(key, value) {
if (this.cache.size >= this.maxSize) {
// 删除最老的条目
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, {
value,
timestamp: Date.now()
});
}
get(key) {
const item = this.cache.get(key);
if (!item) return null;
// 检查是否过期
if (Date.now() - item.timestamp > this.ttl) {
this.cache.delete(key);
return null;
}
return item.value;
}
clear() {
this.cache.clear();
}
}
HTTP 缓存
const { session } = require('electron');
// 配置会话缓存
session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
// 可以在这里添加自定义请求头
callback({
cancel: false,
requestHeaders: {
...details.requestHeaders,
'X-Custom-Header': 'value'
}
});
});
// 配置响应缓存
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
cancel: false,
responseHeaders: {
...details.responseHeaders,
'Cache-Control': ['max-age=3600']
}
});
});
数据同步策略
离线优先
class OfflineFirstSync {
constructor(db, api) {
this.db = db;
this.api = api;
this.syncQueue = [];
}
async save(doc) {
// 先保存到本地
await this.db.put('documents', doc);
// 加入同步队列
this.syncQueue.push({
action: 'save',
doc,
timestamp: Date.now()
});
// 尝试同步
this.trySync();
}
async trySync() {
if (!navigator.onLine) return;
while (this.syncQueue.length > 0) {
const item = this.syncQueue[0];
try {
await this.api.sync(item);
this.syncQueue.shift();
} catch (err) {
console.error('Sync failed:', err);
break;
}
}
}
}
// 监听网络状态
window.addEventListener('online', () => {
syncManager.trySync();
});
这一章想说的
Electron 应用的网络和数据存储:
- 网络请求:使用原生 fetch 或 axios,主进程可配置代理
- SQLite:better-sqlite3 提供高性能的本地数据库
- 键值存储:electron-store 适合配置和轻量数据
- IndexedDB:适合渲染进程中的结构化数据存储
- 缓存策略:内存缓存 + HTTP 缓存 + 离线优先同步
根据数据特点和访问模式选择合适的存储方案。