S16-00 专题-JS-Storage 
[TOC]
Storage 
API-Storage 
属性:
- storage.length:
number,只读,表示存储中键值对的总数。 
方法:
- storage.setItem():
(key,value),将 key 和 value 添加到存储中。如果 key 存在则更新对应的值。 - storage.getItem():
(key),返回 key 对应的 value。 - storage.removeItem():
(key),将该 key 从存储中删除。 - storage.clear():
(),清空存储中的所有key。 - storage.key():
(index),根据索引获取存储对象中的指定键。 
Storage 
Storage:是Web开发中用于在客户端(浏览器)持久化存储数据的技术,允许网页在不依赖服务器的情况下保存用户信息。
Storage主要提供了一种机制,可以让浏览器提供一种比cookie更直观的key、value存储方式。
本地存储:
localStorage:是 HTML5 Web Storage API 的一部分,用于在客户端(浏览器)持久化存储键值对数据。
sessionStorage:是 HTML5 Web Storage API 的一部分,用于在客户端(浏览器)临时存储键值对数据。
Cookies:是网站存储在用户浏览器中的小型文本数据(通常小于 4KB),用于在客户端和服务端之间传递信息。
IndexedDB:是浏览器提供的一种底层 异步 NoSQL 数据库,支持存储大量结构化数据(包括文件、Blob等),适用于复杂 Web 应用的客户端数据管理。
图例:


对比localStorage、sessionStorage:
我们会发现localStorage和sessionStorage看起来非常的相似。
那么它们有什么区别呢?
存储时间:关闭网页后重新打开,localStorage会保留,而sessionStorage会被删除;
页面内跳转:在页面内实现跳转,localStorage会保留,sessionStorage也会保留;
页面外跳转:在页面外实现跳转(打开新的网页),localStorage会保留,sessionStorage不会被保留;
localStorage 
localStorage:是 HTML5 Web Storage API 的一部分,用于在客户端(浏览器)持久化存储键值对数据。
核心特点:
- 持久性:数据除非手动删除(用户清除浏览器缓存或开发者调用API),否则永久保留。
 - 同源策略:同一域名、协议、端口下的页面共享同一存储空间,不同源页面无法互相访问。
 - 容量限制:通常为 5MB(不同浏览器可能略有差异),超出时会抛出错误。
 - 仅支持字符串:键和值均为字符串,存储对象需序列化(如 
JSON.stringify())。 
sessionStorage 
sessionStorage:是 HTML5 Web Storage API 的一部分,用于在客户端(浏览器)临时存储键值对数据。
核心特点:
- 会话级存储:数据仅在当前浏览器标签页或窗口有效,关闭后自动清除。
 - 同源策略:同一域名、协议、端口下的页面共享同一存储空间,但不同标签页的 
sessionStorage相互隔离。 - 容量限制:通常为 5MB(与 
localStorage一致),超出时会抛出错误。 - 仅支持字符串:键和值均为字符串,存储对象需序列化(如 
JSON.stringify())。 
Cookies 
概述 
Cookies:是网站存储在用户浏览器中的小型文本数据(通常小于 4KB),用于在客户端和服务端之间传递信息。
核心特点:
- 生命周期:可设置过期时间(
Expires或Max-Age),否则随浏览器关闭失效(会话Cookie)。 - 作用域:通过 
Domain和Path控制可访问的域名和路径。 - 存储容量:每个 Cookie 最大 4KB,每个域名通常最多允许 50-150 个 Cookies。
 - 自动携带:每次 HTTP 请求会自动附加同域名的 Cookies 到请求头的 
Cookie字段。 
主要用途:
- 会话管理:保持用户登录状态、记录购物车内容等。
 - 个性化:保存用户偏好(如语言、主题)。
 - 行为跟踪:记录用户行为用于分析或广告定向(需符合隐私法规)。
 
语法格式 
name=value; Expires=Wed, 21 Oct 2025 07:28:00 GMT; Max-Age=3600; Domain=example.com; Path=/; Secure; HttpOnly; SameSite=Lax- 必需字段: 
name=value:键值对,内容需 URL 编码(如空格转为%20)。
 - 可选属性: 
Expires:过期时间(GMT 格式)。Max-Age:存活时间(秒,优先级高于Expires)。Domain:指定可访问的域名(默认为当前域名,不包含子域名)。Path:指定可访问的路径(默认为当前路径)。Secure:仅通过 HTTPS 传输。HttpOnly:禁止 JavaScript 访问,防 XSS 攻击。SameSite:限制跨站请求携带 Cookie(值:Strict/Lax/None)。
 
