本地存储 #

一、存储方案概述 #

1.1 存储方案对比 #

方案 容量 同步/异步 适用场景
localStorage ~5MB 同步 简单键值对
sessionStorage ~5MB 同步 临时会话数据
IndexedDB 大量 异步 结构化数据
electron-store 无限制 同步/异步 应用配置
文件系统 无限制 异步 大文件存储

1.2 存储位置 #

javascript
const { app } = require('electron');

// 用户数据目录
console.log('用户数据:', app.getPath('userData'));
// Windows: C:\Users\<user>\AppData\Roaming\<appName>
// macOS: ~/Library/Application Support/<appName>
// Linux: ~/.config/<appName>

// 应用数据目录
console.log('应用数据:', app.getPath('appData'));

// 临时目录
console.log('临时目录:', app.getPath('temp'));

二、Web 存储 API #

2.1 localStorage #

javascript
// 渲染进程中使用

// 存储
localStorage.setItem('username', 'John');
localStorage.setItem('settings', JSON.stringify({ theme: 'dark' }));

// 读取
const username = localStorage.getItem('username');
const settings = JSON.parse(localStorage.getItem('settings'));

// 删除
localStorage.removeItem('username');

// 清空
localStorage.clear();

// 监听变化
window.addEventListener('storage', (e) => {
    console.log('Key:', e.key);
    console.log('Old:', e.oldValue);
    console.log('New:', e.newValue);
});

2.2 sessionStorage #

javascript
// 会话级存储,窗口关闭后清除

// 存储
sessionStorage.setItem('tempData', 'value');

// 读取
const tempData = sessionStorage.getItem('tempData');

// 删除
sessionStorage.removeItem('tempData');

// 清空
sessionStorage.clear();

2.3 存储封装 #

javascript
// storage.js
class Storage {
    constructor(prefix = 'app_') {
        this.prefix = prefix;
    }

    getKey(key) {
        return this.prefix + key;
    }

    set(key, value) {
        localStorage.setItem(this.getKey(key), JSON.stringify(value));
    }

    get(key, defaultValue = null) {
        const value = localStorage.getItem(this.getKey(key));
        return value ? JSON.parse(value) : defaultValue;
    }

    remove(key) {
        localStorage.removeItem(this.getKey(key));
    }

    clear() {
        const keys = Object.keys(localStorage);
        keys.forEach(key => {
            if (key.startsWith(this.prefix)) {
                localStorage.removeItem(key);
            }
        });
    }
}

const storage = new Storage();
module.exports = storage;

三、IndexedDB #

3.1 基本使用 #

javascript
// 打开数据库
const request = indexedDB.open('MyDatabase', 1);

request.onerror = (event) => {
    console.error('数据库打开失败');
};

request.onsuccess = (event) => {
    const db = event.target.result;
    console.log('数据库打开成功');
};

// 创建对象存储
request.onupgradeneeded = (event) => {
    const db = event.target.result;
    
    // 创建用户存储
    const userStore = db.createObjectStore('users', { keyPath: 'id' });
    userStore.createIndex('name', 'name', { unique: false });
    userStore.createIndex('email', 'email', { unique: true });
};

3.2 CRUD 操作 #

javascript
class IndexedDBHelper {
    constructor(dbName, version) {
        this.dbName = dbName;
        this.version = version;
        this.db = null;
    }

    async init() {
        return new Promise((resolve, reject) => {
            const request = indexedDB.open(this.dbName, this.version);
            
            request.onerror = () => reject(request.error);
            request.onsuccess = () => {
                this.db = request.result;
                resolve(this.db);
            };
            request.onupgradeneeded = (event) => {
                this.onUpgrade(event.target.result);
            };
        });
    }

    onUpgrade(db) {
        // 子类实现
    }

