5MB vs 4KB vs 无限大:浏览器存储谁更强?

你有没有想过这个问题:为什么在网页上勾选了"记住我",下次打开还是登录状态?你改了个主题设置,关掉浏览器再打开,主题还在?浏览器是怎么记住这些数据的?

今天,用**"收纳房间"**的故事,来讲讲浏览器存储。


原文地址

墨渊书肆/5MB vs 4KB vs 无限大:浏览器存储谁更强?


浏览器是怎么"装东西"的?

想象一下你家要装修,需要各种收纳工具:

  • 贴身口袋:装点小东西,随时能用
  • 床头柜:装常用物品,随取随用
  • 衣柜:装换季衣服,大容量
  • 仓库:存大件物品,最大但找起来麻烦

浏览器存储也是这个道理。不同的数据,要用不同的"收纳工具"。


像个口袋,随身带

Cookie 是最"古老"的浏览器存储方案。它最大的特点是------会自动跟着请求一起发出去

就像你出门带了个口袋,里面装着身份证、银行卡。进任何一家店,都要掏出身份证证明身份。

浏览器也是:每次请求网页,Cookie 都自动带上,服务器就知道"哦,这是张三的浏览器"。

属性 像什么
容量 ~4KB 口袋里只能装这么多
发送 自动随请求发送 出门就带
生命周期 可设置过期时间 可以设有效期
访问 JS和服务器都能读 谁都能用
  • 登录状态:"记住我"功能
  • 购物车:逛淘宝加购物车
  • 追踪分析:埋点上报
javascript 复制代码
// 设置Cookie
document.cookie = "username=张三; expires=Fri, 31 Dec 2026 23:59:59 GMT; path=/";

// 读取Cookie
console.log(document.cookie);  // "username=张三; theme=dark"

Cookie 虽然方便,但有几个安全属性要注意:

属性 作用 什么意思
HttpOnly JS无法访问 口袋上锁了,店员碰不到
Secure 只在HTTPS发送 只能用加密通道
SameSite 防止CSRF攻击 别人拿不到你的卡

Cookie 是怎么工作的?

Cookie 由 HTTP 协议定义,通过 Set-Cookie 响应头设置:

http 复制代码
HTTP/1.1 200 OK
Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Secure; SameSite=Strict

HTTP/1.1 200 OK
Cookie: sessionId=abc123

浏览器怎么存 Cookie?

每个浏览器都有自己的存储方式:

浏览器 存储位置
Chrome/Edge SQLite 数据库 (%APPDATA%\Local\Google\Chrome\User Data\Default\Cookies)
Firefox JSON 文件 (cookies.sqlite)
Safari 二进制文件

Cookie 的发送规则?

浏览器根据 Domain + Path + SameSite 三个规则决定是否发送:

javascript 复制代码
// 例如:Cookie 设置为 Domain=example.com, Path=/admin
// 会发送给:
// ✅ example.com/admin
// ✅ example.com/admin/users
// ❌ example.com/ (path不匹配)
// ❌ other.com/admin (domain不匹配)

Session Cookie vs 持久 Cookie?

http 复制代码
# 会话Cookie(没有Expires/Max-Age)
Set-Cookie: sessionId=abc123
# 关掉浏览器就失效

# 持久Cookie
Set-Cookie: sessionId=abc123; Expires=Wed, 01 Jan 2027 00:00:00 GMT
# 有效期内都有效

LocalStorage --- 床头柜

容量大,但不主动发

LocalStorage 是 HTML5 引入的存储方案。最大的特点:不会随请求发出去

就像床头柜------你把东西放里面,下次进门直接拿,不用每次出门都背着。

LocalStorage 的特点

属性 像什么
容量 ~5MB/域 床头柜大小
发送 不随请求发送 不随身带
生命周期 永久存储 除非搬家(手动删除)
API 同步操作 马上拿到

