SQLite 适配器
@aiao/rxdb-adapter-sqlite 提供了在浏览器中运行 SQLite 数据库的能力,基于 wa-sqlite 实现,支持多种虚拟文件系统 (VFS) 和运行模式。
安装
- npm
- Yarn
- pnpm
- Bun
npm install @aiao/rxdb @aiao/rxdb-adapter-sqlite
yarn add @aiao/rxdb @aiao/rxdb-adapter-sqlite
pnpm add @aiao/rxdb @aiao/rxdb-adapter-sqlite
bun add @aiao/rxdb @aiao/rxdb-adapter-sqlite
核心概念
虚拟文件系统 (VFS)
SQLite 适配器支持多种 VFS,根据浏览器能力选择:
- OPFSCoopSyncVFS: 使用 Origin Private File System (OPFS),性能最佳,需要 SharedArrayBuffer 支持
- IDBBatchAtomicVFS: 使用 IndexedDB,兼容性最好,适用于不支持 OPFS 的环境
运行模式
- Worker: 在 Web Worker 中运行,推荐用于 OPFS
- SharedWorker: 在 Shared Worker 中运行,推荐用于 IDB,可跨标签页共享
基础使用
推荐配置(自动选择最佳方案)
import { RxDB, SyncType } from '@aiao/rxdb';
import { RxDBAdapterSqlite, SqliteOptions } from '@aiao/rxdb-adapter-sqlite';
import { checkOPFSAvailable } from '@aiao/utils';
const rxdb = new RxDB({
dbName: 'myapp',
entities: [Todo],
sync: {
local: { adapter: 'sqlite' },
type: SyncType.None
}
});
// 注册适配器
rxdb.adapter('sqlite', async db => {
let options: SqliteOptions;
// 检测浏览器能力
const available = await checkOPFSAvailable();
if (available) {
// 使用 OPFS + Worker(最佳性能)
options = {
vfs: 'OPFSCoopSyncVFS',
worker: true,
workerInstance: new Worker(
new URL('./sqlite.worker', import.meta.url),
{ type: 'module', name: 'rxdb-worker' }
),
wasmPath: '/wa-sqlite/wa-sqlite.wasm'
};
} else {
// 使用 IDB + SharedWorker(最佳兼容性)
options = {
vfs: 'IDBBatchAtomicVFS',
sharedWorker: true,
sharedWorkerInstance: new SharedWorker(
new URL('./sqlite-shared.worker', import.meta.url),
{ type: 'module', name: 'rxdb-shared-worker' }
),
wasmPath: '/wa-sqlite/wa-sqlite-async.wasm'
};
}
return new RxDBAdapterSqlite(db, options);
});
// 连接数据库
await rxdb.connect('sqlite').toPromise();
Worker 文件配置
sqlite.worker.ts (用于 OPFS)
import { SqliteWorker } from '@aiao/rxdb-adapter-sqlite';
const worker = new SqliteWorker();
worker.listen();
sqlite-shared.worker.ts (用于 IDB)
import { SqliteSharedWorker } from '@aiao/rxdb-adapter-sqlite';
const worker = new SqliteSharedWorker();
worker.listen();
配置选项
SqliteOptions 接口
interface SqliteOptions {
// 虚拟文件系统类型
vfs: 'OPFSCoopSyncVFS' | 'IDBBatchAtomicVFS';
// Web Worker 配置 (用于 OPFS)
worker?: boolean;
workerInstance?: Worker;
// Shared Worker 配置 (用于 IDB)
sharedWorker?: boolean;
sharedWorkerInstance?: SharedWorker;
// WASM 文件路径
wasmPath: string;
// 数据库文件名(可选,默认使用 dbName)
filename?: string;
// SQLite 配置选项
sqliteOptions?: {
// 页面大小(字节)
pageSize?: number;
// 缓存大小(页数)
cacheSize?: number;
// 日志模式
journalMode?: 'DELETE' | 'TRUNCATE' | 'PERSIST' | 'MEMORY' | 'WAL' | 'OFF';
// 同步模式
synchronous?: 'OFF' | 'NORMAL' | 'FULL' | 'EXTRA';
};
}
VFS 详细配置
OPFS + Worker 模式
最佳性能方案,适用于支持 OPFS 和 SharedArrayBuffer 的现代浏览器。
import { RxDBAdapterSqlite } from '@aiao/rxdb-adapter-sqlite';
const adapter = new RxDBAdapterSqlite(rxdb, {
vfs: 'OPFSCoopSyncVFS',
worker: true,
workerInstance: new Worker(
new URL('./sqlite.worker', import.meta.url),
{ type: 'module', name: 'rxdb-worker' }
),
wasmPath: '/wa-sqlite/wa-sqlite.wasm',
sqliteOptions: {
journalMode: 'WAL', // Write-Ahead Logging
synchronous: 'NORMAL'
}
});
特点:
- ✅ 最佳性能
- ✅ 持久化存储
- ✅ 大容量支持
- ❌ 需要 OPFS 支持
- ❌ 需要 SharedArrayBuffer(需要特定的 HTTP 头)
IDB + SharedWorker 模式
最佳兼容性方案,适用于所有现代浏览器。
import { RxDBAdapterSqlite } from '@aiao/rxdb-adapter-sqlite';
const adapter = new RxDBAdapterSqlite(rxdb, {
vfs: 'IDBBatchAtomicVFS',
sharedWorker: true,
sharedWorkerInstance: new SharedWorker(
new URL('./sqlite-shared.worker', import.meta.url),
{ type: 'module', name: 'rxdb-shared-worker' }
),
wasmPath: '/wa-sqlite/wa-sqlite-async.wasm',
sqliteOptions: {
journalMode: 'DELETE',
synchronous: 'NORMAL'
}
});
特点:
- ✅ 最佳兼容性
- ✅ 可跨标签页共享
- ✅ 持久化存储
- ⚠️ 性能略低于 OPFS
- ⚠️ 存储容量受 IndexedDB 限制
主线程模式(不推荐)
直接在主线程运行,仅用于开发测试。
const adapter = new RxDBAdapterSqlite(rxdb, {
vfs: 'IDBBatchAtomicVFS',
wasmPath: '/wa-sqlite/wa-sqlite-async.wasm'
});
特点:
- ⚠️ 会阻塞主线程
- ⚠️ 性能最差
- ❌ 不推荐生产使用
构建配置
Vite 配置
// vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
// 配置 Worker 支持
worker: {
format: 'es'
},
// 配置静态资源
publicDir: 'public',
// 如果使用 OPFS,需要配置 HTTP 头
server: {
headers: {
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp'
}
},
optimizeDeps: {
exclude: ['@aiao/rxdb-adapter-sqlite']
}
});
静态资源配置
将 wa-sqlite WASM 文件放到 public 目录:
public/
wa-sqlite/
wa-sqlite.wasm # 用于 OPFS
wa-sqlite-async.wasm # 用于 IDB
性能优化
1. 使用 WAL 模式
对于 OPFS,启用 WAL (Write-Ahead Logging) 可以提升并发性能:
sqliteOptions: {
journalMode: 'WAL',
synchronous: 'NORMAL'
}
2. 调整缓存大小
增加缓存可以提升查询性能:
sqliteOptions: {
cacheSize: 10000 // 10000 页,约 40MB (假设页面大小 4KB)
}
3. 批量操作
使用事务批量执行操作:
await rxdb.transaction(async () => {
for (const item of items) {
await item.save();
}
});
4. 索引优化
为常用查询字段创建索引:
@Entity({
indexes: [
{ columns: ['createdAt'] },
{ columns: ['completed', 'createdAt'] }
]
})
export class Todo extends EntityBase {
// ...
}
浏览器兼容性
OPFS + Worker 模式
| 浏览器 | 版本 | 支持 |
|---|---|---|
| Chrome | 102+ | ✅ |
| Edge | 102+ | ✅ |
| Safari | 15.2+ | ✅ |
| Firefox | 111+ | ✅ |
要求:
- 支持 OPFS (File System Access API)
- 支持 SharedArrayBuffer
- 正确配置 HTTP 头
IDB + SharedWorker 模式
| 浏览器 | 版本 | 支持 |
|---|---|---|
| Chrome | 90+ | ✅ |
| Edge | 90+ | ✅ |
| Safari | 14+ | ✅ |
| Firefox | 88+ | ✅ |
要求:
- 支持 IndexedDB
- 支持 Shared Worker
故障排查
SharedArrayBuffer 不可用
如果遇到 "SharedArrayBuffer is not defined" 错误:
- 检查 HTTP 响应头:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
- 如果无法配置 HTTP 头,使用 IDB 模式替代:
const available = await checkOPFSAvailable();
if (!available) {
// 使用 IDB 模式
}
Worker 加载失败
确保 Worker 文件路径正确:
// ✅ 正确:使用 new URL
new Worker(new URL('./sqlite.worker', import.meta.url), {
type: 'module'
})
// ❌ 错误:直接使用字符串路径
new Worker('./sqlite.worker.ts')
WASM 文件加载失败
检查 WASM 文件是否在正确的路径:
// 确保路径与 public 目录中的文件对应
wasmPath: '/wa-sqlite/wa-sqlite.wasm'
数据库锁定
如果多个标签页同时访问数据库导致锁定:
- 使用 SharedWorker 模式(IDB)
- 或实现标签页间的协调机制
迁移指南
从 IDB 迁移到 OPFS
// 1. 检测浏览器支持
const available = await checkOPFSAvailable();
if (available) {
// 2. 导出现有数据
const data = await rxdb.exportDatabase();
// 3. 切换到 OPFS 适配器
await rxdb.disconnect();
rxdb.adapter('sqlite', async db => {
return new RxDBAdapterSqlite(db, {
vfs: 'OPFSCoopSyncVFS',
worker: true,
// ... OPFS 配置
});
});
await rxdb.connect('sqlite').toPromise();
// 4. 导入数据
await rxdb.importDatabase(data);
}
完整示例
import { RxDB, Entity, EntityBase, PropertyType, SyncType } from '@aiao/rxdb';
import { RxDBAdapterSqlite, SqliteOptions } from '@aiao/rxdb-adapter-sqlite';
import { checkOPFSAvailable } from '@aiao/utils';
// 定义实体
@Entity({
name: 'Todo',
properties: [
{ name: 'title', type: PropertyType.string, required: true },
{ name: 'completed', type: PropertyType.boolean, default: false }
]
})
export class Todo extends EntityBase {}
// 初始化数据库
async function initDatabase() {
const rxdb = new RxDB({
dbName: 'todo-app',
entities: [Todo],
sync: {
local: { adapter: 'sqlite' },
type: SyncType.None
}
});
// 注册 SQLite 适配器
rxdb.adapter('sqlite', async db => {
let options: SqliteOptions;
const available = await checkOPFSAvailable();
if (available) {
options = {
vfs: 'OPFSCoopSyncVFS',
worker: true,
workerInstance: new Worker(
new URL('./sqlite.worker', import.meta.url),
{ type: 'module', name: 'rxdb-worker' }
),
wasmPath: '/wa-sqlite/wa-sqlite.wasm',
sqliteOptions: {
journalMode: 'WAL',
synchronous: 'NORMAL',
cacheSize: 10000
}
};
} else {
options = {
vfs: 'IDBBatchAtomicVFS',
sharedWorker: true,
sharedWorkerInstance: new SharedWorker(
new URL('./sqlite-shared.worker', import.meta.url),
{ type: 'module', name: 'rxdb-shared-worker' }
),
wasmPath: '/wa-sqlite/wa-sqlite-async.wasm',
sqliteOptions: {
journalMode: 'DELETE',
synchronous: 'NORMAL'
}
};
}
return new RxDBAdapterSqlite(db, options);
});
// 连接数据库
await rxdb.connect('sqlite').toPromise();
return rxdb;
}
// 使用数据库
async function main() {
const rxdb = await initDatabase();
// 创建待办
const todo = new Todo();
todo.title = '学习 RxDB';
await todo.save();
// 查询待办
const todos = await Todo.find({
where: { completed: false }
}).toPromise();
console.log('未完成的待办:', todos);
}
main();