跳到主要内容

客户端存储

  • 明文存储 http 请求会话信息;
限制
  • 数量;
  • 大小;
    • 个体:4096 字节;
构成
  • 名称 + 值 + 域 + 路径 + 过期时间 + 有效时间 + 安全标志 + http 标识;
    • domain:指定对应域名携带 cookie;
    • path:指定对应路径携带 cookie;
    • expires:指定 cookie 过期时间,超过清楚;
    • max-age:指定 cookie 存在时间,超过清楚;
    • secure:指定只有在 https 协议下才会发送 cookie;
    • httpOnly:指定 cookie 无法通过 js 获取;
    • SameSite:限制 cookie 跨站点请求;
// 示例一
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value; expires=Mon, 22-Jan-07 07:10:24 GMT; domain=.wrox.com
Other-header: other-header-value
// 示例二
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value; domain=.wrox.com; path=/; secure
Other-header: other-header-value
// Document.cookie: string
// 获取 cookie
document.cookie;
// 设置 cookie
document.cookie = "name=Nicholas";
// 删除 cookie, 设置过期日期为过去日期
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
安全措施
  • 使用 httpOnly 限制 xss 攻击;
  • 使用 SameSite 限制 CSRF 攻击;
  • 使用 Secure 保证数据安全;

Web Storage

sessionStorage
  • 存储会话数据;
  • 键值对;
  • 同源存储;
    • 浏览器新建标签页,新建 sessionStorage;
    • 标签页跳转同源标签页,共享 sessionStorage;
localStorage
  • 键值对;
  • 必须在同一个域;
  • 持久化存储;
基本 API
// 存储数据
localStorage.setItem("name", "Nicholas");
// 获取数据
let name = localStorage.getItem("name");
// 删除数据
localStorage.removeItem("book");
// 删除所有数据
localStorage.clear();
// 根据索引获取键
localStorage.key(2);
隐式转换
  • Storage 只能存储字符串;
  • 非字符串存储之前自动转换为字符串;
存储事件
// storage 对象发生变化时触发 storage 事件
// 具有 domain + key + newValue + oldValue 四个属性
window.addEventListener("storage", (event) =>
console.log("Storage changed for ${event.domain}")
);
限制
  • 大小一般限制为 5 mb;

IndexedDb

基本概念

IndexedDb 结构
  • 数据库:一组对象存储的集合;
    • 对象存储:一组对象的集合;
      • 键值对;
  • 事务:数据库操作通过事务完成;
  • 游标:多次查询数据库;
  • 键范围:用于限制游标处理范围;
  • 索引:使用索引读写数据库;
使用逻辑
  • 使用 indexedDB 创建/打开数据库;
  • 使用 IDBOpenDBRequest/IDBRequest 响应事件;
  • 使用 IDBDatabase 操作数据库,创建事务,创建对象存储;
  • 使用 IDBTransaction 建立对象存储读写数据库;
  • 使用 IDBObjectStore 读写数据库,定义并使用游标,建立索引,使用索引;
  • 使用 IDBCursor 定义游标,使用键范围;
  • 使用 IDBKeyRange 定义键范围;
  • 使用 IDBIndex 读写数据库;

并发问题

// 两个不同标签页使用同一数据库
// 进行数据库升级操作有冲突
// 需设置 onversionchange 事件
const request = indexedDB.open("admin", 1);
request.onsuccess = (event) => {
const database = event.target.result;
// ...
database.onversionchange = () => database.close();
};

常用 API

