本地存储 #
一、存储方案概述 #
1.1 存储方案对比 #
| 方案 | 容量 | 持久化 | 性能 | 使用场景 |
|---|---|---|---|---|
| localStorage | ~5MB | 是 | 高 | 简单键值存储 |
| sessionStorage | ~5MB | 否 | 高 | 会话临时数据 |
| IndexedDB | 大 | 是 | 中 | 复杂数据结构 |
| tauri-plugin-store | 无限制 | 是 | 高 | 应用配置存储 |
1.2 存储架构 #
text
┌─────────────────────────────────────────────────────────────┐
│ 存储架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ localStorage│ │sessionStorage│ │ IndexedDB │ │
│ │ │ │ │ │ │ │
│ │ WebView │ │ WebView │ │ WebView │ │
│ │ 存储 │ │ 存储 │ │ 存储 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ tauri-plugin-store │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐│ │
│ │ │ JSON 文件 │ │ 加密存储 │ │ 自动同步 ││ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘│ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
二、localStorage #
2.1 基本使用 #
typescript
// 存储数据
localStorage.setItem('theme', 'dark');
localStorage.setItem('fontSize', '16');
// 读取数据
const theme = localStorage.getItem('theme');
const fontSize = localStorage.getItem('fontSize');
// 删除数据
localStorage.removeItem('theme');
// 清空所有数据
localStorage.clear();
2.2 存储对象 #
typescript
// 存储对象
const user = { name: 'Alice', email: 'alice@example.com' };
localStorage.setItem('user', JSON.stringify(user));
// 读取对象
const storedUser = JSON.parse(localStorage.getItem('user') || 'null');
2.3 封装工具函数 #
typescript
// utils/storage.ts
export const storage = {
get<T>(key: string, defaultValue: T): T {
const value = localStorage.getItem(key);
if (value === null) {
return defaultValue;
}
try {
return JSON.parse(value) as T;
} catch {
return value as unknown as T;
}
},
set<T>(key: string, value: T): void {
localStorage.setItem(key, JSON.stringify(value));
},
remove(key: string): void {
localStorage.removeItem(key);
},
clear(): void {
localStorage.clear();
},
};
2.4 React Hook #
typescript
import { useState, useEffect } from 'react';
export function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
});
const setValue = (value: T | ((val: T) => T)) => {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
localStorage.setItem(key, JSON.stringify(valueToStore));
};
return [storedValue, setValue] as const;
}
三、sessionStorage #
3.1 基本使用 #
typescript
// 存储会话数据
sessionStorage.setItem('sessionToken', 'abc123');
// 读取会话数据
const token = sessionStorage.getItem('sessionToken');
// 删除会话数据
sessionStorage.removeItem('sessionToken');
3.2 会话管理 #
typescript
class SessionManager {
private static TOKEN_KEY = 'session_token';
private static USER_KEY = 'session_user';
static setSession(token: string, user: User) {
sessionStorage.setItem(this.TOKEN_KEY, token);
sessionStorage.setItem(this.USER_KEY, JSON.stringify(user));
}
static getToken(): string | null {
return sessionStorage.getItem(this.TOKEN_KEY);
}
static getUser(): User | null {
const user = sessionStorage.getItem(this.USER_KEY);
return user ? JSON.parse(user) : null;
}
static clearSession() {
sessionStorage.removeItem(this.TOKEN_KEY);
sessionStorage.removeItem(this.USER_KEY);
}
static isAuthenticated(): boolean {
return !!this.getToken();
}
}
四、IndexedDB #
4.1 基本使用 #
typescript
// 打开数据库
const request = indexedDB.open('MyAppDB', 1);
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
if (!db.objectStoreNames.contains('users')) {
db.createObjectStore('users', { keyPath: 'id' });
}
if (!db.objectStoreNames.contains('posts')) {
const store = db.createObjectStore('posts', { keyPath: 'id' });
store.createIndex('authorId', 'authorId', { unique: false });
}
};
request.onsuccess = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
console.log('Database opened successfully');
};
4.2 封装 IndexedDB #
typescript
// utils/indexedDB.ts
export class IndexedDBHelper {
private db: IDBDatabase | null = null;
async open(name: string, version: number, stores: string[]): Promise<IDBDatabase> {
return new Promise((resolve, reject) => {
const request = indexedDB.open(name, version);
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
stores.forEach((store) => {
if (!db.objectStoreNames.contains(store)) {
db.createObjectStore(store, { keyPath: 'id' });
}
});
};
request.onsuccess = (event) => {
this.db = (event.target as IDBOpenDBRequest).result;
resolve(this.db);
};
request.onerror = () => {
reject(request.error);
};
});
}
async add<T>(storeName: string, data: T): Promise<void> {
return this.transaction(storeName, 'readwrite', (store) => {
store.add(data);
});
}
async get<T>(storeName: string, key: IDBValidKey): Promise<T | undefined> {
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<T>(storeName: string): Promise<T[]> {
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<T>(storeName: string, data: T): Promise<void> {
return this.transaction(storeName, 'readwrite', (store) => {
store.put(data);
});
}
async delete(storeName: string, key: IDBValidKey): Promise<void> {
return this.transaction(storeName, 'readwrite', (store) => {
store.delete(key);
});
}
private async transaction(
storeName: string,
mode: IDBTransactionMode,
operation: (store: IDBObjectStore) => void
): Promise<void> {
return new Promise((resolve, reject) => {
const transaction = this.db!.transaction(storeName, mode);
const store = transaction.objectStore(storeName);
operation(store);
transaction.oncomplete = () => resolve();
transaction.onerror = () => reject(transaction.error);
});
}
}
五、tauri-plugin-store #
5.1 安装插件 #
bash
pnpm add @tauri-apps/plugin-store
rust
// src-tauri/src/lib.rs
tauri::Builder::default()
.plugin(tauri_plugin_store::Builder::default().build())
.run(tauri::generate_context!())
.expect("error while running tauri application");
5.2 基本使用 #
typescript
import { Store } from '@tauri-apps/plugin-store';
// 创建存储实例
const store = new Store('app-store.json');
// 存储数据
await store.set('theme', 'dark');
await store.set('user', { name: 'Alice', email: 'alice@example.com' });
// 读取数据
const theme = await store.get<string>('theme');
const user = await store.get<{ name: string; email: string }>('user');
// 删除数据
await store.delete('theme');
// 保存到磁盘
await store.save();
// 清空所有数据
await store.clear();
5.3 自动保存 #
typescript
// 启用自动保存
const store = new Store('app-store.json', { autoSave: true });
// 数据变更会自动保存
await store.set('theme', 'dark'); // 自动保存到磁盘
5.4 监听变更 #
typescript
import { Store } from '@tauri-apps/plugin-store';
const store = new Store('app-store.json');
// 监听变更
const unlisten = store.onChange((key, value) => {
console.log(`Key "${key}" changed to:`, value);
});
// 取消监听
unlisten();
5.5 存储管理器 #
typescript
import { Store } from '@tauri-apps/plugin-store';
class StoreManager {
private stores: Map<string, Store> = new Map();
async getStore(name: string): Promise<Store> {
if (!this.stores.has(name)) {
const store = new Store(`${name}.json`);
await store.load();
this.stores.set(name, store);
}
return this.stores.get(name)!;
}
async get<T>(storeName: string, key: string): Promise<T | null> {
const store = await this.getStore(storeName);
return store.get<T>(key);
}
async set<T>(storeName: string, key: string, value: T): Promise<void> {
const store = await this.getStore(storeName);
await store.set(key, value);
await store.save();
}
async delete(storeName: string, key: string): Promise<void> {
const store = await this.getStore(storeName);
await store.delete(key);
await store.save();
}
}
export const storeManager = new StoreManager();
六、最佳实践 #
6.1 存储键命名 #
typescript
// 使用命名空间避免冲突
const STORAGE_KEYS = {
THEME: 'app:theme',
USER: 'app:user',
SETTINGS: 'app:settings',
RECENT_FILES: 'app:recent_files',
} as const;
6.2 数据版本管理 #
typescript
interface StoredData<T> {
version: number;
data: T;
updatedAt: string;
}
const CURRENT_VERSION = 1;
function saveWithVersion<T>(key: string, data: T): void {
const stored: StoredData<T> = {
version: CURRENT_VERSION,
data,
updatedAt: new Date().toISOString(),
};
localStorage.setItem(key, JSON.stringify(stored));
}
function loadWithVersion<T>(key: string): T | null {
const raw = localStorage.getItem(key);
if (!raw) return null;
const stored: StoredData<T> = JSON.parse(raw);
// 版本迁移
if (stored.version < CURRENT_VERSION) {
return migrateData(stored);
}
return stored.data;
}
6.3 数据加密 #
typescript
import { invoke } from '@tauri-apps/api/core';
class SecureStorage {
static async encrypt(data: string): Promise<string> {
return invoke('encrypt_data', { data });
}
static async decrypt(encrypted: string): Promise<string> {
return invoke('decrypt_data', { data: encrypted });
}
static async setSecure(key: string, value: unknown): Promise<void> {
const encrypted = await this.encrypt(JSON.stringify(value));
localStorage.setItem(key, encrypted);
}
static async getSecure<T>(key: string): Promise<T | null> {
const encrypted = localStorage.getItem(key);
if (!encrypted) return null;
const decrypted = await this.decrypt(encrypted);
return JSON.parse(decrypted);
}
}
七、总结 #
7.1 核心要点 #
| 要点 | 说明 |
|---|---|
| localStorage | 简单键值存储 |
| sessionStorage | 会话临时存储 |
| IndexedDB | 复杂数据存储 |
| tauri-plugin-store | 文件持久化存储 |
7.2 下一步 #
现在你已经掌握了本地存储,接下来让我们学习 文件系统,了解如何在 Tauri 中进行文件操作!
最后更新:2026-03-28