如何在浏览器中存储数据

发布于 2026-05-02 17:38 2795 字 14 min read

在前端开发中,“让浏览器记住一些东西”几乎是所有稍微复杂一点的应用都会遇到的需求:保存用户登录状态、记录偏好设置、缓存接口数据、支持离线应用等。浏览器本地存储做得好,可以显著减少网络请求、提升页面性能和用户体验;做不好,则会很容易出现数据混乱、XSS 泄露隐私等问题。之前我只是对着其中的一两个用的比较多,熟悉一些,其他的只是知道,所以我现在就想做一个全面一些的对比总结。...

在前端开发中,“让浏览器记住一些东西”几乎是所有稍微复杂一点的应用都会遇到的需求:保存用户登录状态、记录偏好设置、缓存接口数据、支持离线应用等。浏览器本地存储做得好,可以显著减少网络请求、提升页面性能和用户体验;做不好,则会很容易出现数据混乱、XSS 泄露隐私等问题。之前我只是对着其中的一两个用的比较多,熟悉一些,其他的只是知道,所以我现在就想做一个全面一些的对比总结。

一、为什么需要浏览器本地存储?

传统 Web 模式下,所有状态都由服务端维护,浏览器只是一个“渲染终端”。但随着前端单页应用、PWA 和离线应用的兴起,越来越多状态被下放到客户端管理,典型需求包括:

  • 记住登录状态(Token、用户信息、权限等)
  • 记录用户偏好(主题色、语言、布局等)
  • 缓存接口数据,减少重复请求
  • 在刷新或关闭页面后保留表单内容
  • 支持弱网或离线场景下的使用

基于以上这些需求,我们就需要对传统的 Web 模式进行改进优化,即诞生了可以在客户端持久化数据,以减少对服务端的依赖,提高用户体验。

二、浏览器常见存储方式总览

目前主流浏览器中,前端常用的存储方案主要有:

  • Cookie
  • localStorage
  • sessionStorage
  • IndexedDB
  • (以及更偏缓存方向的 Cache API,文末会顺带提一下)

可以从几个维度来对比这些方案:容量、生命周期、是否参与网络请求、安全性和易用性。

一个常见的理解是:

  • 少量、与服务端强相关、需要随请求发送 → Cookie
  • 少量、长期存在、只在前端用 → localStorage
  • 少量、只在当前标签页会话内有效 → sessionStorage
  • 大量、结构化、需要查询/索引/事务 → IndexedDB

三、前置核心:浏览器存储的基石 —— 同源策略

  • 在介绍这四种方法前,我们需要对一个前置知识点进行介绍。

    • 所有浏览器存储方案都严格受同源策略约束,这是浏览器安全的核心规则。同源指的是「协议 + 域名 + 端口」三者完全一致,不同源的站点之间,存储数据完全隔离,无法互相访问。

    • 唯一的例外是 Cookie,可通过 Domain 属性配置实现父子域名间的数据共享,其余存储方案均不支持跨子域名访问,这是选型时必须先明确的前提。

四、Cookie:最传统也最“重”的方案

