浏览器存储的7层地狱:从Cookie到Origin Private FileSystem

371 阅读2分钟

浏览器存储的7层地狱:从Cookie到Origin Private FileSystem


第一层:Cookie地狱 —— 古老的诅咒

存储容量:4KB

// 原始API的恐怖之处
document.cookie = `user=John; path=/; expires=${new Date(2025,0,1).toUTCString()}`;
document.cookie = `theme=dark; SameSite=Lax; Secure`;

// 现代封装方案
const cookieStorage = {
  set(key, value, days=7) {
    const date = new Date();
    date.setTime(date.getTime() + (days*24*60*60*1000));
    document.cookie = `${key}=${value};expires=${date.toUTCString()};path=/`;
  },
  get(key) {
    return document.cookie.split('; ')
      .find(row => row.startsWith(`${key}=`))
      ?.split('=')[1];
  }
}

灵魂拷问

  • 为什么每个请求都携带Cookie导致带宽浪费?
  • 如何避免XSS攻击窃取敏感Cookie?(HttpOnly + Secure)
  • 现代前端是否应该完全弃用Cookie?(身份验证除外)

第二层:Web Storage炼狱 —— 双生子困境

localStorage vs sessionStorage

// 容量测试(5MB理论值)
const testData = new Array(5 * 1024 * 1024).fill('a').join('');
try {
  localStorage.setItem('stressTest', testData);
} catch (e) {
  console.error('存储超出限制:', e);
}

// 类型转换陷阱
localStorage.setItem('number', 123); 
console.log(typeof localStorage.getItem('number')); // "string"

// 安全监听(同源页面间)
window.addEventListener('storage', (e) => {
  console.log(`${e.key} 被修改为 ${e.newValue}`);
});

性能警示

  • 大容量存储导致主线程阻塞
  • 同步API对SPA应用的性能影响

第三层:IndexedDB深渊 —— 非关系型迷宫

现代浏览器数据库

// 创建数据库
const request = indexedDB.open('MyDatabase', 2);

request.onupgradeneeded = (e) => {
  const db = e.target.result;
  if (!db.objectStoreNames.contains('files')) {
    const store = db.createObjectStore('files', { 
      keyPath: 'id',
      autoIncrement: true 
    });
    store.createIndex('by_type', 'type', { unique: false });
  }
};

// 事务操作
const tx = db.transaction('files', 'readwrite');
tx.objectStore('files').put({
  type: 'image',
  content: blobData,
  timestamp: Date.now()
});

进阶技巧

  • 使用Dexie.js等库简化操作
  • Web Worker中运行避免阻塞
  • 二进制数据存储(File/Blob)

第四层:Web SQL废墟 —— 被遗忘的遗迹

虽死犹存的SQL

// 最后的挽歌
const db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);
db.transaction((tx) => {
  tx.executeSql('CREATE TABLE IF NOT EXISTS logs (id unique, message)');
  tx.executeSql('INSERT INTO logs (id, message) VALUES (?, ?)', [Date.now(), 'error']);
});

墓碑警告

  • 2010年W3C停止维护
  • 仅Chrome和Safari部分支持

第五层:Cache API幻境 —— Service Worker的领域

离线缓存策略

// 注册Service Worker
navigator.serviceWorker.register('/sw.js');

// sw.js核心代码
const CACHE_NAME = 'v1';
self.addEventListener('install', (e) => {
  e.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => cache.addAll([
        '/',
        '/app.js',
        '/style.css'
      ]))
  );
});

// 网络优先回退策略
self.addEventListener('fetch', (e) => {
  e.respondWith(
    fetch(e.request)
      .catch(() => caches.match(e.request))
  );
});

缓存策略矩阵

策略类型适用场景代码实现难度
缓存优先静态资源⭐⭐
网络优先实时性要求高⭐⭐⭐
仅缓存离线应用
动态更新频繁更新内容⭐⭐⭐⭐

第六层:File System Access API悬崖 —— 用户授权禁区

本地文件系统交互

// 获取文件句柄
const handle = await window.showOpenFilePicker({
  types: [{
    description: 'Text Files',
    accept: {'text/plain': ['.txt']}
  }]
});

// 写入操作
const writable = await handle.createWritable();
await writable.write('Hello World');
await writable.close();

// 目录操作
const dirHandle = await window.showDirectoryPicker();
for await (const entry of dirHandle.values()) {
  console.log(entry.kind, entry.name);
}

权限管理

  • 用户显式授权机制
  • 沙盒限制与隐私保护

第七层:Origin Private FileSystem (OPFS) 终极试炼

浏览器私有文件系统

// 获取OPFS根目录
const root = await navigator.storage.getDirectory();

// 创建并写入文件
const fileHandle = await root.getFileHandle('data.bin', { create: true });
const writable = await fileHandle.createWritable();
await writable.write(new Uint8Array([1,2,3,4,5]));
await writable.close();

// 高性能随机访问
const file = await fileHandle.getFile();
const buffer = await file.arrayBuffer();
const accessHandle = await fileHandle.createSyncAccessHandle();
accessHandle.write(buffer, { at: 0 });
accessHandle.flush();
accessHandle.close();

性能对比

操作类型传统IndexedDBOPFS
100MB写入3200ms850ms
随机读取需要反序列化直接内存访问
大文件处理容易崩溃稳定支持

地狱生存指南(技术选型决策树)

graph TD
    A[需要持久化存储?] -->|否| B[临时存储?]
    A -->|是| C[数据量大小?]
    B -->|是| D[sessionStorage]
    B -->|否| E[放弃存储]
    C -->|小数据 <1MB| F[localStorage]
    C -->|结构化数据| G[IndexedDB]
    C -->|文件操作| H[OPFS]
    C -->|离线缓存| I[Cache API]

灵魂拷问

  1. 为什么OPFS能比传统存储快3-5倍?
    答案:绕过序列化/反序列化过程,直接操作二进制内存

  2. 如何实现浏览器存储加密?
    方案:Web Crypto API + 客户端加密策略

  3. 隐私模式下哪些存储可用?
    真相:所有存储仍可用,但会话结束即清除


创作工具推荐

  1. StorageManager API - 查看存储配额
  2. Dexie.js - IndexedDB优雅封装
  3. browser-fs-access - 文件访问polyfill