参考书籍
Cookie
Cookie 作为最老牌的存储型缓存,其诞生之初其实并不是为了提供浏览器存储的功能,而是为了辨别用户身份。实现页面的登录从而避免页面重复登录就是个很好的例子;
为什么Cookie只适合存储身份信息,而其它信息不建议放在Cookie内?
- Cookie的存储空间特别小,所有的Cookie总和不能超过
4KB - Cookie在同域下会被自动携带进行接口请求**,试想如果Cookie存储了大量非身份信息,在每次接口请求时都会携带上,势必会产生一些资源浪费;
如何操作Cookie
// 存储 Cookie
document.cookie='name=juejin; domain=juejin.cn'
// 读取 Cookie 只能通过 document.cookie 读取所有 Cookie 并进行字符串截取,非常不便
// 删除 Cookie
let date = new Date()
date.setTime(date.getTime() + 10000)
// 设置一个过期时间
document.cookie=`name=test; domain=juejin.cn; expires=${date.toGMTString()}`
如果我们需要增删改查cookie的话,绕不过对字符串的处理,虽然逻辑并不复杂,但是为了确保高效率以及万无一失,推荐使用js-cookie库对cookie进行操作。
import Cookies from 'js-cookie'
// 存储
Cookie Cookies.set('name', 'juejin', { domain: 'juejin.cn' })
// 读取
Cookie Cookies.get('name')
// 删除
Cookie Cookies.remove('name')
客户端存储token信息的方式有哪几种?
1、服务器自动植入
当我们实现登录的时候,将账号和密码通过接口的形式发送给服务端,服务端校验完账号密码,如果正确,会在服务端生成token,然后将token写入浏览器cookie中,通过返回前端的响应报头中设置首部字段 set-cookie 来将 token 信息植入浏览器 cookie 中。后续同域下的所有请求都会自动携带该cookie进行服务端通信。
2、前端手动植入
浏览器端支持cookie可以通过自动植入的方式进行cookie存储,但是在小程序或者APP没有浏览器环境,故不支持cookie。 针对于这些场景需要前端手动的植入token信息,可以和服务端沟通,将token放到接口响应实体中,前端拿到token,将token缓存在本地Web Storage中,然后对http请求进行封装,每个请求都携带上token。
Web Storage
在开发中,有很多场景需要在前端缓存一些数据,如果和身份不相关的话,放在cookie不合适,所以HTML5推出了WebStorage浏览器存储机制,其又分为sessionStorage和localStorage。 其可存储的大小在不同浏览器有不同的空间,整体2.5M~10M的空间;
- Session Storage 作为临时性的本地存储,其生命周期存在于网页会话期间,即使用
Session Storage存储的缓存数据在网页关闭后会自动释放,并不是持久性的。 - Local Storage 则存储于浏览器本地,除非手动删除,否则其一直存在,属于持久性缓存,其本身不支持设置过期时间,但是可以通过二次封装来实现过期时间机制。
另外其两个共同的一个特点:存储的数据都会被转成字符串类型,所以在存储对象时,我们要进行序列化处理JSON.stringfy(obj),还有存储Number类型时会被转成字符串等行为
// 存储 Local Storage 数据
localStorage.setItem('name', 'juejin')
// 读取 Local Storage 数据
localStorage.getItem('name')
// 删除 Local Storage 数据
localStorage.removeItem('name')
如何对localStorage封装,让其具备过期时间、存储对象的能力?
let storage = {
// 存储方法
setStorage: function (key, value, duration) {
let data = {
value: value,
expiryTime: !duration || isNaN(duration) ? 0 : this.getCurrentTimeStamp() + parseInt(duration)
}
localStorage[key] = JSON.stringify(data) // 进行序列化操作
},
// 获取方法
getStorage: function (key) {
let data = localStorage[key]
if (!data || data === "null") {
return null
}
let now = this.getCurrentTimeStamp()
let obj
try {
obj = JSON.parse(data); // 进行反序列化操作
} catch (e) {
return null
}
if (obj.expiryTime === 0 || obj.expiryTime > now) {
return obj.value
}
return null
},
// 删除方法
removeStorage: function (key) {
localStorage.removeItem(key)
},
// 获取当前时间戳
getCurrentTimeStamp: function () {
return Date.parse(new Date())
}
}
IndexDB
在平时开发中,cookie和Web Storage几乎能满足我们的业务开发场景,但是针对于特殊场景,需要在客户端存储较大数据时,可以考虑采用IndexDB(如果兼容性可以介绍)。 WebStorage最大可存储10M,而IndexDB支持不低于250M的空间。
IndexDB用法
获取到新建或者打开的数据库
// 首先判断浏览器是否支持
if (!('indexedDB' in window)) {
console.log('浏览器不支持 indexedDB')
return
}
let idb;
// 打开名为 juejin,版本号为 1 的数据库,如果不存在则自动创建
let request = window.indexedDB.open('juejin', 1)
// 错误回调
request.onerror = function (event) {
console.log('打开数据库失败')
}
// 成功回调
request.onsuccess = function (event) {
idb = request.result
console.log('打开数据库成功')
}
onupgradeneeded:版本变化触发。 例如新建数据库或更新数据库版本时会触发
request.onupgradeneeded = function(e) {
idb = e.target.result;
console.log('running onupgradeneeded')
// 新建对象表时,先判断该表是否存在
if (!idb.objectStoreNames.contains('store')) {
// 创建名为 store 的表,以 id 为主键
let storeOS = idb.createObjectStore('store', { keyPath: 'id' })
}
};
往库里插入数据
// 新增方法
function addItem(item) {
// 新增时必须指定表名和操作模式
let transaction = idb.transaction(['store'], 'readwrite')
// 获取表对象
let store = transaction.objectStore('store')
// 调用 add 方法新增数据
store.add(item)
}
let data = {
id: 1, // 主键 id
name: 'test',
age: '18',
}
addItem(data) // 调用新增方法
从InddexDB库中读取数据
// 读取方法
function readItem(id) {
// 创建事务,指定表名
let transaction = idb.transaction(['store'])
// 获取表对象
let store = transaction.objectStore('store')
// 调用 get 方法获取数据
let requestStore = store.get(id)
requestStore.onsuccess = function() {
if (requestStore.result) {
console.log(requestStore.result) // { id: 1, name: 'test', age: '18' }
}
}
}
readItem(1) // 获取主键 id 为 1 的数据
如果你觉着这样操作IndexDB觉着繁琐,可以考虑使用开源的NPM包www.npmjs.com/package/idb 来操作IndexDB,其对IndexDB做了很好的封装;