操作方式 
不同环境有不同的操作 Cookies 的方法:
服务端操作 
服务端设置:通过 Set-Cookie HTTP 响应头创建或更新 Cookie:
HTTP/1.1 200 OK
Set-Cookie: user_id=123; Expires=Wed, 21 Oct 2025 07:28:00 GMT; Secure; HttpOnly示例:服务端(Node.js)设置 Cookie
const http = require('http');
http.createServer((req, res) => {
  res.setHeader('Set-Cookie', [
    'user_id=123; HttpOnly; Max-Age=3600',
    'theme=dark; SameSite=Lax'
  ]);
  res.end('Cookies set!');
}).listen(3000);客户端操作 
客户端设置:通过 JS 中的 document.cookie 设置/获取 Cookies:
// 创建 Cookie(需手动编码)
document.cookie = "username=John%20Doe; expires=Thu, 18 Dec 2024 12:00:00 UTC; path=/; Secure";
// 读取所有 Cookies
const cookies = document.cookie; // 返回字符串 "cookie1=value1; cookie2=value2"
// 删除 Cookie(设置过期时间为过去)
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";示例:客户端读取 Cookie
// 解析 Cookies 为对象
const cookies = document.cookie.split('; ').reduce((acc, cookie) => {
  const [name, value] = cookie.split('=');
  acc[name] = decodeURIComponent(value);
  return acc;
}, {});
console.log(cookies.user_id); // 输出 "123"安全问题 
CSRF(Cross-site Request Forgery,跨站请求伪造):攻击者诱导用户访问恶意页面,利用已登录的身份发起非法请求(如转账)。
防御:
- 设置 
SameSite=Lax(默认值,阻止跨站 POST 请求携带 Cookie)。 - 添加 CSRF Token 到请求中验证来源。
 
XSS(Cross-site scripting,跨站脚本攻击):恶意脚本窃取 Cookies。
防御:
- 敏感 Cookie 设置 
HttpOnly,禁止 JavaScript 访问。 - 对用户输入严格过滤和转义。
 
最佳安全实践:
敏感信息加密:
js// 服务端加密 Cookie 值 const encryptedValue = encryptAES('user123', secretKey); response.setHeader('Set-Cookie', `session=${encryptedValue}; HttpOnly; Secure`);最小化 Cookie 使用:
- 避免存储敏感数据(如密码、身份证号)。
 - 优先使用服务端 Session 管理身份验证。
 
隐私合规:遵循 GDPR、CCPA 等法规,明确告知用户并获取同意。
IndexedDB【 
概述 
IndexedDB:是浏览器提供的一种底层 异步 NoSQL 数据库,支持存储大量结构化数据(包括文件、Blob等),适用于复杂 Web 应用的客户端数据管理。
核心特点:
- 异步操作:所有读写操作非阻塞,通过事件或 Promise 处理结果。
 - 事务支持:确保数据操作的原子性和一致性。
 - 索引查询:支持基于键、索引的高效查询,类似关系型数据库。
 - 大容量存储:浏览器动态分配存储空间,通常可达数百MB甚至更高。
 - 同源策略:数据库按域名隔离,不同源页面无法互相访问。
 
进阶用法 
封装Storage 
基础封装 


优化:存储对象类型 
原生方法



优化:兼容local和session 


TS封装Storage@ 
使用TS封装的Cache类
enum EStorage {
  Local,
  Session
}
/** 封装Storage */
class Cache {
  storage: Storage
  constructor(type: EStorage) {
    this.storage = type === EStorage.Local ? localStorage : sessionStorage
  }
  getItem(key: string) {
    const res = this.storage.getItem(key)
    return res ? JSON.parse(res) : ''
  }
  setItem(key: string, value: any) {
    this.storage.setItem(key, JSON.stringify(value || ''))
  }
  removeItem(key: string) {
    this.storage.removeItem(key)
  }
  clear() {
    this.storage.clear()
  }
  key(index: number) {
    return this.storage.key(index)
  }
  addItem(key: string, value: any) {
    let res = this.getItem(key)
    if (!res) {
      // 不存在key
      this.storage.setItem(key, JSON.stringify(value || ''))
    } else {
      // 存在key
      if (Array.isArray(res)) {
        res = Array.isArray(value) ? [...res, ...value] : [...res, value]
      } else if (isObjectLiteral(res)) {
        res = { ...res, ...value }
      } else {
        res += value + ''
      }
      this.storage.setItem(key, JSON.stringify(res))
    }
  }
}
export const localCache = new Cache(EStorage.Local)
export const sessionCache = new Cache(EStorage.Session)工具方法
/**
 *  判断是否为对象字面量
 * @param value 要判断的值
 * @returns {boolean} 是否为对象字面量
 */
function isObjectLiteral(value: any) {
  return Object.prototype.toString.call(value) === '[object Object]'
    	 && value !== null 
    	 && !Array.isArray(value)
}测试
// 测试
localCache.setItem('age', 27)
// localCache.setItem('name', 'zhangsan')
// localCache.setItem('user', { name: 'zhangsan', age: 18 })
// localCache.setItem('count', ['one', 'two', 'three', 'four'])
// console.log(localCache.getItem('age'))
// console.log(localCache.getItem('name'))
// console.log(localCache.getItem('user'))
// console.log(localCache.getItem('count'))
// 添加值
// localCache.addItem('info', {})
// localCache.addItem('user', { city: 'beijing' })
// localCache.addItem('count', ['eight'])
// localCache.addItem('count', 'seven')
localCache.addItem('age', 10) // '2710'