LocalStorage 的使用场景

  • 主题设置:深色/浅色模式
  • 用户偏好:字体大小、语言设置
  • 数据缓存:接口数据本地缓存

LocalStorage 的代码

javascript 复制代码
// 设置
localStorage.setItem('username', '张三');
localStorage.setItem('theme', 'dark');

// 读取
const theme = localStorage.getItem('theme');  // 'dark'

// 删除
localStorage.removeItem('theme');

// 清空
localStorage.clear();

// 遍历
for (let i = 0; i < localStorage.length; i++) {
  const key = localStorage.key(i);
  console.log(`${key}: ${localStorage.getItem(key)}`);
}

LocalStorage 的缺点

  • 同步操作:大量数据会卡界面
  • 只能存字符串:对象要转成 JSON
  • 容量有限:5MB 对大数据不够

深入了解 LocalStorage 🔬

同源策略限制

LocalStorage 遵循同源策略:

bash 复制代码
✅ http://example.com 和 https://example.com 共享同一个Storage
✅ http://example.com:8080 和 http://example.com:3000 不共享(端口不同)
✅ http://www.example.com 和 http://example.com 不共享(子域名不同)

存储配额

实际容量取决于浏览器和磁盘空间,Chrome 默认是 5MB(可申请更多):

javascript 复制代码
// 查询当前配额和使用量
navigator.storage.estimate().then(({ usage, quota }) => {
  console.log(`已使用: ${(usage / 1024 / 1024).toFixed(2)} MB`);
  console.log(`总配额: ${(quota / 1024 / 1024).toFixed(2)} MB`);
});

// 请求更大的存储空间(需要用户授权)
navigator.storage.persist().then((granted) => {
  console.log('永久存储权限:', granted);
});

为什么 LocalStorage 是同步的?

因为 LocalStorage 读取是直接读磁盘。如果数据量大,同步读取会阻塞主线程:

javascript 复制代码
// ❌ 错误:大数据量时卡界面
localStorage.setItem('bigData', JSON.stringify(largeArray));

// ✅ 更好:拆分存储或用 IndexedDB

SessionStorage --- 抽屉

只在当前标签页有效

SessionStorageLocalStorage 几乎一样,唯一的区别是------关闭标签页就没了

就像抽屉里的东西,只有在这个房间能用。换到另一个房间(另一个标签页),抽屉里的东西就不在了。

SessionStorage 的特点

属性 和LocalStorage的区别
容量 ~5MB/域 一样
作用域 仅当前标签页 ❌ 跨标签页不共享
生命周期 关闭标签页失效 ❌ 不能持久保存

SessionStorage 的使用场景

  • 表单草稿:填写到一半的表单
  • 临时状态:当前页面的操作状态

SessionStorage 的代码

javascript 复制代码
// 用法和LocalStorage完全一样
sessionStorage.setItem('draft', JSON.stringify({ title: '我的文章', content: '...' }));

关键区别

javascript 复制代码
// 标签页A中设置
sessionStorage.setItem('key', 'value');
localStorage.setItem('key', 'value');

// 在标签页B中读取
sessionStorage.getItem('key');  // null ❌
localStorage.getItem('key');    // 'value' ✅

深入了解 SessionStorage 🔬

iframe 共享问题

注意:同一个标签页中的 iframe 会共享 SessionStorage(因为是同一个浏览器标签页):

javascript 复制代码
// 父页面
sessionStorage.setItem('shared', 'value');

// iframe 内可以读取到
console.log(sessionStorage.getItem('shared'));  // 'value'

sessionStorage 在隐私模式下

  • Chrome 无痕模式sessionStorage 仍然存在,但标签页关闭后失效
  • Firefox 隐私窗口 :完全隔离,每个新窗口都是新的 sessionStorage

和 LocalStorage 的性能对比

两者都是同步 API,性能特性相同。但 SessionStorage 因为数据不持久,有时候比 LocalStorage 更适合存临时数据。


IndexedDB --- 仓库

