Cookies 作为客户端与服务器交互的通道,保持客户端状态,本来就不是设计同来做前端本地存储的,所以其用于做前端本地存储本身缺陷挺多的:
- Cookie 会被附加在每个 HTTP 请求中,所以无形中增加了流量。
- 由于在 HTTP 请求中的 Cookie 是明文传递的,所以安全性成问题,除非用 HTTPS。
- Cookie 的大小限制在 4KB 左右,对于复杂的存储需求来说是不够用的。
WebStorage 是 HTML5 新增的本地存储解决方案之一,其分为 localStorage 和 sessionStorage,结合当前的浏览器份额来看,兼容性问题已经很小了。
特性 | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari (WebKit) |
---|---|---|---|---|---|
localStorage | 4 | 3.5 | 8 | 10.50 | 4 |
sessionStorage | 5 | 2 | 8 | 10.50 | 4 |
这里先说 Local Storage,Session Storage 与 Local Storage 区别是存储周期的区别,具体区别下文会补充。
usage
methods
相较与 Cookies 需要自己封装增删改查操作, Local Storage 提供了一系列可操作 API,使用上便捷不少。localStorage
与 sessionStorage
是一个 key-value
的存储结构,简单的可理解为一个对象。
set
设置指定 key 值。
// setItem(key. value)
localStorage.setItem(key, value);
// 直接在 localStorage 对象上赋值
localStorage.key = value;
// 同上
localStorage[key] = value;
鉴于 Local Storage 中只能存储字符串,传入的值会被默认 toString()
,所以需要序列化之后再存储。
value = JSON.stringify(value);
localStorage.setItem(key, value);
传入的 key 是 string 类型,非 string 类型也会被 toString
:
localStorage.setItem(1, 'test');
// 等价于
localStorage.setItem('1', 'test');
关于 toString()
的猜测可以用如下代码验证:
let a = {};
a.toString = function () {
return 'b';
}
localStorage.setItem(a, 'a');
// 最终存储的是 b:a
get
获取指定 key 值:
var value = localStorage.setItem(key);
var value = localStorage.key;
var value = localStorage[key];
同上取值的时候,建议反序列化之后再使用。
var value = localStorage.getItem(key);
value = JSON.parse(value);
如果读取不存在的 key 则返回 null
localStorage.getItem('not_exit_key');
// null
直接读取 localStorage
可读取 Local Storage 中存储的所有数据。
delete & clear
清空 Local Storage 指定键值。
localStorage.removeItem(key);
同样, delete 相应的 key 也是可行的。
delete localStorage.key
clear()
方法可完全清空 Local Storage。
localStorage.clear();
key & length
localStorage.key(index)
可获取指定对应 index
索引下对应的 key 值,index 需为 number 类型。Local Storage 存储顺序并不是按照存储循序排列的。
localStorage.setItem('b', 1);
localStorage.setItem('a', 1);
localStorage.key(0);
// a
localStorage.key(1);
// b
localStorage.length
可读取 Local Storage 的存储数量。
现在做一回孔乙己,论遍历 Local Storage 有多少种方式?
// 第一种方式
Object.keys(localStorage).forEach((key)=> {
console.log(localStorage.getItem(key);
});
// 第二种方式
for (let key in localStorage) {
if (localStorage.hasOwnProperty(key)) {
console.log(localStorage.getItem(key);
}
}
// 第三种方式
let length = localStorage.length;
for (let i = 0; i < length ; i++) {
const key = localStorage.key(i);
console.log(localStorage.getItem(key));
}
// ...
StorageEvent
当 storage 发生改变时,window 上的 StorageEvent 将会被触发,如下示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
var storageHandler = function(e) {
console.log(e);
};
window.addEventListener('storage', storageHandler, false);
</script>
</body>
</html>
<!-- Page A -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
localStorage.setItem('test', 'hello world');
</script>
</body>
</html>
<!-- Page B -->
页面 A 中监听了Storage 事件,当页面 B 中 Storage 发生变化时,页面 A 能够监听到事件。其中 event
对象有如下附加属性:
属性 | type | 只读类型 | 解释 |
---|---|---|---|
key |
DOMString | Read only | 代表属性名发生变化.当被 clear() 方法清除之后所有属性名变为 null |
newValue |
DOMString | Read only | 新添加进的值.当被 clear() 方法执行过或者键名已被删除时值为null |
oldValue |
DOMString | Read only | 原始值.而被 clear() 方法执行过,或在设置新值之前并没有设置初始值时则返回null。 |
storageArea |
nsIDOMStorage | Read only | 被操作的 storage 对象。 |
url |
DOMString | Read only | key 发生改变的对象所在文档的 URL 地址。 |
值得特别注意的是,该事件不在导致数据变化的当前页面触发。如果浏览器同时打开一个域名下面的多个页面,当其中的一个页面改变 sessionStorage 或 localStorage 的数据时,其他所有页面的
storage
事件会被触发,而原始页面并不触发storage
事件。可以通过这种机制,实现多个窗口之间的通信。所有浏览器之中,只有 IE 浏览器除外,它会在所有页面触发
storage
事件。
生存期 & 安全性
在数据存储的时效性上,Local Storage 并不会像 Cookie 那样可以设置数据存活的时限。也就是说,只要用户不主动删除,Local Storage 存储的数据将会永久存在。
通过 Session Storage 所存储数据的生命周期,和 Session 类似,关闭浏览器或标签页后数据就不存在了。但刷新页面或使用“前进”、“后退按钮”后sessionStorage仍然存在。
Local Storage 与 Session Storage 遵循同源策略:
所谓同源是指,域名,协议,端口相同
除此之外,Session Storage 每个窗口的值都是独立的(每个窗口都有自己的数据),它的数据会随着窗口的关闭而消失,窗口间的 Session Storage 也是不可以共享的。
Safari 浏览器如开启无痕模式, 执行 setItem
、getItem
等方法会抛出 QuotaExceededError
异常,需要做浏览器功能检测并做相对应的兼容;
function lsTest(){
var test = 'test';
try {
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch(e) {
return false;
}
}
// 如检测到不可用可用内存对象模拟作兼容
if (!lsTest()) {
var _$local_storage_mem_cache_ = {};
localStorage.__proto__.setItem = function(key, val) {};
localStorage.__proto__.getItem = function(key) {}
localStorage.__proto__.clear = function() {}
}
容量
相比 Cookies 小的可怜存储空间以及数量限制,Local Storage 与 Session Storage 算是赶上了 MB 时代,不过还是有限制的,一般认为主流浏览器容量限制为 5MB,但部分浏览器是 2.5MB
if(window.localStorage) {
var aa= 1024 * 1024 * 5 - unescape(encodeURIComponent(JSON.stringify(localStorage))).length;
}
如上代码可以简单的推测浏览器 Local Storage 剩余容量,但如果需准确获取,可以不断往 Local Storage 写入数据,直到报错,然后计算总共写入字节数。如下两个网址可测试 Local Storage
使用场景
替代 Cookies
这个不用多说,webStorage 生来就是为了改进 Cookies 不足的。
缺点
Local Storage 不可设置过期时间,解决方案可在写入数据同时写入一条记录过期时间的数据。
缓存静态资源
这种做法目前比较有争议。
优势
可以将静态资源存储在 Local Storage 中,从而减少静态资源的请求,进而优化静态资源的加载。
缺陷
- 静态资源更新的问题
- 存储在 Local Storage 中的 JavaScript 脚本 XSS 攻击问题
- 容量限制
同源窗口通信
如上文提到,利用 Local Storage 中的 storage 事件的特性,可以在窗口通信中进行通信。
表单持久化
比如编辑页面或者表单未提交前刷新了页面,数据丢失,带来不好的用户体验,可以将未提交的数据备份在 Local Storage 中,等用户提交完成后再删除备份。