HTML5 本地存储终极指南

HTML5 本地存储终极指南:localStorage vs IndexedDB + 离线缓存完整方案

别再把所有数据一股脑塞进 localStorage 了。这篇文章帮你彻底搞清楚三个问题:该用哪个存?怎么配合离线缓存?性能差多少?


一、先看结论:一张表选对存储方案

维度 localStorage IndexedDB
容量 5--10 MB / 域名 数百 MB(可达磁盘 50%)
数据类型 仅字符串,对象需 JSON 序列化 任意结构化数据 + Blob / ArrayBuffer
操作方式 同步,阻塞主线程 异步,不阻塞 UI
查询能力 只能按 key 取,无索引 支持索引、范围查询、游标遍历
事务 ❌ 无 ✅ ACID 事务
过期机制 ❌ 永久(除非手动删) ❌ 永久
跨标签页共享
兼容性 IE8+ 全部支持 Chrome/Firefox/Safari/Edge 均支持

一句话原则:能塞进一张纸条的数据用 localStorage,需要建数据库的用 IndexedDB。


二、性能实测:差距不是一点半点

操作 数据量 localStorage 耗时 IndexedDB 耗时
写入单条小记录 1 KB 0.3--0.5 ms 1.8--2.1 ms
读取单条小记录 1 KB 0.3 ms 1.8 ms
写入 1000 条(共 1 MB) 1 MB 1250 ms ⚠️ 320 ms
读取 1000 条(共 1 MB) 1 MB 980 ms ⚠️ 210 ms
条件查询 100 条 --- 手动 filter(全量读取) 15 ms(索引精准定位)

超过 1 MB 的 JSON 写入,localStorage 能让主线程卡顿 400 ms 以上;同样的数据,IndexedDB 用事务分片读取,耗时压在 20--60 ms,UI 完全不卡。

更致命的是 :localStorage 写入超限会静默失败或抛 QuotaExceededError,很难及时发现;IndexedDB 会主动触发 storagequotaexceeded 事件,你能感知到。


三、localStorage:简单到极致,也弱到极致

适合存什么?

  • 用户主题偏好(theme: 'dark'
  • 登录态标记、访客 ID
  • 表单草稿(几条字段)
  • 筛选条件(不超 500 字符)

核心 API

javascript 复制代码
js
localStorage.setItem('key', 'value');
localStorage.getItem('key');
localStorage.removeItem('key');
localStorage.clear();

必须知道的坑

详情
只能存字符串 对象必须 JSON.stringify(),读取时 JSON.parse()
无搜索能力 查"2024年创建的未完成任务"?全量读出再 filter,数据量一大就崩
同步阻塞 存 2 MB JSON,getItem() 卡主线程 400 ms+
被误清风险高 用户勾"清除历史记录"时,localStorage 内容会被一并清除
无事务 "扣款+记账+记录流水"三步,任何一步失败,数据就不一致

四、IndexedDB:真正意义上的前端数据库

适合存什么?

  • 几百条用户收货地址
  • 离线地图瓦片、本地日志文件
  • 按时间/标签/状态筛选的笔记
  • 缓存大量 API 响应集合
  • 音视频二进制资源(Blob / ArrayBuffer)

核心流程:三步走

ini 复制代码
js
// 1. 打开/创建数据库
const request = indexedDB.open('MyAppDB', 2);

// 2. 版本升级时建表、建索引
request.onupgradeneeded = (e) => {
  const db = e.target.result;
  if (!db.objectStoreNames.contains('notes')) {
    const store = db.createObjectStore('notes', { keyPath: 'id', autoIncrement: true });
    store.createIndex('createdAt', 'createdAt', { unique: false });
    store.createIndex('status', 'status', { unique: false });
  }
};

// 3. 事务中增删改查
request.onsuccess = (e) => {
  const db = e.target.result;
  const tx = db.transaction('notes', 'readwrite');
  const store = tx.objectStore('notes');
  store.add({ title: 'Hello', status: 'draft', createdAt: Date.now() });
};

查询示例:按时间范围 + 状态筛选

ini 复制代码
js
const tx = db.transaction('notes', 'readonly');
const store = tx.objectStore('notes');
const idx = store.index('createdAt');
const range = IDBKeyRange.bound(Date.now() - 7 * 86400000, Date.now()); // 过去7天

const request = idx.openCursor(range);
request.onsuccess = (e) => {
  const cursor = e.target.result;
  if (cursor && cursor.value.status === 'draft') {
    console.log(cursor.value);
  }
  cursor?.continue();
};

localStorage 做同样的查询,需要把全部数据 JSON.parse() 出来再 filter(),性能差一个数量级。

推荐封装库

原生 API 确实底层,建议用 idb(轻量、Promise 化、TypeScript 友好),把上面 30 行代码压缩到 5 行:

javascript 复制代码
js
import { openDB } from 'idb';
const db = await openDB('MyAppDB', 1, {
  upgrade(db) {
    db.createObjectStore('notes', { keyPath: 'id', autoIncrement: true });
  }
});
await db.add('notes', { title: 'Hello', status: 'draft' });

五、离线缓存完整方案:Service Worker + Cache API

localStorage 和 IndexedDB 解决的是数据持久化 ,离线缓存解决的是资源可访问。两者配合,才是完整的离线优先(PWA)方案。

⚠️ Application Cache(manifest 文件)已被 W3C 废弃,不要再用了

架构分层

层级 方案 存什么
UI 状态 localStorage 主题、语言、折叠状态
业务数据 IndexedDB 笔记、待办、离线草稿
网络资源 Cache API + Service Worker HTML/CSS/JS/图片/音视频

Step 1:注册 Service Worker

c 复制代码
js
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js')
    .then(reg => console.log('SW 注册成功', reg))
    .catch(err => console.log('SW 注册失败', err));
}

