深蓝的天空

一只切图喵的碎碎念

Web Storage Learning

Cookies 作为客户端与服务器交互的通道,保持客户端状态,本来就不是设计同来做前端本地存储的,所以其用于做前端本地存储本身缺陷挺多的:

  1. Cookie 会被附加在每个 HTTP 请求中,所以无形中增加了流量。
  2. 由于在 HTTP 请求中的 Cookie 是明文传递的,所以安全性成问题,除非用 HTTPS。
  3. 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,使用上便捷不少。localStoragesessionStorage 是一个 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 浏览器如开启无痕模式, 执行 setItemgetItem等方法会抛出 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

  1. https://arty.name/localstorage.html
  2. http://dev-test.nemikor.com/web-storage/support-test/

使用场景

替代 Cookies

这个不用多说,webStorage 生来就是为了改进 Cookies 不足的。

缺点

Local Storage 不可设置过期时间,解决方案可在写入数据同时写入一条记录过期时间的数据。

缓存静态资源

这种做法目前比较有争议

优势

可以将静态资源存储在 Local Storage 中,从而减少静态资源的请求,进而优化静态资源的加载。

缺陷

  1. 静态资源更新的问题
  2. 存储在 Local Storage 中的 JavaScript 脚本 XSS 攻击问题
  3. 容量限制

同源窗口通信

如上文提到,利用 Local Storage 中的 storage 事件的特性,可以在窗口通信中进行通信。

表单持久化

比如编辑页面或者表单未提交前刷新了页面,数据丢失,带来不好的用户体验,可以将未提交的数据备份在 Local Storage 中,等用户提交完成后再删除备份。

参考

  1. http://www.cnblogs.com/dolphinX/p/3348469.html
  2. https://segmentfault.com/a/1190000003936684
  3. https://html.spec.whatwg.org/multipage/webstorage.html
  4. http://web.jobbole.com/92681/

发表评论

电子邮件地址不会被公开。 必填项已用*标注