前言:为什么要讨论"组合"?
想象你在浏览器里造一个"网页操作系统"(WebOS),你需要:
- 系统配置(小数据、同步读)
- 用户文件(大文件、流式读写)
- 数据库(结构化查询、索引检索)
- 会话/认证(需要跟服务器交互)
没有哪个单一存储能完美覆盖所有场景。所以"组合"不是炫技,而是真实架构需求。
一、单兵作战(4种)
1️⃣ LocalStorage 独挑大梁
┌──────────────────────────────────┐
│ LocalStorage │
│ ┌────────────────────────────┐ │
│ │ key1: "value1" │ │
│ │ key2: "value2" │ │
│ │ settings: "{...json...}" │ │
│ └────────────────────────────┘ │
│ 容量:5~10MB │
│ API:同步 │
│ 线程:主线程 only │
└──────────────────────────────────┘
核心特性:
| 项目 | 详情 |
|------|------|
| 容量 | 5~10MB(各浏览器不同) |
| API 类型 | 同步阻塞 |
| 数据格式 | 纯字符串 key-value |
| 可用线程 | 仅主线程(Worker 不可用) |
| 持久性 | 持久(除非用户手动清除) |
| 跨标签页 | ✅ 同源共享 + storage 事件 |
当 WebOS 存储核心时的体验:
// 存
localStorage.setItem('user_config', JSON.stringify({
theme: 'dark',
language: 'zh-CN',
fontSize: 14
}));
// 取
const config = JSON.parse(localStorage.getItem('user_config'));
✅ 优势:
- 极其简单,零学习成本
- 同步读取 → 启动时立刻拿到配置,无需
await - 跨标签页
storage事件 → 可实现简易的跨窗口通信
❌ 致命短板:
- 5MB 天花板:一个操作系统?存几张图片就炸了
- 同步阻塞:数据多了直接卡死主线程,UI 冻结
- 纯字符串:存二进制?不好意思,先 Base64 再说,体积膨胀 33%
- 无索引、无查询:想按条件找数据?只能全部读出来自己遍历
- 无事务:并发写入可能互相覆盖
作为 WebOS 核心评级:⭐(勉强能做最简单的玩具)
🎯 现实定位:系统偏好设置、少量缓存标记,绝不是主存储。
2️⃣ Cookies 独挑大梁
┌──────────────────────────────────────┐
│ Cookies │
│ ┌────────────────────────────────┐ │
│ │ session_id=abc123; Path=/; │ │
│ │ Expires=Thu, 01 Jan 2026; │ │
│ │ HttpOnly; Secure; SameSite │ │
│ └────────────────────────────────┘ │
│ 容量:~4KB / 条,~80条 / 域名 │
│ API:同步(document.cookie) │
│ 特殊:每次 HTTP 请求自动携带 │
└──────────────────────────────────────┘
核心特性:
| 项目 | 详情 |
|------|------|
| 容量 | 单条约 4KB,总计约 80 条(约 320KB) |
| API 类型 | 同步(document.cookie 拼字符串) |
| 数据格式 | 字符串,格式受限(分号/等号有特殊含义) |
| 可用线程 | 主线程(HttpOnly 的 JS 不可读) |
| 持久性 | 可设过期时间,Session Cookie 关浏览器即失效 |
| 独家能力 | 每个 HTTP 请求自动携带到服务器 |
当 WebOS 存储核心时的体验:
// 存(痛苦的字符串拼接)
document.cookie = "theme=dark; path=/; max-age=31536000; Secure";
document.cookie = "lang=zh-CN; path=/; max-age=31536000; Secure";
// 取(更痛苦的字符串解析)
function getCookie(name) {
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
return match ? decodeURIComponent(match[2]) : null;
}
✅ 优势:
- 自动随请求发送:用户身份认证的唯一正解
- 服务端可通过
Set-Cookie主动写入 -
HttpOnly防 XSS 偷 Token - 精细的过期控制
❌ 致命短板:
- 4KB / 条:这是存储吗?这是便签纸
-
API 反人类:
document.cookie是 Web API 最大的历史包袱之一 - 性能杀手:每个请求都带上所有 Cookie → 带宽浪费
- 存大数据?做梦
- 无结构化、无查询、无索引
作为 WebOS 核心评级:⭐(不可能,但它的认证能力不可替代)
🎯 现实定位:Session管理、身份认证 Token、服务端交互凭证。
3️⃣ IndexedDB 独挑大梁
┌─────────────────────────────────────────┐
│ IndexedDB │
│ ┌─────────────────────────────────┐ │
│ │ Database: "WebOS_DB" │ │
│ │ ├─ ObjectStore: "files" │ │
│ │ │ ├─ { id:1, name:"a.txt", │ │
│ │ │ │ data: Blob(1.2MB) } │ │
│ │ │ ├─ { id:2, name:"b.png", │ │
│ │ │ │ data: Blob(5MB) } │ │
│ │ │ └─ index: "by_name" │ │
│ │ ├─ ObjectStore: "settings" │ │
│ │ └─ ObjectStore: "apps" │ │
│ └─────────────────────────────────┘ │
│ 容量:数百MB ~ 数GB │
│ API:异步(基于事件/Promise) │
│ 支持:事务、索引、游标、Blob │
└─────────────────────────────────────────┘
核心特性:
| 项目 | 详情 |
|------|------|
| 容量 | 理论无上限(通常数百MB~数GB,受磁盘和浏览器策略限制) |
| API 类型 | 异步(原生基于事件回调,可用 idb 等库 Promise 化) |
| 数据格式 | 结构化克隆:支持对象、数组、Date、Blob、ArrayBuffer、File 等 |
| 可用线程 | ✅ 主线程 + Web Worker + Service Worker |
| 持久性 | 持久(可申请 navigator.storage.persist() 防清除) |
| 查询能力 | 索引、范围查询、游标遍历 |
| 事务 | ✅ 支持(readonly / readwrite) |
当 WebOS 存储核心时的体验:
// 使用 idb 库简化
import { openDB } from 'idb';
const db = await openDB('WebOS', 1, {
upgrade(db) {
const fileStore = db.createObjectStore('files', { keyPath: 'id', autoIncrement: true });
fileStore.createIndex('by_name', 'name');
fileStore.createIndex('by_type', 'mimeType');
fileStore.createIndex('by_folder', 'folder');
db.createObjectStore('apps', { keyPath: 'appId' });
db.createObjectStore('settings', { keyPath: 'key' });
}
});
// 存文件
await db.put('files', {
name: 'document.pdf',
mimeType: 'application/pdf',
folder: '/home/user/docs',
data: someBlobData, // 直接存二进制!
size: someBlobData.size,
createdAt: new Date(),
modifiedAt: new Date()
});
// 按文件夹查找
const docsInFolder = await db.getAllFromIndex('files', 'by_folder', '/home/user/docs');
// 事务安全地批量操作
const tx = db.transaction('files', 'readwrite');
await Promise.all([
tx.store.put(file1),
tx.store.put(file2),
tx.store.delete(oldFileId),
tx.done
]);
✅ 优势:
- 大容量:真正能存"文件系统"级别的数据
- 结构化存储:不用 JSON.stringify,直接存对象、Blob
- 索引查询:建索引后按条件快速检索
- 事务安全:批量操作要么全成功要么全失败
- Worker 可用:后台线程操作,不卡 UI
- Service Worker 可用:离线缓存的好搭档
❌ 短板:
- API 极度繁琐(原生 API 是公认的"反人类",必须用封装库)
- 不是真正的文件系统:没有目录层级、没有文件流、没有 seek
- 大文件性能:存 1GB 文件虽然可以但读写速度不如真正的文件系统
-
版本升级管理痛苦(
onupgradeneeded) - 不支持 SQL,复杂查询需要自己组合游标
作为 WebOS 核心评级:⭐⭐⭐⭐(最接近"单兵可用"的方案)
🎯 现实定位:WebOS 主数据库、文件元数据存储、应用数据、离线数据。是浏览器存储中的"瑞士军刀"。
4️⃣ OPFS(Origin Private File System)独挑大梁
┌──────────────────────────────────────────┐
│ OPFS (Origin Private File System) │
│ ┌────────────────────────────────────┐ │
│ │ / (root) │ │
│ │ ├── home/ │ │
│ │ │ ├── user/ │ │
│ │ │ │ ├── docs/ │ │
│ │ │ │ │ ├── resume.pdf │ │
│ │ │ │ │ └── notes.txt │ │
│ │ │ │ └── photos/ │ │
│ │ │ │ └── vacation.jpg │ │
│ │ ├── system/ │ │
│ │ │ └── config.json │ │
│ │ └── apps/ │ │
│ │ └── editor/ │ │
│ │ └── cache.dat │ │
│ └────────────────────────────────────┘ │
│ 容量:数GB(接近磁盘限制) │
│ API:异步 + Worker 同步高性能 │
│ 特殊:真正的文件系统 + 高性能随机读写 │
└──────────────────────────────────────────┘
核心特性:
| 项目 | 详情 |
|------|------|
| 容量 | 数 GB 甚至更多(接近 IndexedDB,通常更宽松) |
| API 类型 | 主线程异步 / Worker 中可同步高性能(createSyncAccessHandle) |
| 数据格式 | 真实文件:二进制流、可随机 seek/read/write |
| 可用线程 | ✅ 主线程 + Web Worker |
| 持久性 | 持久(同 IndexedDB,可 persist()) |
| 目录结构 | ✅ 真正的目录层级(mkdir、遍历、递归) |
| 独家能力 | 高性能同步随机读写(Worker 中)、真文件流 |
当 WebOS 存储核心时的体验:
// === 主线程:异步API ===
// 获取根目录
const root = await navigator.storage.getDirectory();
// 创建目录结构
const homeDir = await root.getDirectoryHandle('home', { create: true });
const userDir = await homeDir.getDirectoryHandle('user', { create: true });
const docsDir = await userDir.getDirectoryHandle('docs', { create: true });
// 创建并写入文件
const fileHandle = await docsDir.getFileHandle('notes.txt', { create: true });
const writable = await fileHandle.createWritable();
await writable.write('Hello, WebOS!');
await writable.write(new Blob(['More content...']));
await writable.close();
// 读取文件
const file = await fileHandle.getFile();
const text = await file.text();
console.log(text); // "Hello, WebOS!More content..."
// 遍历目录
for await (const [name, handle] of docsDir) {
console.log(`${name}: ${handle.kind}`); // "notes.txt: file"
}
// 删除
await docsDir.removeEntry('notes.txt');
// 递归删除目录
await homeDir.removeEntry('user', { recursive: true });
// === Worker 中:同步高性能API ===
// worker.js
const root = await navigator.storage.getDirectory();
const dir = await root.getDirectoryHandle('data', { create: true });
const fileHandle = await dir.getFileHandle('database.bin', { create: true });
// 同步访问句柄 —— 性能怪兽
const accessHandle = await fileHandle.createSyncAccessHandle();
// 随机读写(类似 C 的 fread/fwrite/fseek)
const buffer = new ArrayBuffer(1024);
const bytesRead = accessHandle.read(buffer, { at: 0 }); // seek+read
accessHandle.write(new Uint8Array([1,2,3,4]), { at: 1024 }); // seek+write
accessHandle.flush(); // 刷盘
accessHandle.close(); // 关闭
✅ 优势:
- 真正的文件系统:目录、子目录、文件,和操作系统一模一样
-
高性能随机读写:
SyncAccessHandle在 Worker 中性能接近原生 - 大容量:存几个 GB 不在话下
-
流式写入:
WritableStreamAPI,大文件不用全部载入内存 - SQLite 移植:wa-sqlite/sql.js 用 OPFS 做后端,性能飙升
- 最像"硬盘"的浏览器 API
❌ 短板:
- 无索引、无查询:它就是文件系统,不是数据库。想查?自己遍历
- 无元数据搜索:按文件名找可以,按内容/属性搜索不行
- 兼容性:较新 API(Chrome 86+完整支持,Safari 15.2+, Firefox 111+)
- 对用户不可见:文件存在浏览器沙箱里,用户在文件管理器里看不到
- 同步 API 仅限 Worker:主线程只能用异步 API
- 无跨标签页通知:文件改了不会自动通知其他标签页
作为 WebOS 核心评级:⭐⭐⭐⭐(真正的文件系统体验,但缺数据库能力)
🎯 现实定位:WebOS 文件系统层、大文件存储、SQLite 后端、编译缓存。
单兵对比总结
能力雷达图(5分满分):
容量 查询 性能 API易用 文件能力 服务端交互
LocalStorage 1 0 2 5 0 0
Cookies 0 0 1 1 0 5
IndexedDB 4 3 3 2 2 0
OPFS 5 0 5 3 5 0
二、两两组合(6种)
C(4,2) = 6 种组合,我们逐一分析。
组合 1️⃣:LocalStorage + Cookies
┌─────────────────────────────────────────────┐
│ LocalStorage + Cookies │
│ │
│ ┌──────────────┐ ┌───────────────────┐ │
│ │ LocalStorage │ │ Cookies │ │
│ │ │ │ │ │
│ │ 用户偏好设置 │ │ session_id=xxx │ │
│ │ UI状态缓存 │ │ csrf_token=yyy │ │
│ │ 功能开关 │ │ auth_token=zzz │ │
│ │ 最近打开文件 │ │ │ │
│ └──────┬───────┘ └────────┬──────────┘ │
│ │ 主线程同步读取 │ │
│ └──────────┬───────────┘ │
│ ▼ │
│ 简易配置 + 认证层 │
└─────────────────────────────────────────────┘
分工:
- Cookies → 用户认证、Session 管理、CSRF Token
- LocalStorage → 客户端偏好设置、UI 状态、小量缓存
典型代码:
// 启动时:同步读取配置
const theme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', theme);
// 认证:Cookie 自动携带
// (由服务器 Set-Cookie 设置,JS 可能读不到 HttpOnly 的)
fetch('/api/user/profile', { credentials: 'include' })
.then(res => res.json())
.then(user => {
localStorage.setItem('cached_user_name', user.name);
});
体验评估:
| 维度 | 评分 | 说明 |
|------|------|------|
| 总容量 | ⭐ | 5MB + 4KB ≈ 5MB,还是太少 |
| 数据能力 | ⭐ | 纯字符串,无结构化 |
| 文件存储 | ❌ | 完全不可能 |
| 认证能力 | ⭐⭐⭐⭐⭐ | Cookie 的天然优势 |
| 性能 | ⭐⭐⭐ | 都是同步,简单快速但会阻塞 |
| 适合场景 | 传统网站的标配 | |
作为 WebOS 核心:⭐⭐(只能做最基础的配置+登录,没有真正的数据/文件存储能力)
📝 这是传统网站的标配组合,但远不够做"操作系统"。
组合 2️⃣:LocalStorage + IndexedDB
┌──────────────────────────────────────────────────┐
│ LocalStorage + IndexedDB │
│ │
│ ┌──────────────┐ ┌─────────────────────────┐ │
│ │ LocalStorage │ │ IndexedDB │ │
│ │ │ │ │ │
│ │ 快速启动配置 │◄──►│ DB: "WebOS" │ │
│ │ 缓存热点key │ │ ├─ files (Blob存储) │ │
│ │ DB版本号 │ │ ├─ apps (应用注册表) │ │
│ │ 最后同步时间 │ │ ├─ messages (消息) │ │
│ │ │ │ └─ cache (页面缓存) │ │
│ └──────────────┘ └─────────────────────────┘ │
│ │
│ 同步热数据(LS) ←→ 异步冷/大数据(IDB) │
└──────────────────────────────────────────────────┘
分工:
- LocalStorage → 启动配置、热数据缓存、同步读的"快速通道"
- IndexedDB → 主数据库、文件存储、应用数据
典型代码:
// === 启动优化:LS做一级缓存 ===
// 1. 先从 LS 同步读取关键配置(不用 await,瞬间可用)
const cachedConfig = JSON.parse(localStorage.getItem('sys_config') || '{}');
applyConfig(cachedConfig); // 立即应用,用户看到的第一帧就是正确主题
// 2. 后台从 IDB 读取完整配置(可能更新了)
const db = await openDB('WebOS', 1);
const fullConfig = await db.get('settings', 'sys_config');
if (fullConfig && fullConfig.version > cachedConfig.version) {
applyConfig(fullConfig);
// 回写 LS 缓存
localStorage.setItem('sys_config', JSON.stringify(fullConfig));
}
// === 文件操作在 IDB ===
await db.put('files', {
id: crypto.randomUUID(),
name: 'photo.jpg',
folder: '/pictures',
data: imageBlob,
size: imageBlob.size
});
// === LS 存储"最近文件"(小数据、快速读取)===
const recent = JSON.parse(localStorage.getItem('recent_files') || '[]');
recent.unshift({ name: 'photo.jpg', folder: '/pictures', time: Date.now() });
localStorage.setItem('recent_files', JSON.stringify(recent.slice(0, 20)));
体验评估:
| 维度 | 评分 | 说明 |
|------|------|------|
| 总容量 | ⭐⭐⭐⭐ | IDB 提供 GB 级 |
| 启动速度 | ⭐⭐⭐⭐⭐ | LS 同步读配置,秒开 |
| 数据能力 | ⭐⭐⭐⭐ | IDB 索引+事务 |
| 文件存储 | ⭐⭐⭐ | IDB 可存 Blob 但不是真文件系统 |
| 性能 | ⭐⭐⭐ | 热数据同步快,冷数据异步 |
| 离线能力 | ⭐⭐⭐⭐ | IDB 是离线优先的好选择 |
作为 WebOS 核心:⭐⭐⭐⭐(相当实用的组合!启动快+大容量+结构化数据)
📝 这是实际项目中最常见的"进阶组合"。很多 PWA 就是这么搭配的。
组合 3️⃣:LocalStorage + OPFS
┌──────────────────────────────────────────────────┐
│ LocalStorage + OPFS │
│ │
│ ┌──────────────┐ ┌─────────────────────────┐ │
│ │ LocalStorage │ │ OPFS │ │
│ │ │ │ │ │
│ │ 系统配置 │ │ /home/ │ │
│ │ 快速偏好 │ │ /user/docs/ │ │
│ │ 启动标记 │ │ resume.pdf │ │
│ │ 文件索引缓存 │ │ notes.txt │ │
│ │ │ │ /user/photos/ │ │
│ │ │ │ vacation.jpg │ │
│ │ │ │ /system/ │ │
│ │ │ │ database.sqlite │ │
│ └──────────────┘ └─────────────────────────┘ │
│ │
│ 同步配置 ←→ 真文件系统 │
└──────────────────────────────────────────────────┘
分工:
- LocalStorage → 系统偏好、启动配置、简单的文件索引缓存
- OPFS → 真实文件存储、目录管理、大文件读写
典型代码:
// LS:快速配置
localStorage.setItem('desktop_layout', JSON.stringify({
wallpaper: '/system/wallpapers/default.jpg',
icons: [{ name: 'Files', x: 10, y: 10 }]
}));
// OPFS:文件系统操作
const root = await navigator.storage.getDirectory();
const docsDir = await root
.getDirectoryHandle('home', { create: true })
.then(h => h.getDirectoryHandle('user', { create: true }))
.then(h => h.getDirectoryHandle('docs', { create: true }));
const file = await docsDir.getFileHandle('report.md', { create: true });
const writable = await file.createWritable();
await writable.write('# My Report\n\nContent here...');
await writable.close();
// 问题:怎么搜索文件?
// LS 存简易索引(但5MB限制...)
const fileIndex = JSON.parse(localStorage.getItem('file_index') || '[]');
fileIndex.push({ path: '/home/user/docs/report.md', size: 30, type: 'text/markdown' });
localStorage.setItem('file_index', JSON.stringify(fileIndex));
// ⚠️ 文件多了 LS 就存不下了
体验评估:
| 维度 | 评分 | 说明 |
|------|------|------|
| 总容量 | ⭐⭐⭐⭐⭐ | OPFS 提供 GB 级 |
| 文件体验 | ⭐⭐⭐⭐⭐ | 真正的目录+文件 |
| 数据查询 | ⭐ | 没有数据库!搜索文件只能遍历或靠LS简陋索引 |
| 启动速度 | ⭐⭐⭐⭐ | LS 同步配置 |
| 性能 | ⭐⭐⭐⭐ | OPFS Worker同步API极快 |
作为 WebOS 核心:⭐⭐⭐(文件系统很棒,但缺少结构化查询能力是硬伤)
📝 就像一台只有文件系统但没有数据库的电脑 —— 功能不全。
组合 4️⃣:Cookies + IndexedDB
┌──────────────────────────────────────────────────┐
│ Cookies + IndexedDB │
│ │
│ ┌───────────────┐ ┌──────────────────────────┐│
│ │ Cookies │ │ IndexedDB ││
│ │ │ │ ││
│ │ auth_token │──►│ DB: "WebOS" ││
│ │ session_id │ │ ├─ user_data ││
│ │ preferences │ │ ├─ files ││
│ │ │ │ ├─ offline_queue ││
│ │ (每次请求 │ │ │ (离线操作待同步) ││
│ │ 自动携带) │ │ └─ sync_metadata ││
│ └───────┬───────┘ └──────────┬───────────────┘│
│ │ │ │
│ ▼ ▼ │
│ 服务器认证 本地数据存储 │
│ │ │ │
│ └──────── 同步 ────────┘ │
└──────────────────────────────────────────────────┘
分工:
- Cookies → 认证令牌、会话管理
- IndexedDB → 所有客户端数据、离线队列、同步状态
典型代码:
// Cookie 处理认证(通常由服务器 Set-Cookie 设置)
// HttpOnly Cookie 对 JS 不可见,但自动随请求发送
// IndexedDB 存储用户同步下来的数据
const db = await openDB('WebOS', 1, {
upgrade(db) {
db.createObjectStore('user_data', { keyPath: 'id' });
db.createObjectStore('offline_queue', { keyPath: 'queueId', autoIncrement: true });
}
});
// 从服务器拉取数据(Cookie自动认证)
const response = await fetch('/api/data', { credentials: 'include' });
const data = await response.json();
await db.put('user_data', data);
// 离线时,操作存入队列
async function offlineAction(action) {
if (!navigator.onLine) {
await db.add('offline_queue', {
action,
timestamp: Date.now()
});
} else {
await fetch('/api/action', {
method: 'POST',
credentials: 'include', // Cookie自动带上
body: JSON.stringify(action)
});
}
}
// 上线后同步
window.addEventListener('online', async () => {
const queue = await db.getAll('offline_queue');
for (const item of queue) {
await fetch('/api/action', {
method: 'POST',
credentials: 'include',
body: JSON.stringify(item.action)
});
await db.delete('offline_queue', item.queueId);
}
});
体验评估:
| 维度 | 评分 | 说明 |
|------|------|------|
| 总容量 | ⭐⭐⭐⭐ | IDB 提供大容量 |
| 认证能力 | ⭐⭐⭐⭐⭐ | Cookie 天然优势 |
| 离线+同步 | ⭐⭐⭐⭐⭐ | IDB队列 + Cookie认证 = 完美离线同步 |
| 启动速度 | ⭐⭐⭐ | 缺少同步快速读取(没有LS) |
| 文件系统 | ⭐⭐ | IDB 存 Blob 凑合用 |
作为 WebOS 核心:⭐⭐⭐(数据库+认证,但缺快速配置和真文件系统)
📝 这是需要服务端同步的离线优先应用的经典搭配。
组合 5️⃣:Cookies + OPFS
┌──────────────────────────────────────────────────┐
│ Cookies + OPFS │
│ │
│ ┌───────────────┐ ┌──────────────────────────┐│
│ │ Cookies │ │ OPFS ││
│ │ │ │ ││
│ │ auth_token │ │ /uploads/ ││
│ │ session_id │ │ large_file_1.zip ││
│ │ │ │ large_file_2.mp4 ││
│ │ │ │ /workspace/ ││
│ │ │ │ project/ ││
│ │ │ │ src/ ││
│ │ │ │ main.js ││
│ └───────┬───────┘ └──────────┬───────────────┘│
│ │ │ │
│ 认证下载/上传 ◄──────────────► 文件存储 │
└──────────────────────────────────────────────────┘
分工:
- Cookies → 认证凭证,保护文件上传/下载的 API
- OPFS → 大文件存储、工作区文件管理
典型场景: 认证后下载大文件到本地 OPFS 中离线使用
// 带认证的大文件下载,流式存入 OPFS
const response = await fetch('/api/download/large-video.mp4', {
credentials: 'include' // Cookie 自动认证
});
const root = await navigator.storage.getDirectory();
const downloads = await root.getDirectoryHandle('downloads', { create: true });
const fileHandle = await downloads.getFileHandle('large-video.mp4', { create: true });
const writable = await fileHandle.createWritable();
// 流式写入,不占内存
await response.body.pipeTo(writable);
体验评估:
| 维度 | 评分 | 说明 |
|------|------|------|
| 文件存储 | ⭐⭐⭐⭐⭐ | OPFS 全能 |
| 认证 | ⭐⭐⭐⭐⭐ | Cookie 自动认证 |
| 数据查询 | ❌ | 完全没有数据库能力 |
| 配置管理 | ❌ | 没有快速配置方案(Cookie 太小,OPFS 太重) |
| 元数据 | ❌ | 搜索文件属性无能为力 |
作为 WebOS 核心:⭐⭐(有硬盘有网络但没有数据库没有注册表,瘸腿系统)
📝 特定场景有用(如云盘客户端),但通用性太差。
组合 6️⃣:IndexedDB + OPFS ⭐ 黄金搭档
┌──────────────────────────────────────────────────────┐
│ IndexedDB + OPFS ★ 黄金搭档 ★ │
│ │
│ ┌──────────────────────┐ ┌───────────────────────┐ │
│ │ IndexedDB │ │ OPFS │ │
│ │ │ │ │ │
│ │ files_metadata: │ │ /files/ │ │
│ │ ┌─────────────────┐ │ │ {uuid1}.bin ◄──────┤ │
│ │ │ id: uuid1 │──┼──┤ {uuid2}.bin │ │
│ │ │ name: "photo" │ │ │ {uuid3}.bin │ │
│ │ │ path: "/pics/" │ │ │ /workspace/ │ │
│ │ │ size: 5242880 │ │ │ project.db (sqlite) │ │
│ │ │ type: "image" │ │ │ /cache/ │ │
│ │ │ tags: ["旅行"] │ │ │ wasm_module.wasm │ │
│ │ │ created: Date │ │ │ │ │
│ │ └─────────────────┘ │ │ │ │
│ │ │ │ │ │
│ │ apps_registry: │ │ │ │
│ │ (应用注册、权限) │ │ │ │
│ │ │ │ │ │
│ │ search_index: │ │ │ │
│ │ (全文搜索索引) │ │ │ │
│ └──────────┬───────────┘ └───────────┬───────────┘ │
│ │ 双向引用 │ │
│ └──────────────────────────┘ │
│ │
│ 元数据查询(IDB) ←→ 文件内容读写(OPFS) │
│ "数据库" + "文件系统" = 操作系统存储层 │
└──────────────────────────────────────────────────────┘
分工(完美互补):
- IndexedDB → 文件元数据(名称、路径、大小、标签、索引)、应用数据、搜索索引
- OPFS → 实际文件内容(二进制数据)、SQLite 数据库文件、WASM 模块
这是一对天作之合的原因:
IndexedDB 的弱点 OPFS 来补
───────────────────── ─────────────────────
大文件性能差 高性能流式读写
没有目录结构 真实目录层级
不是真文件系统 就是文件系统
OPFS 的弱点 IndexedDB 来补
───────────────────── ─────────────────────
无索引/无查询 索引 + 范围查询
无法按属性搜索 结构化元数据存储
没有事务安全 事务保障
典型代码:
// === 文件管理器:保存文件 ===
async function saveFile(folder, fileName, content, metadata) {
const db = await openDB('WebOS', 1);
const root = await navigator.storage.getDirectory();
// 1. 生成唯一 ID
const fileId = crypto.randomUUID();
// 2. 文件内容存 OPFS
const filesDir = await root.getDirectoryHandle('files', { create: true });
const fileHandle = await filesDir.getFileHandle(`${fileId}.bin`, { create: true });
const writable = await fileHandle.createWritable();
await writable.write(content);
await writable.close();
// 3. 元数据存 IndexedDB(可搜索!)
await db.put('files_metadata', {
id: fileId,
name: fileName,
folder: folder,
size: content.size || content.length,
mimeType: metadata.mimeType,
tags: metadata.tags || [],
createdAt: new Date(),
modifiedAt: new Date()
});
}
// === 搜索文件(IDB 索引查询,瞬间返回)===
async function searchFiles(query) {
const db = await openDB('WebOS', 1);
// 按文件夹
const byFolder = await db.getAllFromIndex('files_metadata', 'by_folder', '/home/docs');
// 按标签
const byTag = await db.getAllFromIndex('files_metadata', 'by_tags', '旅行');
// 按类型
const images = await db.getAllFromIndex('files_metadata', 'by_type',
IDBKeyRange.bound('image/', 'image/\uffff'));
return { byFolder, byTag, images };
}
// === 读取文件内容(OPFS 高性能读取)===
async function readFile(fileId) {
const root = await navigator.storage.getDirectory();
const filesDir = await root.getDirectoryHandle('files');
const fileHandle = await filesDir.getFileHandle(`${fileId}.bin`);
return await fileHandle.getFile();
}
// === 在 Worker 中运行 SQLite(终极方案)===
// Worker 中使用 OPFS 作为 SQLite 的 VFS 后端
// import { sqlite3Worker } from '@aspect/sqlite-wasm';
// 实现高性能关系型数据库
体验评估:
| 维度 | 评分 | 说明 |
|------|------|------|
| 总容量 | ⭐⭐⭐⭐⭐ | 双 GB 级 |
| 文件系统 | ⭐⭐⭐⭐⭐ | OPFS 提供真正的文件系统 |
| 数据查询 | ⭐⭐⭐⭐ | IDB 索引+事务 |
| 性能 | ⭐⭐⭐⭐⭐ | OPFS Worker 同步 + IDB 异步 |
| 启动速度 | ⭐⭐⭐ | 都是异步,首帧可能略慢(无同步快速读取) |
| 认证 | ❌ | 没有 Cookie,服务端交互需自行处理 |
作为 WebOS 核心:⭐⭐⭐⭐⭐(最强双打!数据库+文件系统 = 操作系统存储核心)
📝 如果只能选两个,就选这对! 它们是浏览器中最接近"操作系统存储层"的组合。真实的 WebOS 项目(如 user-operated-internet/puter)核心就是这个思路。
三、三个组合(4种)
C(4,3) = 4 种组合。
组合 A:LocalStorage + Cookies + IndexedDB
┌─────────────────────────────────────────────────────────┐
│ LocalStorage + Cookies + IndexedDB │
│ │
│ ┌────────────┐ ┌────────────┐ ┌───────────────────────┐│
│ │LocalStorage │ │ Cookies │ │ IndexedDB ││
│ │ │ │ │ │ ││
│ │ 快速配置 │ │ auth_token │ │ users, files, ││
│ │ UI状态 │ │ session_id │ │ apps, messages, ││
│ │ 缓存热数据 │ │ csrf_token │ │ offline_queue ││
│ │ DB版本号 │ │ │ │ ││
│ └─────┬──────┘ └─────┬──────┘ └──────────┬────────────┘│
│ │ │ │ │
│ 同步启动配置 自动认证同步 主数据存储 │
│ │ │ │ │
│ └──────────────┼───────────────────┘ │
│ ▼ │
│ 传统 Web 应用终极形态 │
└─────────────────────────────────────────────────────────┘
三角分工:
| 角色 | 存储 | 职责 |
|------|------|------|
| 注册表/快速配置 | LocalStorage | 同步读取系统偏好、UI 状态、热数据缓存 |
| 身份认证层 | Cookies | Session、JWT Token、CSRF 保护 |
| 主数据库 | IndexedDB | 所有业务数据、文件(Blob)、离线队列 |
典型流程:
// 1️⃣ 应用启动(同步,瞬间)
const config = JSON.parse(localStorage.getItem('app_config') || '{}');
applyTheme(config.theme);
showSplashScreen();
// 2️⃣ 检查认证状态(Cookie 自动携带)
const authRes = await fetch('/api/auth/check', { credentials: 'include' });
if (!authRes.ok) return redirectToLogin();
// 3️⃣ 加载用户数据(IDB)
const db = await openDB('WebOS', 1);
const userData = await db.get('users', 'current');
if (!userData || userData.needsSync) {
// 4️⃣ 从服务器同步(Cookie 认证 + IDB 存储)
const serverData = await fetch('/api/user/data', { credentials: 'include' });
const data = await serverData.json();
await db.put('users', { ...data, id: 'current', lastSync: Date.now() });
// 更新 LS 热缓存
localStorage.setItem('user_name', data.name);
localStorage.setItem('last_sync', String(Date.now()));
}
体验评估:
| 维度 | 评分 | 说明 |
|------|------|------|
| 启动体验 | ⭐⭐⭐⭐⭐ | LS 同步秒开 |
| 认证同步 | ⭐⭐⭐⭐⭐ | Cookie 无缝认证 |
| 数据能力 | ⭐⭐⭐⭐ | IDB 结构化大容量 |
| 文件系统 | ⭐⭐ | IDB 存 Blob 勉强凑合,但不是真文件系统 |
| 离线能力 | ⭐⭐⭐⭐ | IDB 离线队列 + 上线同步 |
作为 WebOS 核心:⭐⭐⭐⭐(传统 Web 应用的天花板,但缺真正的文件系统)
📝 这是 2020 年以前大多数高级 PWA 的架构选择,"够用"但不够"像操作系统"。
组合 B:LocalStorage + Cookies + OPFS
┌─────────────────────────────────────────────────────────┐
│ LocalStorage + Cookies + OPFS │
│ │
│ ┌────────────┐ ┌────────────┐ ┌───────────────────────┐│
│ │LocalStorage │ │ Cookies │ │ OPFS ││
│ │ │ │ │ │ ││
│ │ 系统配置 │ │ auth_token │ │ /home/user/ ││
│ │ 文件索引 │ │ session_id │ │ docs/ photos/ ││
│ │ (5MB限制!) │ │ │ │ /system/ ││
│ │ │ │ │ │ config.json ││
│ │ │ │ │ │ /downloads/ ││
│ │ │ │ │ │ big_file.zip ││
│ └────────────┘ └────────────┘ └───────────────────────┘│
│ │
│ ⚠️ 缺少数据库!文件搜索只能遍历或靠LS简陋索引 │
└─────────────────────────────────────────────────────────┘
三角分工:
| 角色 | 存储 | 职责 |
|------|------|------|
| 快速配置 | LocalStorage | 偏好设置、简单索引 |
| 认证 | Cookies | 下载/上传认证 |
| 文件系统 | OPFS | 所有文件内容 |
痛点分析:
// 想搜索所有 .pdf 文件?
// 方案1:遍历 OPFS(慢!没有索引)
async function findPDFs(dirHandle) {
const results = [];
for await (const [name, handle] of dirHandle) {
if (handle.kind === 'file' && name.endsWith('.pdf')) {
results.push(handle);
} else if (handle.kind === 'directory') {
results.push(...await findPDFs(handle)); // 递归!
}
}
return results;
}
// 文件多的时候这会很慢...
// 方案2:LS 存索引(5MB 存得下多少?)
// 假设每条索引 200 字节,5MB ≈ 25000 条
// 对于"操作系统"来说太少了
// 真正的痛点:没有 IndexedDB = 没有数据库
// 没法做:按标签搜索、按日期范围筛选、全文检索...
体验评估:
| 维度 | 评分 | 说明 |
|------|------|------|
| 文件系统 | ⭐⭐⭐⭐⭐ | OPFS 真文件系统 |
| 认证 | ⭐⭐⭐⭐⭐ | Cookie |
| 数据查询 | ⭐ | 致命短板:没有数据库 |
| 启动速度 | ⭐⭐⭐⭐ | LS 同步 |
| 元数据管理 | ⭐ | LS 太小存不了多少索引 |
作为 WebOS 核心:⭐⭐⭐(有文件系统有认证,但没有数据库就像一台没有注册表的电脑)
📝 这个组合的死穴是缺 IndexedDB。文件系统不等于数据库,你不能"按标签搜照片"、"按日期排邮件"。
组合 C:LocalStorage + IndexedDB + OPFS ⭐ 本地最强
┌───────────────────────────────────────────────────────────────┐
│ LocalStorage + IndexedDB + OPFS ★ 本地最强 ★ │
│ │
│ ┌────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │LocalStorage │ │ IndexedDB │ │ OPFS │ │
│ │ │ │ │ │ │ │
│ │ ⚡ 同步 │ │ 📊 结构化数据 │ │ 📁 文件系统 │ │
│ │ │ │ │ │ │ │
│ │ theme:dark │ │ files_meta: │ │ /files/ │ │
│ │ lang:zh-CN │ │ ├ id, name, │ │ {uuid}.bin ◄──┤ │
│ │ last_app │ │ │ path, size, │ │ {uuid}.bin │ │
│ │ db_version │ │ │ tags, date │ │ /sqlite/ │ │
│ │ boot_state │ │ ├ apps_registry │ │ main.db ◄─────┤ │
│ │ cached_user│ │ ├ permissions │ │ /wasm/ │ │
│ │ │ │ └ search_index │ │ module.wasm │ │
│ └──────┬─────┘ └────────┬─────────┘ └────────┬─────────┘ │
│ │ │ │ │
│ ① 启动配置 ② 查询引擎 ③ 存储后端 │
│ (注册表) (数据库) (硬盘) │
│ │ │ │ │
│ └─────────────────┼──────────────────────┘ │
│ ▼ │
│ 三层存储架构 │
│ L1 Cache → Database → File System │
│ │
│ ⚠️ 唯一缺失:没有 Cookie = 没有原生服务端认证 │
└───────────────────────────────────────────────────────────────┘
三层架构设计(类比操作系统):
┌─────────────────────────────────────────┐
│ 操作系统类比 │
│ │
│ LocalStorage ←→ Windows 注册表 │
│ / Linux /etc 配置文件 │
│ 快速、小量、启动必读 │
│ │
│ IndexedDB ←→ 系统数据库 │
│ (SQLite/LevelDB) │
│ 元数据、索引、事务 │
│ │
│ OPFS ←→ 文件系统 (ext4/NTFS) │
│ 目录层级、大文件、流式 │
└─────────────────────────────────────────┘
完整的 WebOS 存储层实现:
// ============================================
// WebOS Storage Layer - 三层架构
// ============================================
class WebOSStorage {
constructor() {
this.db = null; // IndexedDB
this.opfsRoot = null; // OPFS root
}
// =====================
// Layer 1: LocalStorage(启动引导)
// =====================
// 同步读取,零延迟启动
getBootConfig() {
return {
theme: localStorage.getItem('theme') || 'system',
language: localStorage.getItem('lang') || navigator.language,
lastUser: localStorage.getItem('last_user'),
dbVersion: parseInt(localStorage.getItem('db_version') || '0'),
bootCount: parseInt(localStorage.getItem('boot_count') || '0'),
};
}
// 快速写入启动配置
setBootConfig(key, value) {
localStorage.setItem(key, typeof value === 'string' ? value : JSON.stringify(value));
}
// =====================
// Layer 2: IndexedDB(查询引擎)
// =====================
async initDB() {
this.db = await openDB('WebOS', 3, {
upgrade(db, oldVersion) {
if (oldVersion < 1) {
// 文件元数据
const files = db.createObjectStore('files', { keyPath: 'id' });
files.createIndex('by_name', 'name');
files.createIndex('by_folder', 'folder');
files.createIndex('by_type', 'mimeType');
files.createIndex('by_date', 'modifiedAt');
files.createIndex('by_size', 'size');
files.createIndex('by_tags', 'tags', { multiEntry: true });
}
if (oldVersion < 2) {
// 应用注册表
const apps = db.createObjectStore('apps', { keyPath: 'appId' });
apps.createIndex('by_category', 'category');
// 权限表
db.createObjectStore('permissions', { keyPath: 'id' });
}
if (oldVersion < 3) {
// 搜索索引
const search = db.createObjectStore('search_index', { keyPath: 'term' });
search.createIndex('by_file', 'fileIds', { multiEntry: true });
}
}
});
// 更新LS中的版本号
localStorage.setItem('db_version', '3');
}
// 文件元数据操作
async queryFiles(options = {}) {
const { folder, type, tags, dateRange, sortBy = 'modifiedAt' } = options;
if (folder) {
return this.db.getAllFromIndex('files', 'by_folder', folder);
}
if (type) {
return this.db.getAllFromIndex('files', 'by_type', type);
}
if (tags && tags.length > 0) {
// 多标签交集查询
const results = await Promise.all(
tags.map(tag => this.db.getAllFromIndex('files', 'by_tags', tag))
);
// 取交集
const idSets = results.map(r => new Set(r.map(f => f.id)));
const intersection = results[0].filter(f =>
idSets.every(set => set.has(f.id))
);
return intersection;
}
if (dateRange) {
return this.db.getAllFromIndex('files', 'by_date',
IDBKeyRange.bound(dateRange.start, dateRange.end));
}
return this.db.getAll('files');
}
// =====================
// Layer 3: OPFS(文件存储后端)
// =====================
async initOPFS() {
this.opfsRoot = await navigator.storage.getDirectory();
// 确保基础目录结构
await this.ensureDir('files');
await this.ensureDir('sqlite');
await this.ensureDir('cache');
await this.ensureDir('temp');
}
async ensureDir(path) {
const parts = path.split('/');
let current = this.opfsRoot;
for (const part of parts) {
current = await current.getDirectoryHandle(part, { create: true });
}
return current;
}
// =====================
// 跨层协作:完整的文件操作
// =====================
async saveFile(virtualPath, content, metadata = {}) {
const fileId = crypto.randomUUID();
const fileName = virtualPath.split('/').pop();
const folder = virtualPath.substring(0, virtualPath.lastIndexOf('/')) || '/';
// Layer 3: 文件内容存 OPFS
const filesDir = await this.opfsRoot.getDirectoryHandle('files', { create: true });
const fileHandle = await filesDir.getFileHandle(`${fileId}.bin`, { create: true });
const writable = await fileHandle.createWritable();
await writable.write(content);
await writable.close();
const size = content.size || content.byteLength || new Blob([content]).size;
// Layer 2: 元数据存 IndexedDB
const meta = {
id: fileId,
name: fileName,
folder: folder,
virtualPath: virtualPath,
size: size,
mimeType: metadata.mimeType || 'application/octet-stream',
tags: metadata.tags || [],
createdAt: new Date(),
modifiedAt: new Date(),
permissions: metadata.permissions || { read: true, write: true }
};
await this.db.put('files', meta);
// Layer 1: 更新LS最近文件
const recent = JSON.parse(localStorage.getItem('recent_files') || '[]');
recent.unshift({ id: fileId, name: fileName, path: virtualPath, time: Date.now() });
localStorage.setItem('recent_files', JSON.stringify(recent.slice(0, 20)));
return meta;
}
async readFile(fileId) {
// Layer 2: 读取元数据
const meta = await this.db.get('files', fileId);
if (!meta) throw new Error('File not found');
// Layer 3: 读取文件内容
const filesDir = await this.opfsRoot.getDirectoryHandle('files');
const fileHandle = await filesDir.getFileHandle(`${fileId}.bin`);
const file = await fileHandle.getFile();
return { meta, content: file };
}
async deleteFile(fileId) {
// Layer 2: 删除元数据
const meta = await this.db.get('files', fileId);
await this.db.delete('files', fileId);
// Layer 3: 删除文件
const filesDir = await this.opfsRoot.getDirectoryHandle('files');
await filesDir.removeEntry(`${fileId}.bin`);
return meta;
}
// 全局搜索
async search(query) {
// 文件名搜索
const allFiles = await this.db.getAll('files');
const nameMatches = allFiles.filter(f =>
f.name.toLowerCase().includes(query.toLowerCase())
);
// 标签搜索
const tagMatches = await this.db.getAllFromIndex('files', 'by_tags', query);
// 合并去重
const seen = new Set();
const results = [];
for (const f of [...nameMatches, ...tagMatches]) {
if (!seen.has(f.id)) {
seen.add(f.id);
results.push(f);
}
}
return results;
}
}
// ============================================
// 使用示例
// ============================================
const storage = new WebOSStorage();
// 🚀 启动Phase 1: 同步,瞬间完成
const bootConfig = storage.getBootConfig();
applyTheme(bootConfig.theme);
showBootScreen(bootConfig);
// 🚀 启动Phase 2: 异步初始化
await Promise.all([
storage.initDB(),
storage.initOPFS()
]);
// 准备就绪,显示桌面
const recentFiles = JSON.parse(localStorage.getItem('recent_files') || '[]');
renderDesktop(bootConfig, recentFiles);
// 用户操作
await storage.saveFile('/home/user/docs/hello.md',
new Blob(['# Hello World']),
{ mimeType: 'text/markdown', tags: ['笔记', '教程'] }
);
const results = await storage.search('Hello');
console.log(results); // [{ name: 'hello.md', ... }]
体验评估:
| 维度 | 评分 | 说明 |
|------|------|------|
| 启动体验 | ⭐⭐⭐⭐⭐ | LS 同步秒开 |
| 总容量 | ⭐⭐⭐⭐⭐ | IDB + OPFS 双 GB 级 |
| 文件系统 | ⭐⭐⭐⭐⭐ | OPFS 真目录+真文件 |
| 数据查询 | ⭐⭐⭐⭐ | IDB 多维索引 |
| 性能 | ⭐⭐⭐⭐⭐ | 三层各司其职 |
| 认证/同步 | ⭐ | 缺 Cookie,需要自行实现 Token 管理 |
| 离线能力 | ⭐⭐⭐⭐⭐ | 完全离线可用 |
作为 WebOS 核心:⭐⭐⭐⭐⭐(纯客户端最强方案!)
📝 如果你做的是纯本地 WebOS(不需要服务端同步),这就是终极答案。缺 Cookie 的问题可以用 IDB 存 Token +
fetchheader 手动带来解决。
组合 D:Cookies + IndexedDB + OPFS
┌───────────────────────────────────────────────────────────────┐
│ Cookies + IndexedDB + OPFS │
│ │
│ ┌────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Cookies │ │ IndexedDB │ │ OPFS │ │
│ │ │ │ │ │ │ │
│ │ auth_token │ │ files_metadata │ │ /files/ │ │
│ │ session_id │ │ apps_registry │ │ /sqlite/ │ │
│ │ csrf_token │ │ sync_queue │ │ /downloads/ │ │
│ │ │ │ user_data │ │ /workspace/ │ │
│ └─────┬──────┘ └────────┬─────────┘ └────────┬─────────┘ │
│ │ │ │ │
│ 认证 + 同步 查询 + 索引 文件存储 │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 云同步 WebOS 架构 │ │
│ │ │ │
│ │ [浏览器] ◄──Cookie认证──► [服务器] │ │
│ │ │ │ │ │
│ │ IDB+OPFS(本地) 数据库+文件存储(云端) │ │
│ │ │ │ │ │
│ │ └──────── 双向同步 ────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ⚠️ 缺少 LocalStorage = 启动时没有同步快速配置读取 │
└───────────────────────────────────────────────────────────────┘
三角分工:
| 角色 | 存储 | 职责 |
|------|------|------|
| 网络认证层 | Cookies | HTTP 自动认证、会话管理 |
| 数据库层 | IndexedDB | 元数据、索引、同步队列、应用数据 |
| 文件系统层 | OPFS | 实际文件内容、SQLite 后端 |
优势独特之处:完整的云同步能力
// 完整的云文件同步方案
class CloudSyncEngine {
async syncFiles() {
const db = await openDB('WebOS', 1);
// 1. Cookie 自动认证,获取服务器文件列表
const serverFiles = await fetch('/api/files/list', {
credentials: 'include' // Cookie 自动带上
}).then(r => r.json());
// 2. IDB 中的本地文件列表
const localFiles = await db.getAll('files_metadata');
// 3. 差异对比
const { toDownload, toUpload, toDelete } = this.diff(serverFiles, localFiles);
// 4. 下载新文件:Cookie认证 → OPFS存储 → IDB记录
for (const file of toDownload) {
const response = await fetch(`/api/files/${file.id}/content`, {
credentials: 'include'
});
// 流式写入 OPFS
const root = await navigator.storage.getDirectory();
const filesDir = await root.getDirectoryHandle('files', { create: true });
const fh = await filesDir.getFileHandle(`${file.id}.bin`, { create: true });
const writable = await fh.createWritable();
await response.body.pipeTo(writable);
// 元数据存 IDB
await db.put('files_metadata', {
...file,
synced: true,
lastSync: Date.now()
});
}
// 5. 上传本地新文件
for (const file of toUpload) {
const root = await navigator.storage.getDirectory();
const filesDir = await root.getDirectoryHandle('files');
const fh = await filesDir.getFileHandle(`${file.id}.bin`);
const blob = await fh.getFile();
await fetch(`/api/files/${file.id}/upload`, {
method: 'PUT',
credentials: 'include', // Cookie 认证
body: blob
});
await db.put('files_metadata', { ...file, synced: true });
}
}
}
体验评估:
| 维度 | 评分 | 说明 |
|------|------|------|
| 文件系统 | ⭐⭐⭐⭐⭐ | OPFS |
| 数据库 | ⭐⭐⭐⭐ | IDB |
| 认证同步 | ⭐⭐⭐⭐⭐ | Cookie |
| 启动速度 | ⭐⭐⭐ | 无同步快速读取(要 await IDB) |
| 云同步 | ⭐⭐⭐⭐⭐ | 三者配合完美 |
| 离线能力 | ⭐⭐⭐⭐⭐ | IDB 队列 + OPFS 文件 |
痛点:缺 LocalStorage
// 启动时的尴尬:
// ❌ 不能这样(需要 await)
const config = await db.get('settings', 'boot_config'); // 用户要等...
// ✅ 如果有 LS 可以这样(同步,瞬间)
const config = JSON.parse(localStorage.getItem('boot_config')); // 立即可用
有人会说"那我把配置存 Cookie 里",但 Cookie 只有 4KB,而且 API 极其反人类,而且每次请求都会发送浪费带宽。
作为 WebOS 核心:⭐⭐⭐⭐(云同步方案最优,但启动体验略逊)
📝 如果是需要云同步的 WebOS,这个组合的核心能力是最完整的。启动优化可以用其他技巧(如 Service Worker 缓存首屏 HTML 内联配置)来弥补缺少 LS 的影响。
三个组合总结
┌──────────┐
│ 启动体验 │
│ (LS) │
└─────┬────┘
│
┌────────────────┼────────────────┐
│ │ │
┌────┴─────┐ ┌─────┴────┐ ┌──────┴────┐
│组合A │ │组合C │ │组合B │
│LS+CK+IDB│ │LS+IDB+OP│ │LS+CK+OPFS│
│传统Web │ │本地最强 │ │缺数据库 │
│终极形态 │ │⭐⭐⭐⭐⭐│ │有硬伤 │
│⭐⭐⭐⭐ │ │ │ │⭐⭐⭐ │
└──────────┘ └──────────┘ └───────────┘
┌──────────┐
│ 云同步 │
│(Cookie) │
└─────┬────┘
│
┌──────┴────┐
│组合D │
│CK+IDB+OP │
│云同步最佳 │
│⭐⭐⭐⭐ │
└───────────┘
四、四大金刚合体(1种)
终极组合:LocalStorage + Cookies + IndexedDB + OPFS
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ★★★ 四大存储全上阵 —— WebOS 完整存储架构 ★★★ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 应用层 (Apps) │ │
│ │ 📝 编辑器 📁 文件管理器 🖼️ 图片查看器 ⚙️ 设置 📧 邮件 │ │
│ └─────────────────────────────┬───────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────▼───────────────────────────────────┐ │
│ │ WebOS Storage API │ │
│ │ (统一存储访问抽象层) │ │
│ └──┬──────────┬─────────────┬────────────────┬────────────────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌──────┐ ┌───────┐ ┌──────────┐ ┌─────────────────┐ │
│ │ L1 │ │ L2 │ │ L3 │ │ L4 │ │
│ │ │ │ │ │ │ │ │ │
│ │ LS │ │Cookie │ │IndexedDB │ │ OPFS │ │
│ │ │ │ │ │ │ │ │ │
│ │BIOS/ │ │网络 │ │数据库 │ │文件系统 │ │
│ │注册表│ │认证层 │ │引擎 │ │(硬盘) │ │
│ │ │ │ │ │ │ │ │ │
│ │5-10MB│ │~320KB │ │~数GB │ │~数GB │ │
│ │同步 │ │自动 │ │异步 │ │异步/Worker同步 │ │
│ │快速 │ │HTTP │ │事务+索引 │ │目录+流式读写 │ │
│ └──┬───┘ └───┬───┘ └────┬─────┘ └───────┬─────────┘ │
│ │ │ │ │ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 数据流向详解 │ │
│ │ │ │
│ │ [启动] ──LS同步读取──► 主题/语言/布局 ──► 首帧渲染(0ms延迟) │ │
│ │ │ │
│ │ [认证] ──Cookie──► 自动发送至服务器 ──► 获取用户身份 │ │
│ │ │ │
│ │ [查询] ──IDB索引──► 文件列表/搜索/筛选 ──► UI展示数据 │ │
│ │ │ │
│ │ [读写] ──OPFS──► 流式读写大文件 ──► 编辑器/播放器 │ │
│ │ │ │
│ │ [同步] ──Cookie认证──► IDB对比差异 ──► OPFS下载/上传文件 │ │
│ │ │ │
│ │ [缓存] ──IDB热数据──► LS缓存子集 ──► 下次启动秒读 │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
四层架构详解:
┌─────────────────────────────────────────────────────────┐
│ 真实操作系统类比 │
│ │
│ Layer 1: LocalStorage ≈ BIOS/UEFI + 注册表 │
│ ────────────────────────────────────────────── │
│ • 系统首次启动时第一个读取的地方 │
│ • 存储最关键的少量配置 │
│ • 同步访问,零延迟 │
│ • 类比:BIOS 存启动顺序、注册表存桌面布局 │
│ │
│ Layer 2: Cookies ≈ 网卡固件 + 安全芯片 (TPM) │
│ ────────────────────────────────────────────── │
│ • 不存"数据",存"身份凭证" │
│ • 每次网络请求自动出示身份(像网卡自动带MAC地址) │
│ • HttpOnly = 硬件级安全(JS碰不到) │
│ • 类比:TPM 芯片存密钥、网卡自动握手 │
│ │
│ Layer 3: IndexedDB ≈ 系统数据库 + 注册表数据库 │
│ ────────────────────────────────────────────── │
│ • 结构化元数据存储 │
│ • 索引加速查询 │
│ • 事务保障一致性 │
│ • 类比:Windows 的 ESE 数据库、macOS 的 Spotlight 索引 │
│ │
│ Layer 4: OPFS ≈ 文件系统 (ext4 / NTFS / APFS) │
│ ────────────────────────────────────────────── │
│ • 真实的目录树 │
│ • 大文件流式读写 │
│ • 随机访问(seek) │
│ • 类比:就是硬盘上的文件系统本身 │
│ │
└─────────────────────────────────────────────────────────┘
完整的 WebOS 实现:
// ================================================================
// WebOS Complete Storage Architecture
// 四层存储统一管理
// ================================================================
class WebOS {
// ================================
// PHASE 1: 启动引导 (< 10ms)
// ================================
bootPhase1_BIOS() {
// 👉 LocalStorage: 同步读取,瞬间完成
console.time('boot-phase1');
this.config = {
theme: localStorage.getItem('sys:theme') || 'auto',
language: localStorage.getItem('sys:lang') || 'zh-CN',
fontSize: parseInt(localStorage.getItem('sys:fontsize') || '14'),
wallpaper: localStorage.getItem('sys:wallpaper') || 'default',
windowPositions: JSON.parse(localStorage.getItem('sys:windows') || '{}'),
desktopIcons: JSON.parse(localStorage.getItem('sys:desktop') || '[]'),
lastUser: localStorage.getItem('sys:last_user'),
bootCount: parseInt(localStorage.getItem('sys:boot_count') || '0') + 1,
};
// 更新启动计数
localStorage.setItem('sys:boot_count', String(this.config.bootCount));
// 立即应用视觉配置(用户看到第一帧就是正确的主题和布局)
document.documentElement.style.setProperty('--font-size', `${this.config.fontSize}px`);
document.documentElement.setAttribute('data-theme', this.config.theme);
console.timeEnd('boot-phase1'); // 通常 < 5ms
return this.config;
}
// ================================
// PHASE 2: 身份验证
// ================================
async bootPhase2_Auth() {
// 👉 Cookies: 自动携带认证信息
console.time('boot-phase2');
try {
const res = await fetch('/api/auth/session', {
credentials: 'include', // Cookie 自动发送
headers: { 'X-Boot-Count': String(this.config.bootCount) }
});
if (res.ok) {
this.user = await res.json();
// 缓存用户名到 LS(下次启动立即显示)
localStorage.setItem('sys:last_user', this.user.name);
localStorage.setItem('sys:user_avatar_url', this.user.avatarUrl);
} else if (res.status === 401) {
// 未认证 → 显示登录界面
this.user = null;
return this.showLoginScreen();
}
} catch (e) {
// 离线模式 → 使用上次缓存的用户信息
this.user = {
name: localStorage.getItem('sys:last_user') || 'Guest',
offline: true
};
}
console.timeEnd('boot-phase2');
}
// ================================
// PHASE 3: 数据库初始化
// ================================
async bootPhase3_Database() {
// 👉 IndexedDB: 主数据库
console.time('boot-phase3');
this.db = await openDB('WebOS', 5, {
upgrade(db, oldVersion) {
// 文件元数据表
if (oldVersion < 1) {
const files = db.createObjectStore('files', { keyPath: 'id' });
files.createIndex('by_path', 'virtualPath', { unique: true });
files.createIndex('by_folder', 'folder');
files.createIndex('by_name', 'name');
files.createIndex('by_type', 'mimeType');
files.createIndex('by_modified', 'modifiedAt');
files.createIndex('by_size', 'size');
files.createIndex('by_tags', 'tags', { multiEntry: true });
files.createIndex('by_ext', 'extension');
}
// 应用注册表
if (oldVersion < 2) {
const apps = db.createObjectStore('apps', { keyPath: 'id' });
apps.createIndex('by_name', 'name');
apps.createIndex('by_category', 'category');
apps.createIndex('by_mimeType', 'supportedTypes', { multiEntry: true });
}
// 系统设置(完整版,LS 里只存子集)
if (oldVersion < 3) {
db.createObjectStore('settings', { keyPath: 'key' });
}
// 通知/消息
if (oldVersion < 4) {
const notifications = db.createObjectStore('notifications',
{ keyPath: 'id', autoIncrement: true });
notifications.createIndex('by_date', 'createdAt');
notifications.createIndex('by_read', 'read');
}
// 同步队列
if (oldVersion < 5) {
const syncQueue = db.createObjectStore('sync_queue',
{ keyPath: 'id', autoIncrement: true });
syncQueue.createIndex('by_priority', 'priority');
syncQueue.createIndex('by_status', 'status');
}
}
});
console.timeEnd('boot-phase3');
}
// ================================
// PHASE 4: 文件系统挂载
// ================================
async bootPhase4_FileSystem() {
// 👉 OPFS: 文件系统
console.time('boot-phase4');
this.fsRoot = await navigator.storage.getDirectory();
// 申请持久化存储
if (navigator.storage && navigator.storage.persist) {
const persistent = await navigator.storage.persist();
console.log(`Storage persisted: ${persistent}`);
}
// 初始化目录结构
const dirs = [
'home',
'home/Desktop',
'home/Documents',
'home/Downloads',
'home/Pictures',
'home/Music',
'home/Videos',
'system',
'system/fonts',
'system/icons',
'system/wallpapers',
'apps',
'tmp',
'var/sqlite',
'var/cache',
'var/log'
];
for (const dir of dirs) {
await this.ensureDirectory(dir);
}
console.timeEnd('boot-phase4');
}
async ensureDirectory(path) {
const parts = path.split('/');
let current = this.fsRoot;
for (const part of parts) {
current = await current.getDirectoryHandle(part, { create: true });
}
return current;
}
// ================================
// 完整启动流程
// ================================
async boot() {
console.log('🖥️ WebOS Booting...');
console.time('total-boot');
// Phase 1: BIOS(同步,瞬间)
const config = this.bootPhase1_BIOS();
this.renderBootScreen(config); // 立即显示启动画面
// Phase 2-4: 并行初始化
await Promise.all([
this.bootPhase2_Auth(),
this.bootPhase3_Database(),
this.bootPhase4_FileSystem()
]);
// Phase 5: 启动完成
console.timeEnd('total-boot');
console.log('🖥️ WebOS Ready!');
this.renderDesktop();
}
// ================================
// 核心文件操作(四层协作)
// ================================
async createFile(virtualPath, content, options = {}) {
const id = crypto.randomUUID();
const name = virtualPath.split('/').pop();
const folder = virtualPath.substring(0, virtualPath.lastIndexOf('/')) || '/';
const ext = name.includes('.') ? name.split('.').pop().toLowerCase() : '';
const blob = content instanceof Blob ? content : new Blob([content]);
// === Layer 4 (OPFS): 写入文件内容 ===
const filesDir = await this.ensureDirectory('home');
// 用虚拟路径映射真实 OPFS 路径
const targetDir = await this.ensureDirectory(
'home' + folder.replace(/^\/home/, '')
);
const fileHandle = await targetDir.getFileHandle(name, { create: true });
const writable = await fileHandle.createWritable();
await writable.write(blob);
await writable.close();
// === Layer 3 (IndexedDB): 记录元数据 ===
const metadata = {
id,
name,
folder,
virtualPath,
extension: ext,
size: blob.size,
mimeType: options.mimeType || this.guessMimeType(ext),
tags: options.tags || [],
createdAt: new Date(),
modifiedAt: new Date(),
owner: this.user?.name || 'system',
permissions: options.permissions || 0o644,
synced: false,
checksum: await this.computeChecksum(blob)
};
await this.db.put('files', metadata);
// === Layer 1 (LocalStorage): 更新热缓存 ===
const recent = JSON.parse(localStorage.getItem('sys:recent_files') || '[]');
recent.unshift({
id, name, path: virtualPath,
time: Date.now(), type: metadata.mimeType
});
localStorage.setItem('sys:recent_files', JSON.stringify(recent.slice(0, 30)));
// === Layer 2 (Cookie 辅助): 标记有待同步数据 ===
// 通过设置一个轻量 Cookie,服务端知道下次请求时有数据要同步
if (!this.user?.offline) {
document.cookie = `pending_sync=1; path=/; max-age=86400; SameSite=Strict`;
// 加入同步队列
await this.db.add('sync_queue', {
action: 'upload',
fileId: id,
virtualPath,
priority: options.priority || 5,
status: 'pending',
createdAt: new Date()
});
}
return metadata;
}
// 高级文件搜索(IDB 索引驱动)
async search(query, options = {}) {
const results = {
byName: [],
byTag: [],
byType: [],
byDate: [],
};
const allFiles = await this.db.getAll('files');
// 文件名模糊搜索
if (query) {
const q = query.toLowerCase();
results.byName = allFiles.filter(f => f.name.toLowerCase().includes(q));
}
// 按标签
if (options.tags) {
for (const tag of options.tags) {
const tagged = await this.db.getAllFromIndex('files', 'by_tags', tag);
results.byTag.push(...tagged);
}
}
// 按类型
if (options.mimeType) {
results.byType = await this.db.getAllFromIndex('files', 'by_type', options.mimeType);
}
// 按日期范围
if (options.dateRange) {
results.byDate = await this.db.getAllFromIndex('files', 'by_modified',
IDBKeyRange.bound(options.dateRange.from, options.dateRange.to));
}
// 按大小
if (options.minSize || options.maxSize) {
const sizeResults = await this.db.getAllFromIndex('files', 'by_size',
IDBKeyRange.bound(options.minSize || 0, options.maxSize || Infinity));
results.bySize = sizeResults;
}
return results;
}
// 云同步引擎(Cookie + IDB + OPFS 三层协作)
async syncWithCloud() {
if (this.user?.offline) {
console.log('Offline mode, skipping sync');
return;
}
// 1. Cookie 自动认证,获取同步状态
const syncStatus = await fetch('/api/sync/status', {
credentials: 'include'
}).then(r => r.json());
// 2. IDB: 获取待同步队列
const pendingUploads = await this.db.getAllFromIndex(
'sync_queue', 'by_status', 'pending'
);
// 3. 上传(OPFS读文件 → Cookie认证 → 发送到服务器)
for (const task of pendingUploads) {
try {
const fileMeta = await this.db.get('files', task.fileId);
// 从 OPFS 读取文件内容
const dirHandle = await this.navigateToDir(fileMeta.folder);
const fileHandle = await dirHandle.getFileHandle(fileMeta.name);
const file = await fileHandle.getFile();
// 上传(Cookie 自动认证)
const formData = new FormData();
formData.append('file', file);
formData.append('metadata', JSON.stringify(fileMeta));
await fetch('/api/files/upload', {
method: 'POST',
credentials: 'include',
body: formData
});
// 更新同步状态
await this.db.put('sync_queue', { ...task, status: 'completed' });
await this.db.put('files', { ...fileMeta, synced: true });
} catch (e) {
await this.db.put('sync_queue', {
...task, status: 'failed', error: e.message
});
}
}
// 4. 下载服务端新文件
for (const serverFile of syncStatus.newFiles) {
const response = await fetch(`/api/files/${serverFile.id}/download`, {
credentials: 'include'
});
// 流式写入 OPFS
const dirHandle = await this.ensureDirectory(
'home' + serverFile.folder.replace(/^\/home/, '')
);
const fh = await dirHandle.getFileHandle(serverFile.name, { create: true });
const writable = await fh.createWritable();
await response.body.pipeTo(writable);
// 元数据存 IDB
await this.db.put('files', {
...serverFile,
synced: true,
lastSync: Date.now()
});
}
// 5. 清除同步标记 Cookie
document.cookie = 'pending_sync=; path=/; max-age=0';
// 6. 更新 LS 最后同步时间
localStorage.setItem('sys:last_sync', new Date().toISOString());
}
// 获取系统存储信息
async getStorageInfo() {
const estimate = await navigator.storage.estimate();
return {
// 总体
totalQuota: this.formatBytes(estimate.quota),
totalUsage: this.formatBytes(estimate.usage),
usagePercent: ((estimate.usage / estimate.quota) * 100).toFixed(1) + '%',
// 各层
layers: {
localStorage: {
used: this.formatBytes(
Object.keys(localStorage).reduce((sum, key) =>
sum + localStorage.getItem(key).length * 2, 0) // UTF-16
),
limit: '5-10MB',
entries: localStorage.length
},
cookies: {
used: this.formatBytes(document.cookie.length),
limit: '~4KB/条',
entries: document.cookie.split(';').filter(Boolean).length
},
indexedDB: {
used: estimate.usageDetails?.indexedDB
? this.formatBytes(estimate.usageDetails.indexedDB)
: 'N/A',
fileCount: await this.db.count('files'),
appCount: await this.db.count('apps')
},
opfs: {
used: estimate.usageDetails?.fileSystem
? this.formatBytes(estimate.usageDetails.fileSystem)
: 'N/A',
directories: await this.countDirectories(this.fsRoot)
}
}
};
}
formatBytes(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return (bytes / Math.pow(k, i)).toFixed(1) + ' ' + sizes[i];
}
async countDirectories(dirHandle) {
let count = 0;
for await (const [, handle] of dirHandle) {
if (handle.kind === 'directory') {
count += 1 + await this.countDirectories(handle);
}
}
return count;
}
guessMimeType(ext) {
const map = {
txt: 'text/plain', md: 'text/markdown', html: 'text/html',
js: 'text/javascript', css: 'text/css', json: 'application/json',
png: 'image/png', jpg: 'image/jpeg', gif: 'image/gif', webp: 'image/webp',
svg: 'image/svg+xml', pdf: 'application/pdf', zip: 'application/zip',
mp3: 'audio/mpeg', mp4: 'video/mp4', wasm: 'application/wasm',
};
return map[ext] || 'application/octet-stream';
}
async computeChecksum(blob) {
const buffer = await blob.arrayBuffer();
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
return Array.from(new Uint8Array(hashBuffer))
.map(b => b.toString(16).padStart(2, '0')).join('');
}
async navigateToDir(virtualPath) {
const cleanPath = virtualPath.replace(/^\//, '');
const parts = cleanPath.split('/').filter(Boolean);
let current = this.fsRoot;
for (const part of parts) {
current = await current.getDirectoryHandle(part, { create: true });
}
return current;
}
}
// ================================================================
// 启动!
// ================================================================
const webos = new WebOS();
webos.boot().then(() => {
console.log('Welcome to WebOS!');
});
四层协作流程图:
用户动作:保存文件
━━━━━━━━━━━━━━━━━
用户点击"保存"
│
▼
┌──────────────────────────────┐
│ WebOS Storage API │
│ createFile(path, data) │
└──────────┬───────────────────┘
│
┌────────────┼────────────┬──────────────┐
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌──────────┐ ┌──────────┐
│ Layer 4 │ │ Layer 3 │ │ Layer 1 │ │ Layer 2 │
│ OPFS │ │ IDB │ │ LS │ │ Cookie │
│ │ │ │ │ │ │ │
│ 写入 │ │ 存元数据 │ │ 更新最近 │ │ 标记待 │
│ 文件内容 │ │ 建索引 │ │ 文件列表 │ │ 同步 │
│ (Blob) │ │ 加事务 │ │ (同步) │ │ │
└────┬────┘ └────┬────┘ └─────┬────┘ └─────┬────┘
│ │ │ │
▼ ▼ ▼ ▼
文件持久化 可搜索/筛选 下次启动秒显示 服务器知道
有新文件了
用户动作:搜索文件
━━━━━━━━━━━━━━━━━
用户输入 "旅行 .jpg 2024年"
│
▼
┌──────────────┐
│ Layer 3 │
│ IndexedDB │
│ │
│ 索引查询: │
│ by_tags:"旅行"│
│ by_ext:"jpg" │
│ by_date:2024 │
└──────┬───────┘
│
▼
返回元数据列表
[{id, name, folder, size, ...}]
│
▼
用户点击某张图片
│
▼
┌──────────────┐
│ Layer 4 │
│ OPFS │
│ │
│ 读取文件内容 │
│ getFile() │
└──────┬───────┘
│
▼
图片查看器显示
用户动作:启动系统
━━━━━━━━━━━━━━━━━
0ms ┌──────────────────────┐
│ Layer 1: LocalStorage │ 同步!
│ 读取主题、语言、布局 │ 瞬间完成
│ 立即渲染首帧 │
└───────────┬──────────┘
│
5ms ┌───────────┴──────────┐
│ 显示启动画面 │
│ (已经是正确的主题色) │
└───────────┬──────────┘
│ 并行 ↓
5ms ┌───────────┴──────────────────────────┐
│ │
┌──────┴───────┐ ┌────────────┐ ┌─────────────┴─┐
│ Layer 2 │ │ Layer 3 │ │ Layer 4 │
│ Cookie认证 │ │ IDB初始化 │ │ OPFS挂载 │
│ fetch+cookie │ │ openDB() │ │ getDirectory() │
└──────┬───────┘ └──────┬─────┘ └───────┬────────┘
│ │ │
~100ms └────────────────┼───────────────┘
│
▼
启动完成,显示桌面
最近文件来自 LS 缓存(瞬间显示)
完整文件列表从 IDB 异步加载
终极评估:
| 维度 | 评分 | 说明 |
|---|---|---|
| 启动体验 | ⭐⭐⭐⭐⭐ | LS 同步秒开首帧 |
| 容量 | ⭐⭐⭐⭐⭐ | IDB+OPFS 双 GB 级 |
| 文件系统 | ⭐⭐⭐⭐⭐ | OPFS 真目录+真文件 |
| 数据查询 | ⭐⭐⭐⭐⭐ | IDB 多维索引+事务 |
| 认证/安全 | ⭐⭐⭐⭐⭐ | Cookie HttpOnly+Secure |
| 云同步 | ⭐⭐⭐⭐⭐ | Cookie认证+IDB队列+OPFS流式传输 |
| 离线能力 | ⭐⭐⭐⭐⭐ | 完全离线可用 |
| 性能 | ⭐⭐⭐⭐⭐ | 各层各司其职,无瓶颈 |
| 复杂度 | ⚠️ | 架构复杂,需要统一抽象层 |
作为 WebOS 核心:⭐⭐⭐⭐⭐+ (终极方案,无短板)
五、终极总结
所有 15 种组合一览表
┌─────┬──────────────────────────┬────┬────┬────┬────┬───────┐
│ 编号 │ 组合 │容量│查询│文件│认证│WebOS │
├─────┼──────────────────────────┼────┼────┼────┼────┼───────┤
│ │ 【单兵】 │ │ │ │ │ │
│ 1 │ LocalStorage │ ⭐ │ ❌ │ ❌ │ ❌ │ ⭐ │
│ 2 │ Cookies │ ❌ │ ❌ │ ❌ │⭐⭐⭐⭐⭐│ ⭐ │
│ 3 │ IndexedDB │⭐⭐⭐⭐│⭐⭐⭐│⭐⭐│ ❌ │⭐⭐⭐⭐│
│ 4 │ OPFS │⭐⭐⭐⭐⭐│ ❌ │⭐⭐⭐⭐⭐│ ❌ │⭐⭐⭐⭐│
├─────┼──────────────────────────┼────┼────┼────┼────┼───────┤
│ │ 【两两组合】 │ │ │ │ │ │
│ 5 │ LS + Cookie │ ⭐ │ ❌ │ ❌ │⭐⭐⭐⭐⭐│⭐⭐ │
│ 6 │ LS + IDB │⭐⭐⭐⭐│⭐⭐⭐│⭐⭐│ ❌ │⭐⭐⭐⭐│
│ 7 │ LS + OPFS │⭐⭐⭐⭐⭐│ ❌ │⭐⭐⭐⭐⭐│ ❌ │⭐⭐⭐ │
│ 8 │ Cookie + IDB │⭐⭐⭐⭐│⭐⭐⭐│⭐⭐│⭐⭐⭐⭐⭐│⭐⭐⭐ │
│ 9 │ Cookie + OPFS │⭐⭐⭐⭐⭐│ ❌ │⭐⭐⭐⭐⭐│⭐⭐⭐⭐⭐│⭐⭐ │
│ 10 │ IDB + OPFS ★ │⭐⭐⭐⭐⭐│⭐⭐⭐│⭐⭐⭐⭐⭐│ ❌ │⭐⭐⭐⭐⭐│
├─────┼──────────────────────────┼────┼────┼────┼────┼───────┤
│ │ 【三个组合】 │ │ │ │ │ │
│ 11 │ LS + Cookie + IDB │⭐⭐⭐⭐│⭐⭐⭐│⭐⭐│⭐⭐⭐⭐⭐│⭐⭐⭐⭐│
│ 12 │ LS + Cookie + OPFS │⭐⭐⭐⭐⭐│ ❌ │⭐⭐⭐⭐⭐│⭐⭐⭐⭐⭐│⭐⭐⭐ │
│ 13 │ LS + IDB + OPFS ★ │⭐⭐⭐⭐⭐│⭐⭐⭐│⭐⭐⭐⭐⭐│ ❌ │⭐⭐⭐⭐⭐│
│ 14 │ Cookie + IDB + OPFS │⭐⭐⭐⭐⭐│⭐⭐⭐│⭐⭐⭐⭐⭐│⭐⭐⭐⭐⭐│⭐⭐⭐⭐│
├─────┼──────────────────────────┼────┼────┼────┼────┼───────┤
│ │ 【全上】 │ │ │ │ │ │
│ 15 │ LS+Cookie+IDB+OPFS ★★ │⭐⭐⭐⭐⭐│⭐⭐⭐│⭐⭐⭐⭐⭐│⭐⭐⭐⭐⭐│⭐⭐⭐⭐⭐+│
└─────┴──────────────────────────┴────┴────┴────┴────┴───────┘
★ = 两两最佳
★★ = 终极方案
选型决策树
你要做什么?
│
├── 简单网页/传统Web应用
│ └── 👉 LocalStorage + Cookies (组合5)
│
├── PWA / 离线优先应用(不需要文件系统)
│ └── 👉 LocalStorage + Cookies + IndexedDB (组合11)
│
├── 纯本地 WebOS(不需要服务端)
│ └── 👉 LocalStorage + IndexedDB + OPFS (组合13)
│
├── 需要云同步的 WebOS
│ └── 👉 全部四个 (组合15)
│
├── 云盘/文件管理类应用
│ └── 👉 Cookies + IndexedDB + OPFS (组合14)
│
├── 编辑器/IDE 类(需要高性能文件操作)
│ └── 👉 IndexedDB + OPFS (组合10)
│
└── 预算有限、快速原型
└── 👉 IndexedDB 单兵 (编号3)
最终建议
如果你真的要做一个 WebOS,答案就是全部四个都上。 每个存储都有其不可替代的独特价值:
- LocalStorage:唯一的同步快速读取 → 启动体验
- Cookies:唯一能自动随 HTTP 请求发送 → 服务端认证
- IndexedDB:唯一的结构化查询+事务引擎 → 数据库
- OPFS:唯一的真文件系统 + 高性能随机读写 → 硬盘
它们不是竞争关系,而是操作系统存储栈的四个层次。
Top comments (0)