浏览器里的数据库

IndexedDB 是浏览器内置的数据库。容量巨大,能存文件、音频、视频这些大东西。

就像仓库------你家装修工具、电风扇、行李箱都放这儿。东西多,但找起来要翻半天。

IndexedDB 的特点

属性 像什么
容量 很大(取决于磁盘) 仓库,接近无限
数据类型 什么都能存 不挑东西
API 异步操作 异步,不卡界面
查询 支持索引 能分类查找

IndexedDB 的使用场景

  • 离线数据:PWA离线应用
  • 多媒体存储:图片、音频、视频缓存
  • 复杂数据:需要索引查询的数据

IndexedDB 的代码

javascript 复制代码
// 打开数据库
const request = indexedDB.open('myDatabase', 1);

// 创建表(对象存储)
request.onupgradeneeded = (event) => {
  const db = event.target.result;
  const store = db.createObjectStore('users', { keyPath: 'id' });
  store.createIndex('name', 'name', { unique: false });
  store.createIndex('email', 'email', { unique: true });
};

// 添加数据
request.onsuccess = (event) => {
  const db = event.target.result;
  const tx = db.transaction(['users'], 'readwrite');
  const store = tx.objectStore('users');

  store.add({ id: 1, name: '张三', email: 'zhangsan@example.com' });
  store.add({ id: 2, name: '李四', email: 'lisi@example.com' });
};

// 查询数据
const getRequest = store.get(1);
getRequest.onsuccess = () => {
  console.log('查询结果:', getRequest.result);
};

// 使用索引查询
const index = store.index('name');
const indexRequest = index.get('张三');
indexRequest.onsuccess = () => {
  console.log('索引查询结果:', indexRequest.result);
};

IndexedDB 的缺点

  • API 复杂:需要写一堆回调
  • 学习成本高:概念多(数据库、表、事务、索引)

深入了解 IndexedDB 🔬

数据库版本和升级

javascript 复制代码
const request = indexedDB.open('myDatabase', 2);  // 版本号从1升到2

request.onupgradeneeded = (event) => {
  const db = event.target.result;

  // 创建新存储
  if (!db.objectStoreNames.contains('products')) {
    db.createObjectStore('products', { keyPath: 'id' });
  }

  // 删除旧存储
  if (db.objectStoreNames.contains('oldData')) {
    db.deleteObjectStore('oldData');
  }
};

事务的原子性

javascript 复制代码
const tx = db.transaction(['users', 'orders'], 'readwrite');

// 两个操作在一个事务里,要么全成功,要么全失败
tx.objectStore('users').add({ id: 1, name: '张三' });
tx.objectStore('orders').add({ id: 1, userId: 1, product: '电脑' });

tx.oncomplete = () => console.log('事务成功');
tx.onerror = () => console.log('事务失败,全部回滚');

游标遍历大量数据

javascript 复制代码
const tx = db.transaction(['users'], 'readonly');
const store = tx.objectStore('users');
const cursor = store.openCursor();

cursor.onsuccess = (event) => {
  const cur = event.target.result;
  if (cur) {
    console.log('用户:', cur.value.name);
    cur.continue();  // 继续下一个
  }
};

Promise 封装(更简洁的写法)

javascript 复制代码
function openDB(name, version) {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open(name, version);
    request.onupgradeneeded = (e) => resolve(e.target.result);
    request.onsuccess = (e) => resolve(e.target.result);
    request.onerror = (e) => reject(e.target.error);
  });
}

// 使用
const db = await openDB('myDatabase', 1);
const tx = db.transaction('users', 'readwrite');
await tx.objectStore('users').add({ id: 1, name: '张三' });

Cache API --- 集装箱

Service Worker 的专属工具

Cache API 是 Service Worker 的一部分,专门用来缓存网络请求。

就像集装箱------你坐飞机带不了大件行李,但可以用集装箱海运。东西多、个头大,但只能走特定渠道。

Cache API 的特点

