前言
随着web应用的发展,对于客户端存储信息的需求也在增强,无论是用户信息、偏好等信息,都有必要存储在客户端
目前常用的客户端存储方案有三种:
- cookie
- 浏览器存储API
- IndexDB
cookie
cookie全称HTTP cookie,最初用于客户端存储会话信息。这个规范要求服务器在响应HTTP请求时,通过发送Set-Cookie HTTP头部包含会话信息。
如下例:
上面的服务端响应会设置一个名称为ttype的cookie,其值为WEB,并设置了有效期等信息
浏览器会存储这个cookie,并在之后的所有请求中都会通过HTTP头部将cookie回发给服务器
限制
- cookie是与特定域绑定的。在发送请求时,它会与请求一起发送到创建它的域。这个限制能保证cookie中存储的信息只对被认可的接收者开放,不被其他域接收
- cookie的大小和数量也会被限制:
- 浏览器不超过300个cookie
- 每个cookie不超过4096字节,也即4KB
- 每个域不超过20个cookie
- ... 如果cookie超出单个域的限制,那么浏览器就会删除之前设置的cookie 如果cookie超出大小限制,那么该cookie会被静默删除
cookie的构成
cookie在浏览器中有如下参数:
- 名称:也即键。cookie名不区分大小写,因此myCookie和MyCookie是一样的,但是仍然建议进行区分,因为某些服务器软件会区别对待。cookie名必须经过URL编码
- 值:存储在cookie里面的字符串值,这个值必须经过URL编码
- 域:cookie有效的域。发送到这个域的所有请求都会包含对应的cookie。这个值可能包含子域,例如www.voiceclub.cn ,也可以不包含,例如:.voiceclub.cn,表示包含voiceclub.cn下面的所有子域
- 路径:表示请求URL中包含该路径才会把cookie发送到服务器,例如可以指定cookie只能由www.voiceclub.cn/allaudio/ 访问,因此www.voiceclub.cn/ 下的页面就不会发送cookie,即使请求的是同一个域。默认情况下,该值为/表示全部
- 过期时间:表示何时删除cookie的时间戳。默认情况下,浏览器会在会话结束后删除所有的cookie,不过也可以设置cookie的过期时间,这样即便关闭浏览器cookie也会保存在浏览器上。过期时间设置为过去的时间会立即删除该cookie
- 安全标志:设置之后只有使用SSL安全连接的情况下才会把cookie发送到服务器。例如请求www.voiceclub.cn/ 会发送cookie,而www.voiceclub.cn/ 则不会
上述的这些参数都在Set-Cookie头部中使用分号加空格隔开,例如上面的示例
当在实际向服务器发送cookie时,只有名称和值会发送到服务器
最佳实践
如果不通过服务端“种”cookie的方式,前端在js中也可以使用相关API实现cookie的设置
document.cookie = 'name:smx';
代码非常简单,如果需要像服务端那样增加其他的参数的话,只需要在后面继续拼写其他参数就可以了:
document.cookie = 'name:smx; domain:.voiceclub.cn';
上面的名称和值字符并不需要编码,但是仍然推荐使用URL编码的方式书写
document.cookie = encodeURIComponent('name') + '=' + encodeURIComponent('smx') + '; domain=.voiceclub.cn';
为了方便cookie的设置与获取,最佳实践是声明一个cookie工具类来实现相关功能:
class CookieUtil {
static set(name, value, expires, path, domain, secure) {
let cookieText = `${encodeURIComponent(name) = encodeURIComponent(value)}`;
if (expires instanceof Date) {
cookieText += `; expires=${expires.toGMTString()}`;
}
if (path) {
cookieText += `; path=${path}`;
}
if (domain) {
cookieText += `; domain=${domain}`;
}
if (secure) {
cookieText += `; secure=`;
}
document.cookie = cookieText;
}
static get(name) {
const cookieName = encodeURIComponent(name) + '=';
const cookieStart = document.cookie.indexOf(cookieName);
let cookieValue = null;
if (cookieStart > -1) {
let cookieEnd = document.cookie.indexOf(';', cookieStart);
// 最后一个cookie
if (cookieEnd === -1) {
cookieEnd = document.cookie.length;
}
cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd));
return cookieValue;
}
}
static unset(name, path, domain, secure) {
CookieUtil.set(name, '', new Date(0), path, domain, secure);
}
}
Web Storage
Storage类型用于保存键值对,它的实例与其他对象一样,但增加了以下方法:
- clear 清空全部的键值对 未在Firefox中实现
- getItem(name) 获取对应键名的值
- key(index) 获取对应索引位置的值
- removeItem(name) 移除对应键名的键值对
- setItem(name, value) 设置给定键名的值
下面以sessionStorage为例,演示上面的方法以及属性写法
window.sessionStorage.setItem('1', 1);
console.log(window.sessionStorage);
/*
{
"IsThisFirstTime_Log_From_LiveServer": "true",
"1": "1"
}
*/
console.log(window.sessionStorage.key(1)); // 1
window.sessionStorage.clear();
console.log(window.sessionStorage.getItem('1')) // null
window.sessionStorage.name = 'smx';
console.log(window.sessionStorage);
/*
{
"IsThisFirstTime_Log_From_LiveServer": "true",
"name": "smx"
}
*/
delete window.sessionStorage.name;
console.log(window.sessionStorage);
/*
{
"IsThisFirstTime_Log_From_LiveServer": "true"
}
*/
虽然通过属性的方式也可以实现类似的功能,但是仍然建议使用给出的方法
sessionStorage
sessoinStorage只作用于当前会话,可以理解为当前tab页,当当前tab页关闭后,该数据会被清空
即使开启两个相同的网站,在其中一个网站设置sessionStorage,在另外一个页面也不会生效这意味着sessionStorage只对当前tab页面生效
通过刷新页面的方式并不会导致sessionStorage丢失
localStorage
localStorage提供了本地持久化存储能力,它保存的数据会直到js调用API清除或者用户手动清除浏览器缓存才删除
存储事件
每当storage对象发生变化时,都会在文档上触发storage事件。使用属性或setItem()设置值、使用delete或removeItem()删除值,以及每次调用clear()时都会触发这个事件
该事件有如下四个属性:
- domain:存储变化对应的域
- key:被设置或删除的键
- newValue:键被设置的新值,若键被删除则为null
- oldValue:键变化之前的值
例如:
window.addEventListener('storage', (event) => {
console.log(event);
});
window.localStorage.setItem('name', 'smx');
window.localStorage.setItem('age', 24);
window.localStorage.clear();
要注意的是,storage事件只会在同源且打开的其他页面被触发,例如某个页面修改storage对象另外一个页面进行监听,是可以监听到对象变化的,而在自身页面修改属性并监听是无法获取到变化的,这一点很重要,意味着storage事件就是实现同源网页之间的storage通信的
限制
与cookie相同,storage也有限制,一般来说每个源(协议、域、端口)会限制5M的大小,原因个人推测为防止用户数据被滥用以及保护系统存储空间
IndexDB
IndexDB已经发行很多年了,但是对于它几乎没有接触,毕竟在前端方面需要使用数据库存储数据的案例实在是太少。而且各种浏览器对于IndexDB的兼容方案也不统一,最大的问题是IndexDB的学习成本很高,要了解相当的的概念以及API,因此暂不展开对于它的了解