    async add(storeName, data) {
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([storeName], 'readwrite');
            const store = transaction.objectStore(storeName);
            const request = store.add(data);
            request.onsuccess = () => resolve(request.result);
            request.onerror = () => reject(request.error);
        });
    }

    async get(storeName, key) {
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([storeName], 'readonly');
            const store = transaction.objectStore(storeName);
            const request = store.get(key);
            request.onsuccess = () => resolve(request.result);
            request.onerror = () => reject(request.error);
        });
    }

    async getAll(storeName) {
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([storeName], 'readonly');
            const store = transaction.objectStore(storeName);
            const request = store.getAll();
            request.onsuccess = () => resolve(request.result);
            request.onerror = () => reject(request.error);
        });
    }

    async put(storeName, data) {
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([storeName], 'readwrite');
            const store = transaction.objectStore(storeName);
            const request = store.put(data);
            request.onsuccess = () => resolve(request.result);
            request.onerror = () => reject(request.error);
        });
    }

    async delete(storeName, key) {
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([storeName], 'readwrite');
            const store = transaction.objectStore(storeName);
            const request = store.delete(key);
            request.onsuccess = () => resolve();
            request.onerror = () => reject(request.error);
        });
    }
}

四、electron-store #

4.1 安装与使用 #

bash
npm install electron-store
javascript
const Store = require('electron-store');

const store = new Store();

// 存储
store.set('username', 'John');
store.set('settings', {
    theme: 'dark',
    language: 'zh-CN'
});

// 读取
const username = store.get('username');
const theme = store.get('settings.theme');

// 默认值
const fontSize = store.get('fontSize', 14);

// 删除
store.delete('username');

// 清空
store.clear();

// 检查是否存在
store.has('username');

// 获取所有数据
store.store;

// 获取存储路径
store.path;

4.2 配置选项 #

javascript
const store = new Store({
    // 配置文件名
    name: 'config',
    
    // 默认值
    defaults: {
        theme: 'light',
        language: 'zh-CN',
        window: {
            width: 800,
            height: 600
        }
    },
    
    // 加密密钥(可选)
    encryptionKey: 'my-secret-key',
    
    // 自定义路径
    cwd: app.getPath('userData'),
    
    // 文件扩展名
    fileExtension: 'json',
    
    // 是否在退出时保存
    accessPropertiesByDotNotation: true,
    
    // 验证配置
    schema: {
        theme: {
            type: 'string',
            enum: ['light', 'dark']
        },
        language: {
            type: 'string'
        }
    }
});

4.3 监听变化 #

javascript
// 监听所有变化
store.onDidAnyChange((newValue, oldValue) => {
    console.log('配置已变化');
});

// 监听特定键
store.onDidChange('theme', (newValue, oldValue) => {
    console.log(`主题从 ${oldValue} 变为 ${newValue}`);
});

4.4 多实例存储 #

javascript
// 用户配置
const userStore = new Store({ name: 'user' });

// 应用配置
const appStore = new Store({ name: 'app' });

// 缓存
const cacheStore = new Store({ name: 'cache' });

五、配置管理 #

5.1 配置管理器 #

javascript
// configManager.js
const Store = require('electron-store');

class ConfigManager {
    constructor() {
        this.store = new Store({
            name: 'config',
            defaults: {
                general: {
                    theme: 'light',
                    language: 'zh-CN',
                    autoStart: false,
                    minimizeToTray: true
                },
                window: {
                    width: 1024,
                    height: 768,
                    x: undefined,
                    y: undefined,
                    isMaximized: false
                },
                shortcuts: {
                    showWindow: 'CommandOrControl+Shift+W',
                    quickCapture: 'CommandOrControl+Shift+C'
                }
            }
        });
    }

    get(key, defaultValue) {
        return this.store.get(key, defaultValue);
    }

    set(key, value) {
        this.store.set(key, value);
    }

    getGeneral() {
        return this.store.get('general');
    }

    setGeneral(config) {
        this.store.set('general', { ...this.getGeneral(), ...config });
    }

    getWindowState() {
        return this.store.get('window');
    }

    setWindowState(state) {
        this.store.set('window', state);
    }

    reset() {
        this.store.clear();
    }
}

module.exports = new ConfigManager();

5.2 使用配置管理器 #

javascript
// main.js
const config = require('./configManager');

// 获取配置
const theme = config.get('general.theme');

// 设置配置
config.set('general.theme', 'dark');

// 保存窗口状态
config.setWindowState({
    width: mainWindow.getSize()[0],
    height: mainWindow.getSize()[1],
    x: mainWindow.getPosition()[0],
    y: mainWindow.getPosition()[1],
    isMaximized: mainWindow.isMaximized()
});

六、缓存管理 #

6.1 缓存实现 #

javascript
// cacheManager.js
const Store = require('electron-store');

class CacheManager {
    constructor() {
        this.store = new Store({ name: 'cache' });
    }