属性 像什么
容量 很大 集装箱,装得多
存储内容 Request/Response 对 整套打包
生命周期 手动管理 不用就扔
API 异步操作 不卡界面

Cache API 的使用场景

  • 离线应用:把整个网站缓存下来
  • 性能优化:缓存静态资源
  • Service Worker:配合SW实现缓存策略

Cache API 的代码

javascript 复制代码
// 在Service Worker中使用
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((cachedResponse) => {
        return cachedResponse || fetch(event.request);
      })
  );
});

// 打开缓存
caches.open('my-cache').then((cache) => {
  cache.addAll([
    '/css/style.css',
    '/js/app.js',
    '/images/logo.png'
  ]);
});

// 缓存特定请求
cache.put(request, response);

// 删除缓存
caches.delete('my-cache');

深入了解 Cache API 🔬

缓存策略

javascript 复制代码
// Cache First(缓存优先)
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((response) => response || fetch(event.request))
  );
});

// Network First(网络优先)
self.addEventListener('fetch', (event) => {
  event.respondWith(
    fetch(event.request)
      .catch(() => caches.match(event.request))
  );
});

// Stale-While-Revalidate(先返回缓存,同时更新缓存)
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.open('my-cache').then((cache) => {
      return cache.match(event.request).then((response) => {
        const fetchPromise = fetch(event.request).then((networkResponse) => {
          cache.put(event.request, networkResponse.clone());
          return networkResponse;
        });
        return response || fetchPromise;
      });
    })
  );
});

Cache API 和 cookies

Cache API 存储的是完整的 Request/Response 对,不只是 body:

javascript 复制代码
// 缓存时包含了headers、status等所有信息
cache.match(request).then((response) => {
  console.log(response.status);      // 200
  console.log(response.headers.get('content-type'));  // 'text/html'
});

缓存清理策略

javascript 复制代码
// 删除指定缓存
caches.delete('old-cache');

// 清理所有版本,只保留最新的
caches.keys().then((cacheNames) => {
  Promise.all(
    cacheNames
      .filter((name) => name.startsWith('app-') && name !== 'app-v2')
      .map((name) => caches.delete(name))
  );
});

Storage Event --- 跨标签页喊话

标签页之间能"喊话"

当 LocalStorage 发生变化时,其他同源的标签页会收到通知。

就像你在客厅喊了一句"饭好了",厨房的人、卧室的人都能听到。

Storage Event 的代码

javascript 复制代码
// 标签页A中监听
window.addEventListener('storage', (event) => {
  console.log('key:', event.key);      // 变化的键
  console.log('oldValue:', event.oldValue);  // 旧值
  console.log('newValue:', event.newValue);  // 新值
  console.log('url:', event.url);      // 触发变化的页面URL
  console.log('storageArea:', event.storageArea);  // localStorage 或 sessionStorage
});

// 标签页B中修改
localStorage.setItem('theme', 'dark');  // 标签页A会收到通知

使用场景

  • 多标签页同步:一个标签页登录,其他标签页同步登录状态
  • 状态广播:跨标签页的状态通知

深入了解 Storage Event 🔬

Storage Event 的触发条件

javascript 复制代码
// ✅ 会触发 storage 事件
localStorage.setItem('key', 'value');
localStorage.removeItem('key');
localStorage.clear();

// ❌ 不会触发 storage 事件(同一个标签页)
// Storage Event 只在「其他标签页」变化时触发

SessionStorage 也会触发?

注意:SessionStorage 本身不跨标签页共享,但 Storage Event 只监听 localStorage 的变化。

javascript 复制代码
// SessionStorage 变化不会触发 storage 事件
sessionStorage.setItem('key', 'value');  // 不会触发其他标签页

// localStorage 变化会触发
localStorage.setItem('key', 'value');  // 其他标签页会收到通知

隐私模式下不触发

在无痕/隐私模式下,Storage Event 不会触发,这是浏览器的隐私保护机制。