indexedDB | IDBOpenDBRequest
const open = indexedDB.open("data");
// 新建数据库时触发
open.onupgradeneeded = (e: any) => {
const database = e.target.result;
// ...
};
// 连接数据库成功触发
open.onsuccess = (e: any) => {
const database = e.target.result;
// ...
};
// 连接数据库失败触发
open.onerror = (e: any) => {
const database = e.target.result;
// ...
};
IDBDatabase
// (method) IDBDatabase.close(): void 关闭数据库
// (method) IDBDatabase.createObjectStore(name: string, options?: IDBObjectStoreParameters | undefined): IDBObjectStore 创建对象存储
db.createObjectStore(type, { keyPath: "id" }); // 指定主键为 id
// (method) IDBDatabase.deleteObjectStore(name: string): void 删除对象存储
// (method) IDBDatabase.transaction(storeNames: string | string[], mode?: IDBTransactionMode | undefined, options?: IDBTransactionOptions | undefined): IDBTransaction 创建事务
const trans = db.transaction("cache", "readwrite"); // readonly(只读) + readwrite(读写)
IDBTransaction
// (method) IDBTransaction.objectStore(name: string): IDBObjectStore 获取对象存储
IDBObjectStore/
// (method) IDBObjectStore.add(value: any, key?: IDBValidKey | undefined): IDBRequest<IDBValidKey> 添加记录
// (method) IDBObjectStore.put(value: any, key?: IDBValidKey | undefined): IDBRequest<IDBValidKey> 更新记录
// (method) IDBObjectStore.get(query: IDBValidKey | IDBKeyRange): IDBRequest<any> 获取记录
// (method) IDBObjectStore.delete(query: IDBValidKey | IDBKeyRange): IDBRequest<undefined> 删除记录
// (method) IDBObjectStore.clear(): IDBRequest<undefined> 清空记录
// (method) IDBObjectStore.openCursor(query?: IDBValidKey | IDBKeyRange | null | undefined, direction?: IDBCursorDirection | undefined): IDBRequest<IDBCursorWithValue | null> 定义并使用游标
// (method) IDBObjectStore.createIndex(name: string, keyPath: string | string[], options?: IDBIndexParameters | undefined): IDBIndex 创建索引
// (method) IDBObjectStore.index(name: string): IDBIndex 使用索引
// (method) IDBObjectStore.deleteIndex(name: string): void 删除索引

// (property) IDBRequest<any>.onsuccess: ((this: IDBRequest<any>, ev: Event) => any) | null 请求成功触发
// (property) IDBRequest<any>.onerror: ((this: IDBRequest<any>, ev: Event) => any) | null 请求失败触发
IDBCursor
// (property) IDBCursor.direction: IDBCursorDirection 游标遍历方向
// (property) IDBCursor.key: IDBValidKey 对应记录的值
// (property) IDBCursor.primaryKey: IDBValidKey 游标使用键
// (method) IDBCursor.update(value: any): IDBRequest<IDBValidKey> 更新对应记录
// (method) IDBCursor.delete(): IDBRequest<undefined> 删除对应记录
// (method) IDBCursor.advance(count: number): void 向前移动游标
// (method) IDBCursor.continue(key?: IDBValidKey | undefined): void 向后移动游标
const store = transaction.objectStore(storeKey);
const request = store.openCursor();
request.onsuccess((event: any) => {
const cursor = event.target.result;
cursor.update("23"); // 游标重用同一个请求, 避免嵌套 onsuccess
cursor.continue();
});
IDBKeyRange
// (method) lowerBound(lower: any, open?: boolean | undefined): IDBKeyRange 设置下限
// (method) upperBound(upper: any, open?: boolean | undefined): IDBKeyRange 设置上限
// (method) bound(lower: any, upper: any, lowerOpen?: boolean | undefined, upperOpen?: boolean | undefined): IDBKeyRange 设置上下限
// (method) only(value: any): IDBKeyRange 设置唯一值
IDBIndex | IDBCursorWithValue
// (property) IDBIndex.name: string 索引名称
// (property) IDBIndex.keyPath: string | string[] 索引使用键
// (property) IDBIndex.objectStore: IDBObjectStore 对应对象存储
// (property) IDBIndex.unique: boolean 索引是否唯一
// (method) IDBIndex.openCursor(query?: IDBValidKey | IDBKeyRange | null | undefined, direction?: IDBCursorDirection | undefined): IDBRequest<IDBCursorWithValue | null> 使用游标
// (method) IDBIndex.openKeyCursor(query?: IDBValidKey | IDBKeyRange | null | undefined, direction?: IDBCursorDirection | undefined): IDBRequest<IDBCursor | null> 使用游标
// (method) IDBIndex.get(query: IDBValidKey | IDBKeyRange): IDBRequest<any> 获取对应记录
// (method) IDBIndex.getKey(query: IDBValidKey | IDBKeyRange): IDBRequest<IDBValidKey | undefined> 获取对应记录主键

// IDBCursorWithValue 返回记录为对象