Step 2:sw.js 实现缓存策略

ini 复制代码
js
const CACHE_NAME = 'my-app-v1';
const urlsToCache = ['/', '/index.html', '/styles/main.css', '/scripts/app.js', '/images/logo.png'];

// 安装:预缓存核心资源
self.addEventListener('install', (e) => {
  e.waitUntil(
    caches.open(CACHE_NAME).then(cache => cache.addAll(urlsToCache))
  );
});

// 拦截请求:缓存优先,未命中再网络
self.addEventListener('fetch', (e) => {
  e.respondWith(
    caches.match(e.request).then(cached => {
      if (cached) return cached;
      return fetch(e.request).then(resp => {
        if (!resp || resp.status !== 200) return resp;
        const clone = resp.clone();
        caches.open(CACHE_NAME).then(cache => cache.put(e.request, clone));
        return resp;
      });
    })
  );
});

// 更新机制:SW 版本号变更触发
self.addEventListener('activate', (e) => {
  e.waitUntil(
    caches.keys().then(keys =>
      Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k)))
    )
  );
});

Step 3:网络恢复后数据同步

javascript 复制代码
js
// sw.js
self.addEventListener('sync', (e) => {
  if (e.tag === 'sync-pending') {
    e.waitUntil(syncPendingData());
  }
});

async function syncPendingData() {
  const db = await openDatabase();
  const pending = await db.getAll('pending');
  for (const item of pending) {
    try {
      await fetch('/api/sync', { method: 'POST', body: JSON.stringify(item) });
      await db.delete('pending', item.id);
    } catch (err) {
      console.error('同步失败:', err);
    }
  }
}

六、实战组合方案:一个真实项目怎么选

数据 存哪里 为什么
主题色、语言 localStorage 几个字节,同步读取不影响性能
JWT access_token localStorage + 短期过期 轻量、快速,配合刷新机制
100 条收货地址 IndexedDB 需要按省市筛选,localStorage 查不动
笔记/待办列表 IndexedDB 离线编辑 + 同步,必须事务保障
表单草稿 localStorage 几条字段,丢失也不心疼
离线地图瓦片 IndexedDB(存 Blob)+ Cache API(存 URL 映射) 二进制数据 localStorage 存不了
HTML/CSS/JS/图片 Cache API SW 拦截,离线秒开
播放进度 localStorage 轻量状态,{url, time} 一行 JSON 搞定

七、最终建议

项目类型 推荐方案
周期短、团队经验有限、需求简单 localStorage + SW 缓存
PWA / 离线优先应用 / 中大型系统 IndexedDB + Cache API + SW
已有 localStorage 代码但数据激增 新模块用 IndexedDB,旧数据按需迁移,别全量重写
混合使用 localStorage 存开关和小配置,IndexedDB 管主体业务,内存 Map 提热数据性能

记住:localStorage 是便利贴,IndexedDB 是数据库,Service Worker 是快递员。便利贴贴几张够用,几百张订单得建库,快递员负责让你断网也能收货。

相关推荐
壹方秘境32 分钟前
ApiCatcher支持抓包HTTP传输大文件的实现原理分享
前端·后端·客户端
一份执念44 分钟前
uni-app项目 (vue+vite + uni-UI)中引入umd格式JS文件,微信小程序中导入报错处理方案
前端·uni-app·echarts
ClouGence1 小时前
2026 年自动化测试工具选型指南:8 款主流工具对比
前端·测试
lichenyang4532 小时前
为什么需要双线程通信、JavaScriptProxy 和 runJavaScript 分别干什么
前端
以和为贵2 小时前
前端也能搞懂 RAG:用 JS 手写一条最小检索增强链路
前端·人工智能·面试
风止何安啊2 小时前
网课倍速痛点解决:一套前端代码实现自由控速播放器
前端·javascript·node.js
牧艺2 小时前
用 Next.js + React Three Fiber 打造 3D 快递仓储可视化
前端·three.js
锋行天下3 小时前
如何用Vite实现Vue组件的按需打包和远程加载
前端·vue.js·前端框架
光影少年3 小时前
原生DOM操作在React 中的注意事项
前端·javascript·react.js
禅思院5 小时前
前端部署“三层漏斗”完全指南:从CI/CD到自动回滚的工程化实战【开题】
前端·架构·前端框架