Cookie 是最早的浏览器存储方式之一,最初就是为了解决“HTTP 是无状态的”这一问题,用来在客户端和服务端之间共享一小段状态信息,维持客户端与服务端的会话状态,是唯一能自动在前后端同步的存储方案。

  • 核心原理

    • Cookie 是服务端通过 Set-Cookie 响应头发送到浏览器、并保存在本地的小块数据,浏览器下次向同一服务端发起符合规则的请求时,会自动将 Cookie 携带在Cookie请求头中,回传给服务端。
  • 典型特征

    • 容量很小:单个域名下总容量大约几 KB 级别

    • 会自动随着同域 HTTP 请求发送给服务器

    • 可以设置过期时间(Session Cookie / Persistent Cookie)

    • 可以通过属性控制安全性(如 HttpOnlySecureSameSite

我们可以通过两种方式操作 Cookie:

  • 通过 document.cookie 在浏览器端直接读写
  • 通过服务端响应头 Set-Cookie 下发

典型示例:

// 写入 Cookie
document.cookie = 'theme=dark; max-age=3600; path=/';

// 读取 Cookie(简单解析)
function getCookie(name) {
  const pattern = new RegExp('(?:^|; )' + encodeURIComponent(name) + '=([^;]*)');
  const match = document.cookie.match(pattern);
  return match ? decodeURIComponent(match[1]) : null;
}

const theme = getCookie('theme');

推荐在项目中封装一层 Cookie 工具函数,避免到处拼字符串

  • 适用场景:

    • 会话标识(Session ID)

    • 短小的鉴权 Token(更推荐 HttpOnly + Secure)

    • 简单的跨页面偏好设置(如果需要服务端也感知)

  • 注意事项:

    • 不要在 Cookie 中存放敏感的明文信息(如密码、完整个人信息)

    • Token 建议使用 HttpOnly,避免被 XSS 通过 JS 读取

    • 过多、过大的 Cookie 会拖累所有同域请求的性能


五、localStorage:最常用的“持久储物柜”

1. 特点与限制

localStorage 是 HTML5 引入的 Web Storage API 中的一部分,它提供了一个简单的键值对存储空间,有如下特征:

  • 与域名绑定,通常容量在 5MB 左右
  • 数据持久存在,除非显式清除或用户清理浏览器数据
  • 不随 HTTP 请求发送,仅在前端可见
  • API 简单、同步执行(阻塞主线程)
  • 仅支持字符串类型,需要手动处理序列化和反序列化

2. 基本用法示例

// 写入
localStorage.setItem('token', 'xxx-yyy-zzz');

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

// 删除某个 key
localStorage.removeItem('token');

// 清空所有
localStorage.clear();

如果要存对象,需要配合 JSON.stringifyJSON.parse

const user = { id: 1, name: 'Alice', role: 'admin' };

localStorage.setItem('user', JSON.stringify(user));

const raw = localStorage.getItem('user');
const userObj = raw ? JSON.parse(raw) : null;

3. 使用场景与最佳实践

  • 常见场景:

    • 存放 UI 层配置(主题色、布局、语言)

    • 缓存一些非敏感的接口数据(如字典项、热门列表)

    • 记录用户上次访问的状态(如当前 Tab、分页页码)

  • 实践建议:

    • 避免把敏感信息(Access Token、用户隐私)长期明文放在 localStorage 中

    • 封装统一的 Storage 工具,集中管理 key 名、版本号、过期策略

    • 考虑数据膨胀对性能的影响,大量字符串读写会阻塞主线程

六、sessionStorage:会话级的临时存储

1. 特点

sessionStoragelocalStorage API 几乎完全相同,但它的生命周期和作用范围不同:

  1. 只在当前标签页有效,标签页关闭即销毁
  2. 同一浏览器同一域名下的不同标签页互不共享
  3. 容量通常与 localStorage 相近(约 5MB)
  4. 同样是同步 API,字符串键值对存储

2. 典型用法示例

// 保存表单草稿
function saveDraft(formId) {
  const form = document.querySelector(`#${formId}`);
  const data = Object.fromEntries(new FormData(form).entries());
  sessionStorage.setItem('form-draft', JSON.stringify(data));
}

function restoreDraft(formId) {
  const form = document.querySelector(`#${formId}`);
  const draftStr = sessionStorage.getItem('form-draft');
  if (!draftStr) return;
  const draft = JSON.parse(draftStr);
  Object.keys(draft).forEach((key) => {
    const input = form.elements.namedItem(key);
    if (input) input.value = draft[key];
  });
}

3. 使用场景

  1. 临时表单数据防丢(刷新页面不丢,关闭标签页就清)
  2. 页内导航时的数据中转(不想暴露在 URL 上)
  3. 某些只在当前会话内生效的状态(如一次性的向导步骤)

七、IndexedDB:浏览器内的“小型数据库”

如果某一应用需要存储大量数据(比如离线缓存大量列表、图片缩略图、聊天记录等),或者需要复杂的查询能力,仅靠 localStorage 就不够用了,我们需要另一个解决方法,即 IndexeDB。

1. IndexedDB 的特点

  • IndexedDB 是浏览器内置的 NoSQL 数据库,有以下能力:

    • 支持存储大量数据,只受磁盘空间限制

    • 支持二进制(Blob、ArrayBuffer)等任意 JS 对象

    • 支持索引、范围查询、游标、事务等数据库特性

    • 异步 API,不阻塞主线程

    • 和域名绑定,安全隔离

对于传统的 客户端-服务器 应用,以上的这些功能通常是没有必要的。IndexedDB 适用于离线应用,可与 ServiceWorkers 和其他技术相结合使用。

2. 基本使用流程概览

本文不在于详细解释教会每一个方法的使用,所以我也不过多的去介绍用法,只是简单说一下步骤。

详细介绍与使用方法推荐在下面的文章学习,写的都很好:

https://zh.javascript.info/indexeddb https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API

  • IndexedDB 的 API 相对繁琐,一般包含几个步骤:

    • 打开数据库(indexedDB.open),指定名称和版本

    • onupgradeneeded 中创建对象存储(类似表)和索引

    • onsuccess 中拿到数据库实例

    • 发起读写事务(db.transaction),执行增删改查

3. 适用场景

  • 大型数据集的离线缓存(如文章列表、聊天记录)
  • 大文件或二进制数据的本地存储(图片、音频切片)
  • 需要复杂查询能力的客户端数据层(多字段筛选、排序)

八、如何选择合适的存储方式?

我们可以从这几个问题倒推,然后结合选择方法:

  1. 数据大小有多大?
    • 几 KB:Cookie、Web Storage 都可以
    • 几百 KB~MB:localStorage、sessionStorage
    • 更大、甚至几十 MB:IndexedDB
  2. 需要被服务端感知吗?
    • 需要:Cookie(随请求发送)
    • 不需要:localStorage / sessionStorage / IndexedDB
  3. 数据的生命周期是怎样的?
    • 只在当前会话/标签页内:sessionStorage
    • 长期保留,用户下次访问还需使用:localStorage / IndexedDB
    • 按过期时间控制,并服务端参与:Cookie
  4. 是否包含敏感信息?
    • 敏感信息优先考虑存服务器 + 短期 Token,配合 HttpOnly Cookie
    • 尽量避免在 localStorage 中长期存放敏感明文数据
  • 一个常见的组合实践:

    • 认证凭证:短期 Token 放在 HttpOnly Cookie 中(前端无权读),前端只负责根据“是否登录”调整 UI

    • UI 偏好/非敏感配置:localStorage

    • 临时表单数据:sessionStorage

    • 大批量列表缓存、离线功能:IndexedDB

九、安全与性能注意事项

1. XSS 与数据泄露

  • 无论是 Cookie、localStorage 还是 IndexedDB,只要能被 JS 读写,就有可能在发生 XSS 时被攻击者窃取。建议:

    • 对用户可输入的内容严格做 XSS 防御(转义、CSP 等)

    • 不在可被 JS 读到的存储中保存高价值敏感数据

    • 对关键数据做加密/签名(虽然前端加密本身也需要谨慎看待)

2. 存储配额与异常处理

  • 不同浏览器的实际存储配额、策略有所不同:

    • 超出配额时,写入会抛出异常

    • 用户可能随时清理站点数据

    • 隐身模式下的行为可能不同

  • 因此实际使用时就需要考虑到这些,采取一些应对措施,比如:

    • 封装一层安全读写:捕获异常、做降级处理

    • 对关键数据设计“缺失时如何恢复”的逻辑(例如重新拉取接口)

3. 同步 API 的阻塞问题

  • localStorage / sessionStorage 的读写是同步的,在大量或高频操作时可能造成主线程卡顿。建议:

    • 不在高频事件(如 scrollmousemove)里直接操作 Storage

    • 尽量合并多次写入,做节流/防抖

    • 对于大规模读写,考虑使用 IndexedDB 这种异步方案

喜欢的话,留下你的评论吧~