🧠 一、本地存储(Local Storage):持久化的“备忘录”
Local Storage 是一种简单、持久的键值对(key-value)存储机制,数据会一直保留在用户的浏览器中,除非被用户或代码主动清除。
核心特征:
- 生命周期:永久性。数据不会因浏览器关闭或电脑重启而消失。
- 存储容量:较大,通常为 5MB 左右(因浏览器而异)。
- 数据格式:只能存储字符串。任何非字符串数据在存入前都必须通过
JSON.stringify()序列化,取出后通过JSON.parse()反序列化。 - 作用域:遵循同源策略(Same-Origin Policy)。只有同协议、同域名、同端口的页面才能访问。
- API:简单直观,同步执行。
适用场景:
- 用户偏好设置:如网站主题(暗色/亮色模式)、语言选择。
- 持久化用户界面状态:如记住用户上次折叠的面板、一个不那么重要的表单草稿。
- 缓存非敏感数据:缓存一些不常变动的 API 响应,减少不必要的网络请求。
优点:
- API 简单易用,上手快。
- 存储容量远大于 Cookie,能满足多数基本需求。
缺点:
- API 是同步的:读写操作会阻塞主线程,对于大量或频繁的操作可能影响页面性能。
- 无法直接存储复杂数据结构:需要手动进行 JSON 序列化和反序列化。
- 无法被 Web Workers 访问:限制了其在后台线程中的使用。
- 安全性较低:容易受到 XSS 攻击,恶意脚本可以轻易读写 Local Storage 中的所有数据。
代码示例(存储对象):
// 准备一个对象const userSettings = { theme: 'dark', notifications: { enabled: true, level: 'important' }};
// 1. 存储:使用 JSON.stringify() 将对象转换为字符串localStorage.setItem('settings', JSON.stringify(userSettings));
// 2. 读取:先获取字符串,再用 JSON.parse() 转换回对象const storedSettingsStr = localStorage.getItem('settings');const retrievedSettings = JSON.parse(storedSettingsStr);
console.log(retrievedSettings.theme); // "dark"
// 3. 移除localStorage.removeItem('settings');
// 4. 清空所有localStorage.clear();⚡ 二、会话存储(Session Storage):临时的“标签页便签”
Session Storage 的 API 与 Local Storage 完全相同,但其生命周期截然不同。它的数据仅在当前浏览器标签页的会话期间有效。
核心特征:
- 生命周期:会话级别。一旦标签页或浏览器被关闭,存储的数据就会被清除。
- 作用域:标签页隔离。即使是同源的页面,在不同的标签页中打开,它们的 Session Storage 也是相互独立的。
- 数据格式与容量:与 Local Storage 相同(字符串,约 5MB)。
适用场景:
- 单次会话的临时数据:如多步骤表单中前几步填写的信息,用户关闭页面后就无需保留。
- 防止页面刷新导致数据丢失:在单页面应用(SPA)中,可以将当前页面的状态(如滚动位置、选项卡)存入 Session Storage,刷新后可以恢复。
- 临时存储敏感信息:比如一次性登录的 token,关闭标签页后自动失效,比 Local Storage 更安全。
优点:
- 数据隔离性好,不会在不同标签页间造成数据污染。
- 生命周期由浏览器自动管理,用完即焚,无需手动清理。
缺点:
- 应用范围受限,无法实现跨标签页的数据共享。
代码示例:
// 在一个多步骤表单的第一页sessionStorage.setItem('formStep1', JSON.stringify(step1Data));
// 在第二页,可以读取第一页的数据const previousData = JSON.parse(sessionStorage.getItem('formStep1'));if (previousData) { console.log(`Welcome, ${previousData.name}!`);}
// 当用户关闭这个标签页时,'formStep1' 会被自动删除。🍪 三、Cookie:服务器与客户端的“通行证”
Cookie 是历史最悠久的浏览器存储机制。它的核心设计目标是在无状态的 HTTP 协议上维持状态,因此它最大的特点是会自动在客户端与服务器之间双向传递。
1. Cookie 的本质
Cookie 是一小段文本信息,由服务器通过
Set-CookieHTTP 响应头发送给浏览器。浏览器保存后,在后续向该服务器发起的每一个请求中,都会自动通过CookieHTTP 请求头将这些信息带回给服务器。
核心交互流程:
-
服务器设置:客户端首次请求后,服务器在响应头中加入
Set-Cookie。HTTP/1.1 200 OKContent-Type: text/htmlSet-Cookie: sessionId=a3fWa; HttpOnly; SameSite=Lax -
浏览器存储:浏览器收到响应,将
sessionId=a3fWa这条 Cookie 与该域名关联并保存。 -
浏览器自动发送:当用户再次访问该域名下的任何资源(页面、API、图片等),浏览器都会在请求头中自动添加
Cookie。GET /api/user/profile HTTP/1.1Host: example.comCookie: sessionId=a3fWa -
服务器读取与识别:服务器通过读取请求头中的
Cookie,识别出用户身份,从而提供个性化的响应。
2. Cookie 的关键属性(Attributes)
Cookie 不只是键值对,其行为由一系列属性精确控制。
| 属性 | 作用 | 示例 |
|---|---|---|
Expires / Max-Age | 控制生命周期。Expires 是一个绝对的过期时间点,Max-Age 是相对的过期秒数(更推荐)。若不设置,则为会话 Cookie,浏览器关闭时删除。 | Max-Age=3600 (1 小时后过期) |
Domain | 控制生效域名。可设置为父域名(如 .example.com),使其在所有子域名(a.example.com, b.example.com)中共享。 | Domain=.example.com |
Path | 控制生效路径。只有当请求的路径匹配时,才会发送 Cookie。通常设置为 /,表示整个域名下都有效。 | Path=/ |
HttpOnly | 核心安全属性。设置后,该 Cookie 无法通过 JavaScript (document.cookie) 访问,能有效防御 XSS 攻击窃取 Cookie。 | HttpOnly |
Secure | 核心安全属性。设置后,该 Cookie 只会在 HTTPS 连接中被发送,防止在不安全的 HTTP 连接中被中间人窃听。 | Secure |
SameSite | 核心安全属性。用于防御 CSRF 攻击,控制在跨站请求中是否发送 Cookie。 - Strict: 最严格,完全禁止跨站发送。- Lax: (现代浏览器默认值) 允许部分安全的顶层导航(如链接跳转)发送,但禁止在 <img>、<iframe>、POST 表单等场景下发送。- None: 允许所有跨站请求发送,但必须同时设置 Secure 属性。 | SameSite=Strict |
3. 前端操作 Cookie 的挑战与方案
前端通过 document.cookie 操作非 HttpOnly 的 Cookie,但其 API 非常原始和不便。
- 读取:
document.cookie返回一个由;分隔的所有 Cookie 的字符串,需要手动解析。 - 写入/更新:每次赋值都是新增或覆盖一个 Cookie,而不是替换整个字符串。
// 直接操作 document.cookie(不推荐)document.cookie = "username=Alice; max-age=3600; path=/; SameSite=Lax";推荐方案:使用成熟的库(如 js-cookie)或封装辅助函数来简化操作。
// 简单的辅助函数示例function getCookie(name) { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift();}4. Cookie 的生命周期
| 特性 | 会话 Cookie | 持久 Cookie |
|---|---|---|
| 存储位置 | 浏览器内存 | 设备硬盘 |
| 生命周期 | 浏览器关闭即删除 | 持续到 Expires 或 Max-Age 到期 |
| 设置标志 | 不包含 Expires/Max-Age | 必须包含 Expires 或 Max-Age |
| 安全性 | 较高(临时存在) | 需额外保护(加密 + HttpOnly/Secure) |
| 典型用途 | 临时会话、购物车、单次登录状态 | 持久登录、用户偏好、长期跟踪 |
| 用户控制 | 无法手动删除(关闭浏览器自动清除) | 可通过浏览器设置或开发者工具手动删除 |
5. 总结:Cookie 的优缺点
| 优点 | 缺点 |
|---|---|
| 自动发送,服务器驱动:无需手动管理,是实现有状态会话的基石。 | 性能开销:每个同源请求(包括图片、CSS)都会携带,增加了请求头的大小。 |
强大的控制属性:通过 HttpOnly, Secure, SameSite 等属性可实现高安全性。 | 容量极小:仅约 4KB,不适合存储复杂数据。 |
| 兼容性极佳:所有浏览器都支持。 | API 简陋:前端原生 API 难以使用,需要封装或使用库。 |
跨子域名共享:通过设置 Domain 属性,轻松实现主域名与子域名间的通信。 | 安全风险:配置不当容易遭受 XSS 和 CSRF 攻击。 |
🗂️ 四、IndexedDB:前端的强大数据库
当需要存储大量结构化数据,并进行高效查询时,IndexedDB 是不二之选。它是一个内置在浏览器中的事务型、异步的 NoSQL 数据库。
核心概念:
- 数据库(Database):存储的顶层容器,按域名划分。
- 对象存储空间(Object Store):类似于 SQL 中的表,用于存储数据对象。
- 索引(Index):用于对 Object Store 中的数据按特定属性进行快速检索。
- 事务(Transaction):所有数据操作(增删改查)都必须在事务中进行,保证了操作的原子性(要么全部成功,要么全部失败)。
- 异步 API:所有操作都是异步的,通过事件回调或 Promise 来处理结果,不会阻塞主线程。
优点:
- 存储容量巨大:通常可达数百 MB 甚至数 GB,具体取决于用户的磁盘空间。
- 支持复杂数据类型:可以存储 JavaScript 对象、文件、Blob 等。
- 支持事务:保证了数据的一致性和可靠性。
- 支持索引查询:可以根据数据的任意属性建立索引,实现快速检索。
- 可用于 Web Workers:可以在后台线程中操作数据库,避免影响 UI 性能。
缺点:
- API 相对复杂:原生 API 基于事件回调,比较繁琐。强烈推荐使用封装库,如
dexie.js,它提供了非常友好的 Promise-based API。 - 不适合服务器通信:它纯粹是客户端数据库,数据不会自动发送到服务器。
代码示例(使用 dexie.js 简化):
// 引入 dexie.js// <script src="https://unpkg.com/dexie/dist/dexie.js"></script>
// 1. 定义数据库结构const db = new Dexie('MyFriendsDB');db.version(1).stores({ friends: '++id, name, age' // '++id' 表示自增主键,'name' 和 'age' 是索引});
// 2. 添加数据async function addFriend() { try { await db.friends.add({ name: 'Bob', age: 30, }); console.log('Friend added!'); } catch (error) { console.error('Failed to add friend:', error); }}
// 3. 查询数据async function findFriends() { // 根据索引 'age' 查询 const oldFriends = await db.friends.where('age').above(25).toArray(); console.log('Friends older than 25:', oldFriends);}
addFriend();findFriends();💾 五、Cache Storage:为离线体验而生
Cache Storage 是一个专门用于存储 HTTP 请求和响应的缓存机制。它通常与 Service Worker 结合使用,是实现**渐进式网络应用(PWA)**离线访问能力的核心技术。
工作流程(与 Service Worker 配合):
- 拦截请求:Service Worker 作为一个网络代理,可以拦截页面发出的所有网络请求。
- 查询缓存:对于被拦截的请求,Service Worker 会首先检查 Cache Storage 中是否存在匹配的、已缓存的响应。
- 响应策略:
- 缓存命中(Cache Hit):如果找到缓存,则直接将缓存的响应返回给页面,无需发起网络请求,速度极快,且在离线时也能工作。
- 缓存未命中(Cache Miss):如果未找到缓存,则将请求转发到网络。获取到网络响应后,一方面将其返回给页面,另一方面可以将其副本存入 Cache Storage,以便下次使用。
优点:
- 实现真正的离线访问:可以缓存整个应用的 Shell(HTML、CSS、JS)和数据。
- 提升加载速度:对于已缓存的资源,加载速度接近瞬时。
- 编程控制力强:开发者可以完全控制缓存的策略(何时缓存、缓存什么、何时更新)。
- 存储容量大:与 IndexedDB 类似,容量非常可观。
示例(在 Service Worker 中):
const CACHE_NAME = 'my-app-cache-v1';const urlsToCache = ['/', '/styles/main.css', '/script/main.js'];
// 1. 安装时缓存核心资源self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => { console.log('Opened cache'); return cache.addAll(urlsToCache); }) );});
// 2. 拦截 fetch 请求并应用缓存策略self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request) .then(response => { // 如果缓存中有,则直接返回缓存的响应 if (response) { return response; } // 否则,发起网络请求 return fetch(event.request); }) );});📦 六、Web SQL(已废弃)
- 特征:一个基于 SQL 的关系型数据库 API。
- 状态:已被 W3C 废弃。由于缺乏统一的 SQL 方言标准,主流浏览器已停止支持。
- 建议:绝对不要在新的项目中使用。对于需要结构化存储的场景,请使用 IndexedDB。
✅ 总结对比表
| 存储方式 | 容量 | 生命周期 | 与服务器通信 | API 复杂度 | 主要用途 |
|---|---|---|---|---|---|
| Cookie | ~4KB | 可自定义过期时间 | 自动双向传递 | 原始 API 复杂 | 身份认证、会话保持、跟踪用户行为 |
| Local Storage | ~5MB | 永久(手动清除) | 需手动通过代码发送 | 非常简单 | 用户偏好、持久化 UI 状态、非关键数据缓存 |
| Session Storage | ~5MB | 标签页会话级 | 需手动通过代码发送 | 非常简单 | 多步表单临时数据、单页应用临时状态 |
| IndexedDB | 大型(GB 级) | 永久(手动清除) | 需手动通过代码发送 | 原生 API 复杂 | 客户端数据库、大量结构化数据、离线应用数据 |
| Cache Storage | 大型(GB 级) | 永久(代码控制) | 存储网络请求/响应 | 中等(常与 SW 结合) | PWA 离线缓存、应用 Shell 缓存、提升性能 |