    set(key, value, ttl = 0) {
        const item = {
            value,
            timestamp: Date.now(),
            ttl
        };
        this.store.set(key, item);
    }

    get(key) {
        const item = this.store.get(key);
        
        if (!item) return null;
        
        // 检查是否过期
        if (item.ttl > 0 && Date.now() - item.timestamp > item.ttl) {
            this.delete(key);
            return null;
        }
        
        return item.value;
    }

    delete(key) {
        this.store.delete(key);
    }

    clear() {
        this.store.clear();
    }

    clearExpired() {
        const items = this.store.store;
        const now = Date.now();
        
        Object.keys(items).forEach(key => {
            const item = items[key];
            if (item.ttl > 0 && now - item.timestamp > item.ttl) {
                this.delete(key);
            }
        });
    }
}

module.exports = new CacheManager();

6.2 使用缓存 #

javascript
const cache = require('./cacheManager');

// 设置缓存,有效期 1 小时
cache.set('userData', userData, 3600000);

// 获取缓存
const userData = cache.get('userData');

// 清除过期缓存
cache.clearExpired();

七、数据迁移 #

7.1 版本迁移 #

javascript
class DataMigration {
    constructor(store) {
        this.store = store;
        this.currentVersion = 2;
    }

    migrate() {
        const version = this.store.get('version', 1);
        
        if (version < 2) {
            this.migrateToV2();
        }
        
        this.store.set('version', this.currentVersion);
    }

    migrateToV2() {
        // 迁移旧数据格式
        const oldTheme = this.store.get('theme');
        if (oldTheme) {
            this.store.set('general.theme', oldTheme);
            this.store.delete('theme');
        }
    }
}

// 使用
const migration = new DataMigration(store);
migration.migrate();

八、数据备份 #

8.1 备份与恢复 #

javascript
const fs = require('fs');
const path = require('path');

class DataBackup {
    constructor(store) {
        this.store = store;
        this.backupDir = path.join(app.getPath('userData'), 'backups');
        
        if (!fs.existsSync(this.backupDir)) {
            fs.mkdirSync(this.backupDir, { recursive: true });
        }
    }

    backup(name) {
        const backupPath = path.join(this.backupDir, `${name}.json`);
        const data = JSON.stringify(this.store.store, null, 2);
        fs.writeFileSync(backupPath, data);
        return backupPath;
    }

    restore(name) {
        const backupPath = path.join(this.backupDir, `${name}.json`);
        if (fs.existsSync(backupPath)) {
            const data = JSON.parse(fs.readFileSync(backupPath, 'utf-8'));
            this.store.store = data;
            return true;
        }
        return false;
    }

    listBackups() {
        return fs.readdirSync(this.backupDir)
            .filter(f => f.endsWith('.json'))
            .map(f => f.replace('.json', ''));
    }

    deleteBackup(name) {
        const backupPath = path.join(this.backupDir, `${name}.json`);
        if (fs.existsSync(backupPath)) {
            fs.unlinkSync(backupPath);
        }
    }
}

九、最佳实践 #

9.1 存储策略选择 #

javascript
// 根据数据类型选择存储方案
function chooseStorage(data) {
    // 简单配置:electron-store
    // 大量数据:IndexedDB
    // 临时数据:sessionStorage
    // 大文件:文件系统
}

9.2 数据加密 #

javascript
const crypto = require('crypto');

function encrypt(text, key) {
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
    let encrypted = cipher.update(text);
    encrypted = Buffer.concat([encrypted, cipher.final()]);
    return iv.toString('hex') + ':' + encrypted.toString('hex');
}

function decrypt(text, key) {
    const [ivHex, encryptedHex] = text.split(':');
    const iv = Buffer.from(ivHex, 'hex');
    const encrypted = Buffer.from(encryptedHex, 'hex');
    const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
    let decrypted = decipher.update(encrypted);
    decrypted = Buffer.concat([decrypted, decipher.final()]);
    return decrypted.toString();
}

十、总结 #

10.1 核心要点 #

要点 说明
localStorage 简单键值对存储
IndexedDB 结构化大数据存储
electron-store 推荐的应用配置存储
配置管理 统一管理应用配置
数据迁移 版本升级时迁移数据

10.2 下一步 #

现在你已经掌握了本地存储,接下来让我们学习 文件系统操作,深入了解 Node.js 文件操作 API!

最后更新:2026-03-28