cookie
- 存活期:cookie 可设置为长时间放弃
- 作用范畴:cookie 存储在客户端,发送 http 申请时会将 cookie 增加到 cookie 头字段,发送给服务器
- 存储量:单个 cookie 保留的数据不能超过 4K。
session
session 是另一种记录服务器和客户端会话状态的机制。session 是基于 cookie 实现的,session 存储在服务器端,sessionId 会被存储到客户端的 cookie 中。
- 存活期:session 个别生效工夫较短,客户端敞开或者 session 超时都会生效。
- 作用范畴:session 存储在服务端,安全性绝对 cookie 要好一些。
- 存储量:session 存储没有下限,但出于对服务器的性能思考,session 内不要寄存过多的数据,并且须要设置 session 删除机制。
token
- 简略 token 的组成:uid(用户惟一的身份标识)、time(以后工夫的工夫戳)、sign(签名,token 的前几位以哈希算法压缩成的肯定长度的十六进制字符串)
- token:服务端验证客户端发送过去的 token 时,还须要查询数据库获取用户信息,而后验证 token 是否无效。每一次申请都须要携带 token,须要把 token 放到 http 的 Header 里。基于 token 的用户认证是一种服务端无状态的认证形式,服务端不必寄存 token 数据。用解析 token 的计算工夫换取 session 的存储空间,从而加重服务器的压力,缩小频繁的查询数据库。
jwt
- JSON Web Token(简称 JWT)是目前最风行的跨域认证解决方案。是一种认证受权机制。JWT 是为了在网络应用环境间传递申明而执行的一种基于 JSON 的凋谢规范(RFC 7519)。JWT 的申明个别被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比方用在用户登录上
- 将 token 和 payload 加密后存储于客户端,服务端只须要应用密钥解密进行校验(校验也是 jwt 本人实现的)即可,不须要查问或者缩小查询数据库,因为 jwt 自蕴含了用户信息和加密的数据。
sessionStorage
- 存活期:sessionStorage 是会话级别贮存,在浏览器或页面敞开时数据就会销毁。不同的浏览器 tab 页面不雷同
- 作用范畴:只是客户端的贮存,不会波及到服务器贮存
- 存储量:localStorage 和 sessionStorage 个别有 5M
localStorage
- 存活期:localStorage 是长久化的本地贮存,不刻意去删除数据, 数据是不会销毁的。不同的浏览器 tab 页面雷同。
- 作用范畴:只是客户端的贮存,不会波及到服务器贮存。
- 存储量:localStorage 和 sessionStorage 个别有 5M。
- 数据结构:键值键,值只能是字符串
storage 事件:
- 当同源页面的某个页面批改了 localStorage, 其余的同源页面只有注册了 storage 事件,就会触发
-
示例
// 页面 A 监听 import React, {useEffect} from 'react'; const HomePage = () => {const handleStorage = (e) => {console.log('e', e); }; useEffect(() => {window.addEventListener('storage', handleStorage); return () => {window.removeEventListener('storage', handleStorage); }; }, []); return <div style={{height: 'calc(100vh - 60px)', overflow: 'scroll' }}></div>; }; export default HomePage; // 页面 B 触发 localStorage import React from 'react'; const SubPage = () => { return (<div style={{ height: 'calc(100vh - 60px)', overflow: 'scroll' }}> <button onClick={() => {localStorage.setItem('zlz_test_storage', 'test-' + Math.ceil(Math.random() * 20)); }} > localStorage </button> </div> ); }; export default SubPage;
- 同页面触发,需额定自定义事件实现
Event
和dispatchEvent
const originSetItem = localStorage.setItem;
localStorage.setItem = function (key, value) {const setItemChange = new Event('storage');
setItemChange.key = key;
setItemChange.oldValue = localStorage.getItem(key);
setItemChange.newValue = value;
window.dispatchEvent(setItemChange);
originSetItem.apply(this, arguments);
};
indexDB
- IndexedDB 次要用于客户端存储大量结构化数据(包含, 文件 / blobs)。该 API 应用索引来实现对该数据的高性能搜寻。尽管 Web Storage 对于存储较大量的数据很有用,但对于存储更大量的结构化数据来说,这种办法不太有用
- IndexedDB 是一个基于 JavaScript 的面向对象的数据库。它容许咱们存储和检索用键索引的对象;能够存储结构化克隆算法反对的任何对象。咱们只须要指定数据库模式,关上与数据库的连贯,而后检索和更新一系列事务。
- 浏览器的本地数据库,异步,也有同源限度,容量在 250M 以上甚至没有限度
- 繁难封装
// DBUtils.ts
interface EventResponse extends EventTarget {
target: {result: IDBDatabase | unknown;};
}
type DBMethods = {getItem: (key: string) => Promise<unknown>;
getAllItem: () => Promise<unknown>;
getAllKeys: () => Promise<unknown>;
setItem: (key: string, value: string | number | boolean | unknown[] | Record<string, unknown>) => Promise<unknown>;
removeItem: (key: string) => Promise<unknown>;
removeAll: () => Promise<unknown>;};
interface DBUtilOption {
dbName?: string;
version?: number;
keyPath?: string;
}
export default class DBUtils {
dbName: string;
DBOpenResult: Promise<unknown>;
db: IDBDatabase | undefined;
keyPath: string;
constructor(config: DBUtilOption = {}) {const { dbName = 'cache', version = 1, keyPath = 'id'} = config;
this.dbName = dbName;
this.keyPath = keyPath;
this.DBOpenResult = new Promise((resolve, reject) => {
// 关上数据库
const request: IDBOpenDBRequest = window.indexedDB.open(this.dbName, version);
request.onerror = () => {reject('数据库关上异样');
};
request.onsuccess = () => {
this.db = request.result;
resolve('');
};
request.onupgradeneeded = (event) => {const ev = (event as unknown) as EventResponse;
const db = ev.target.result as IDBDatabase;
db.onerror = function () {reject('数据库关上失败');
};
// 创立一个数据库存储对象
const objectStore: IDBObjectStore = db.createObjectStore(this.dbName, {
keyPath,
autoIncrement: !config.keyPath,
});
// 建设一个索引, 没有配置则默认应用 id
objectStore.createIndex(keyPath, keyPath, {unique: true,});
};
});
}
setItem: DBMethods['setItem'] = (key, value) =>
new Promise((resolve, reject) => {this.DBOpenResult.then(() => {if (!this.db) return;
const transaction = this.db.transaction([this.dbName], 'readwrite');
// 关上曾经存储的数据对象
const objectStore = transaction.objectStore(this.dbName);
// 增加到数据对象中
// {key: value}
let values = {};
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || Array.isArray(value)) {values[this.keyPath] = key;
values[key] = value;
} else {
values = value;
if (!value[this.keyPath]) {values[this.keyPath] = key;
}
}
const objectStoreRequest = objectStore.put(values);
objectStoreRequest.onsuccess = () => {resolve('');
};
objectStoreRequest.onerror = (err) => {console.error(` 从数据库设置 ${key}异样 `, err);
reject();};
});
});
getItem: DBMethods['getItem'] = (key) =>
new Promise((resolve, reject) => {this.DBOpenResult.then(() => {if (!this.db) return;
const transaction = this.db.transaction([this.dbName], 'readonly');
const objectStore = transaction.objectStore(this.dbName);
const objectStoreRequest = objectStore.index(this.keyPath).get(key);
objectStoreRequest.onsuccess = () => {resolve(objectStoreRequest.result);
};
objectStoreRequest.onerror = (err) => {console.error(` 从数据库读取 ${key}异样 `, err);
reject();};
});
});
getAllItem: DBMethods['getAllItem'] = () => {// let recordList = [];
return new Promise((resolve, reject) => {this.DBOpenResult.then(() => {if (!this.db) return;
const transaction = this.db.transaction([this.dbName], 'readonly');
const objectStore = transaction.objectStore(this.dbName);
// const objectStoreRequest = objectStore.openCursor();
// objectStoreRequest.onsuccess = function (event) {
// var cursor = event.target.result;
// // 如果没有遍历完,持续上面的逻辑
// if (cursor) {// recordList.push(cursor.value);
// // 持续下一个游标项
// cursor.continue();
// // 如果全副遍历结束
// } else {// resolve(recordList);
// }
// };
const objectStoreRequest = objectStore.getAll();
objectStoreRequest.onsuccess = function (event) {const ev = (event as unknown) as EventResponse;
resolve(ev.target?.result);
};
objectStoreRequest.onerror = (err) => {console.error(` 从数据库读取异样 `, err);
reject();};
});
});
};
getAllKeys: DBMethods['getAllKeys'] = () =>
new Promise((resolve, reject) => {this.DBOpenResult.then(() => {if (!this.db) return;
const transaction = this.db.transaction([this.dbName], 'readonly');
const objectStore = transaction.objectStore(this.dbName);
const objectStoreRequest = objectStore.getAllKeys();
objectStoreRequest.onsuccess = function (event) {const ev = (event as unknown) as EventResponse;
resolve(ev.target?.result);
};
objectStoreRequest.onerror = (err) => {console.error(` 从数据库读取异样 `, err);
reject();};
});
});
removeItem: DBMethods['removeItem'] = (key) =>
new Promise((resolve, reject) => {this.DBOpenResult.then(() => {if (!this.db) return;
const transaction = this.db.transaction([this.dbName], 'readwrite');
const objectStore = transaction.objectStore(this.dbName);
const objectStoreRequest = objectStore.delete(key);
objectStoreRequest.onsuccess = () => {resolve('');
};
objectStoreRequest.onerror = (err) => {console.error(` 从数据库删除 ${key}异样 `, err);
reject();};
});
});
removeAll: DBMethods['removeAll'] = () =>
new Promise((resolve, reject) => {this.DBOpenResult.then(() => {if (!this.db) return;
const transaction = this.db.transaction([this.dbName], 'readwrite');
const objectStore = transaction.objectStore(this.dbName);
const objectStoreRequest = objectStore.clear();
objectStoreRequest.onsuccess = () => {resolve('');
};
objectStoreRequest.onerror = (err) => {console.error(` 数据库清空异样 `, err);
reject();};
});
});
}
- 应用示例
import React from 'react';
import DBUtils from './DBUtils';
const HomePage = () => {const db = new DBUtils({ dbName: 'indexdb_test_001', keyPath: 'fileKey'});
const {removeAll, removeItem, getItem, setItem, getAllItem, getAllKeys} = db;
const onSetValue = () => {const key = 'key-' + Math.ceil(Math.random() * 10);
setItem(key, { data: 'xxxxxdddd'});
setItem('token', '222222');
};
const onGetValue = async () => {const res = await getItem('token');
console.log(res);
};
const onGetAllValue = async () => {const res = await getAllItem();
const res2 = await getAllKeys();
console.log(res, res2);
};
const onDeleteValue = async () => {await removeItem('1111');
};
const onClear = async () => {await removeAll();
};
return (<div style={{ height: 'calc(100vh - 60px)', overflow: 'scroll' }}>
<p>
<button onClick={() => onSetValue()}> 设置值 </button>
</p>
<p>
<button onClick={onGetValue}> 获取值 </button>
</p>
<p>
<button onClick={onGetAllValue}> 获取所有值 </button>
</p>
<p>
<button onClick={onDeleteValue}> 删除值 </button>
</p>
<p>
<button onClick={onClear}> 重置 </button>
</p>
</div>
);
};
export default HomePage;