横向对比

特性 Cookie LocalStorage SessionStorage IndexedDB Cache API
容量 ~4KB ~5MB ~5MB 很大 很大
生命周期 可设置 永久 关闭失效 永久 手动
发送 自动发 不发 不发 不发 不发
API 简单 同步简单 同步简单 异步复杂 异步
数据类型 字符串 字符串 字符串 所有可序列化 Request/Response
跨标签页 共享 共享 不共享 共享 不共享

怎么选?

场景 推荐
需要服务器读取 Cookie
存用户偏好、主题 LocalStorage
临时状态、标签页隔离 SessionStorage
大数据、离线存储 IndexedDB
Service Worker缓存 Cache API

注意事项

1. 不要存敏感信息

LocalStorage 可以被 JS 访问,XSS 攻击能偷走数据。敏感信息用 HttpOnly Cookie。

2. 存储配额

浏览器对存储有限制,可以用 API 查询:

javascript 复制代码
navigator.storage.estimate().then(({ usage, quota }) => {
  console.log('已用:', (usage / 1024 / 1024).toFixed(2), 'MB');
  console.log('总配额:', (quota / 1024 / 1024).toFixed(2), 'MB');
});

3. 序列化问题

LocalStorage 和 SessionStorage 只能存字符串,对象要转 JSON:

javascript 复制代码
// 存
localStorage.setItem('data', JSON.stringify({ name: '张三' }));

// 取
const data = JSON.parse(localStorage.getItem('data'));

4. 同步 API 的性能问题

LocalStorage/SessionStorage 是同步操作,大量数据会阻塞主线程:

javascript 复制代码
// ❌ 不好:大量数据卡界面
for (let i = 0; i < 10000; i++) {
  localStorage.setItem(`key${i}`, `value${i}`);
}

// ✅ 更好:用 IndexedDB 存储大量数据

总结

存储方式 像什么 特点
Cookie 口袋 小、随请求发、安全属性多
LocalStorage 床头柜 5MB、不发送、永久
SessionStorage 抽屉 5MB、不发送、仅标签页
IndexedDB 仓库 巨大、异步、复杂
Cache API 集装箱 Service Worker专用

选对"收纳工具",数据管理更轻松。


写在最后

现在你应该明白了:

  • Cookie = 口袋,随身带、自动发送、容量小
  • LocalStorage = 床头柜,大容量、不发送、永久保存
  • SessionStorage = 抽屉,只在当前标签页有效
  • IndexedDB = 仓库,最大但操作复杂
  • Cache API = 集装箱,Service Worker专用

下次你在网页上勾选"记住我",或者调整了主题设置------你就知道浏览器是用哪种"收纳工具"帮你存的了。

相关推荐
牛奶2 小时前
setTimeout设为0就马上执行?JS异步背后的秘密
前端·性能优化·promise
LaughingZhu3 小时前
Product Hunt 每日热榜 | 2026-04-05
前端·数据库·人工智能·经验分享·神经网络
SuperEugene4 小时前
Vue3 组件复用设计:Props / 插槽 / 组合式函数,三种复用方式选型|组件化设计基础篇
前端·javascript·vue.js
nFBD29OFC4 小时前
利用Vue元素指令自动合并tailwind类名
前端·javascript·vue.js
ISkp3V8b45 小时前
ASP.NET MVC]Contact Manager开发之旅之迭代2 - 修改样式,美化应用
前端·chrome
Highcharts.js5 小时前
高级可视化图表的暗色模式与主题|Highcharts 自适应主题配色全解
前端·react.js·实时图表
zk_one6 小时前
【无标题】
开发语言·前端·javascript
precious。。。8 小时前
1.2.1 三角不等式演示
前端·javascript·html
小陈工8 小时前
Python Web开发入门(十一):RESTful API设计原则与最佳实践——让你的API既优雅又好用
开发语言·前端·人工智能·后端·python·安全·restful