像Django或Spring这样的服务器端框架主要通过会话(Session)管理在服务器上处理,并通过cookie(饼干)识别客户端。服务器管理状态是很方便的。

然而,在像SPA(单页面应用)这样的环境中,客户端(浏览器)承担了更多的角色。与服务器的通信主要通过API进行数据交换,而用户的状态或UI设置等常常由客户端直接管理。

在这种情况下,我们需要决定数据存储在"哪里",而浏览器提供了几种存储方式。明确各个存储的特性和用途是高效开发SPA的关键。

今天,我们将了解浏览器的主要存储方式:cookie(饼干)、本地存储(Local Storage)、会话存储(Session Storage)以及IndexedDB




Cookie是浏览器存储的元老。最初是为了在服务器与客户端之间保持状态(Stateful)而设计的。

  • 特点:

    • 容量小:大约4KB,容量限制非常小。

    • 服务器传输:这是最大的特点。cookie在所有HTTP请求中自动包含并发送到服务器。这在服务器需要了解客户端状态时非常有用,但也可能因不必要的数据传输而降低性能。

    • 过期时间:可以通过Expires或者Max-Age属性设置过期日期和时间。如果不设置则以会话cookie(浏览器关闭时删除)的方式运作。

    • 安全选项:可以设置HttpOnly(防止JavaScript访问)、Secure(仅在HTTPS中传输)等安全设置。

  • 主要用途:

    • 认证 (Authentication):保存服务器发布的登录认证令牌(JWT等), 使得服务器可以在用户页面移动时识别用户。(特别是建议与HttpOnly选项一起使用)

    • 用户追踪与广告:用于跟踪用户的访问记录或行为模式。(例如:谷歌分析)

    • “今日不再显示”弹窗


2. 本地存储 (Local Storage)

这是HTML5中引入的Web Storage API的一部分。它是为了弥补cookie的缺点(容量小、自动服务器传输)而出现的。

  • 特点:

    • 容量大:每个浏览器各不相同,但通常提供5MB ~ 10MB的丰厚容量。

    • 不传输到服务器:本地存储只在客户端保存数据, 不会自动发送到服务器。适合存储服务器不需要知道的数据。

    • 持久性:数据永久保存。除非用户手动删除浏览器缓存或通过JavaScript代码调用localStorage.removeItem(),否则数据不会消失。

    • 简易API:提供localStorage.setItem('key', 'value'), localStorage.getItem('key')等易于使用的API。(仅支持存储字符串)

  • 主要用途:

    • 保存用户设置:用于保存用户选择的UI设置,如深色模式/浅色模式设置、语言设置等。

    • 保存非敏感数据:例如用户最后查看的内容列表、自动保存功能等。

    • 数据缓存:在API响应中缓存不常变化的数据,以减少不必要的请求。


3. 会话存储 (Session Storage)



与本地存储几乎共享所有特点,但在数据的生命周期(Life Cycle)上有决定性的差异。

  • 特点:

    • 容量大:与本地存储相似,提供约5MB的容量。

    • 不传输到服务器:仅存储在客户端。

    • 基于会话的生命周期:数据仅限于当前浏览器标签(或窗口)。关闭该标签后,会话存储的所有数据会立即删除

    • 数据隔离:即使是同个域名,不同标签之间的数据也不会共享。

  • 主要用途:

    • 一次性数据保存:用于存储当前标签中唯一有效的信息。

    • 临时表单数据:在分步注册表单或问卷调查中临时保存用户输入的内容,直到用户关闭标签。

    • 刷新时数据保持:确保页面刷新时数据保持有效,但在关闭标签时应消失。


4. IndexedDB

如果说本地/会话存储是简单的键值存储, 那么IndexedDB是在浏览器中存在的真正的事务数据库系统

  • 特点:

    • 大容量存储:可以存储数百MB到数GB,只要用户的磁盘空间允许(根据浏览器政策而定),可以存储非常大的数据。

    • 多种数据类型:可以存储不仅限于字符串的JavaScript对象、文件(Blob)、数组等复杂结构的数据

    • 事务支持:保证数据的完整性和一致性。可以将多个操作打包成一个单元进行处理。

    • 异步API:IndexedDB的所有操作是异步处理的。这种方式在读写大量数据时不会阻塞主UI线程,从而保持应用的响应性。

    • 索引支持:可以创建索引,以便根据特定字段非常快速地检索数据。

  • 缺点:

    • 复杂的API:相比于localStorage,API复杂得多,使用困难。(因此通常会与Dexie.js, idb等封装库一起使用。)
  • 主要用途:

    • 离线Web应用 (PWA):在没有网络连接的情况下也能运行的应用中存储核心数据。(例如:离线文档编辑器)

    • 大规模应用状态缓存:用于浏览器中存储以前的聊天记录或管理复杂的用户数据,例如Gemini或ChatGPT。

    • 用户生成内容临时存储:在用户上传长文本、图像编辑数据到服务器之前安全地保存。


5. 一目了然比较

A conceptual diagram illustrating different types of web browser storage: Cookie, Local Storage, Session Storage, and IndexedDB, each with symbolic icons representing their characteristics within a stylized browser window.

四种存储的特性可以总结如下表:

标准 Cookie(饼干) 本地存储 会话存储 IndexedDB
容量 ~ 4KB ~ 5-10MB ~ 5MB ~ GB+(大容量)
过期时间 手动设置 永久性 关闭标签时 永久性
服务器传送 (自动传送) (仅限客户端) (仅限客户端) (仅限客户端)
访问范围 同一域名 同一来源 当前标签 同一来源
数据形式 字符串 字符串 字符串 对象、文件、Blob等
API 同步(复杂) 同步(简易) 同步(简易) 异步(复杂)
检索功能 是(使用索引)

6. 结论:何时以及如何使用?

在开发像React这样的SPA时,明确区分这四种存储的用途是有益的。

  • Cookie(饼干):

    • 用于服务器必须知道的数据。(例如:安全性重要的认证令牌)

    • 建议使用HttpOnly选项来保护令牌免受XSS攻击。

  • 本地存储 (Local Storage):

    • 用于需要在用户关闭浏览器后仍保持的简单数据

    • (例如:用户UI主题设置、语言选择)

  • 会话存储 (Session Storage):

    • 用于仅在当前操作(标签)中需要的临时数据

    • (例如:多个步骤的表单填写时的临时保存)

  • IndexedDB:

    • 在需要结构化的大型数据在线离线支持的情况下使用。

    • (例如:复杂的应用状态、聊天记录缓存、离线文档数据)

重要的安全提示:本地存储、会话存储、IndexedDB都可以通过JavaScript轻松访问。这意味着它们容易受到XSS(跨站脚本)攻击。绝对不应该在此存储密码、个人识别信息、敏感的认证令牌等。(认证令牌使用HttpOnly cookie是最安全的。)