像Django或Spring这样的服务器端框架主要通过会话(Session)管理在服务器上处理,并通过cookie(饼干)识别客户端。服务器管理状态是很方便的。
然而,在像SPA(单页面应用)这样的环境中,客户端(浏览器)承担了更多的角色。与服务器的通信主要通过API进行数据交换,而用户的状态或UI设置等常常由客户端直接管理。
在这种情况下,我们需要决定数据存储在"哪里",而浏览器提供了几种存储方式。明确各个存储的特性和用途是高效开发SPA的关键。
今天,我们将了解浏览器的主要存储方式:cookie(饼干)、本地存储(Local Storage)、会话存储(Session Storage)以及IndexedDB。
1. Cookie(饼干)
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等封装库一起使用。)
- 复杂的API:相比于
-
主要用途:
-
离线Web应用 (PWA):在没有网络连接的情况下也能运行的应用中存储核心数据。(例如:离线文档编辑器)
-
大规模应用状态缓存:用于浏览器中存储以前的聊天记录或管理复杂的用户数据,例如Gemini或ChatGPT。
-
用户生成内容临时存储:在用户上传长文本、图像编辑数据到服务器之前安全地保存。
-
5. 一目了然比较

四种存储的特性可以总结如下表:
| 标准 | Cookie(饼干) | 本地存储 | 会话存储 | IndexedDB |
|---|---|---|---|---|
| 容量 | ~ 4KB | ~ 5-10MB | ~ 5MB | ~ GB+(大容量) |
| 过期时间 | 手动设置 | 永久性 | 关闭标签时 | 永久性 |
| 服务器传送 | 是(自动传送) | 否(仅限客户端) | 否(仅限客户端) | 否(仅限客户端) |
| 访问范围 | 同一域名 | 同一来源 | 当前标签 | 同一来源 |
| 数据形式 | 字符串 | 字符串 | 字符串 | 对象、文件、Blob等 |
| API | 同步(复杂) | 同步(简易) | 同步(简易) | 异步(复杂) |
| 检索功能 | 否 | 否 | 否 | 是(使用索引) |
6. 结论:何时以及如何使用?
在开发像React这样的SPA时,明确区分这四种存储的用途是有益的。
-
Cookie(饼干):
-
用于服务器必须知道的数据。(例如:安全性重要的认证令牌)
-
建议使用
HttpOnly选项来保护令牌免受XSS攻击。
-
-
本地存储 (Local Storage):
-
用于需要在用户关闭浏览器后仍保持的简单数据。
-
(例如:用户UI主题设置、语言选择)
-
-
会话存储 (Session Storage):
-
用于仅在当前操作(标签)中需要的临时数据。
-
(例如:多个步骤的表单填写时的临时保存)
-
-
IndexedDB:
-
在需要结构化的大型数据或在线离线支持的情况下使用。
-
(例如:复杂的应用状态、聊天记录缓存、离线文档数据)
-
重要的安全提示:本地存储、会话存储、IndexedDB都可以通过JavaScript轻松访问。这意味着它们容易受到XSS(跨站脚本)攻击。绝对不应该在此存储密码、个人识别信息、敏感的认证令牌等。(认证令牌使用
HttpOnlycookie是最安全的。)
目前没有评论。