本地存储

282 阅读6分钟

一、本地存储历程

image.png

  • 最早的Cookies,问题主要就是太小,大概也就4KB的样子,而且IE6只支持每个域名20个cookies,太少了。优势就是大家都支持,而且支持得还蛮好。很早以前那些禁用cookies的用户也都慢慢的不存在了,就好像以前禁用javascript的用户不存在了一样。
  • userData是IE的东西,垃圾。Flash吧,空间是Cookie的25倍,基本够用。再之后Google推出了Gears,虽然没有限制,但不爽的地方就是要装额外的插件。
  • 到了HTML5把这些都统一了,官方建议是每个网站5MB,非常大了,就存些字符串,足够了。比较诡异的是居然所有支持的浏览器目前都采用的5MB,尽管有一些浏览器可以让用户设置,但对于网页制作者来说,目前的形势就5MB来考虑是比较妥当的。

IndexDB是一个相对底层一点API。通常用于存储大量结构化数据,包括文件、Blob等。IndexDB的API使用索引来实现对数据的高性能读取。

localStorage、sessionStorage是浏览器最常用的数据存储方案。两个各有优缺点。

  • localStorage支持持久性存储,浏览器会话结束依旧可以存储数据在浏览器内存中,方便下次数据读取与使用。但缺点是存储的大小有限制,不同的浏览器基于的存储空间有所区别,但是都在5M左右。对于普通的业务场景足够胜任。但是对于通讯应用,存储对话记录这种高密度数据存储的场景来说,内存空间完全是不够用的。
  • sessionStorage支持会话存储,浏览器相同域名会话窗口之间支持内存数据共享。当会话结束之后,关闭会话窗口,内存数据会被清除。缺点很明显,会话不支持永久存储,同时内存空间也很小。

三者都是基于源(Origin)的对象存储。源(Origin)指的是协议、域名、端口。不同的源数据是不支持共享的,相互之间保持独立,能够完美的避免不同源的数据污染。

www.baidu.comwww.google.com两个是不同的源,数据均不支持共享。

如果不同源数据支持共享会造成严重的数据污染,如下面的场景:

www.baidu.com源存储了一个数据 键为a,值为b

www.google.com源存储了一个数据 键为a,值为b

两键值对修改会造成相互相应,从而引发数据污染。

二、localStorage

在HTML5中,本地存储是一个window的属性,包括localStorage和sessionStorage,从名字应该可以很清楚的辨认二者的区别,前者是一直存在本地的,后者只是伴随着session,窗口一旦关闭就没了。二者用法完全相同。

1.如何创建和访问 localStorage

(1)方式一:点的方式操作

localStorage.lastname="Smith";
document.write(localStorage.lastname);

(2)方式二:存储API方式操作

  • 调用setItem()方法,将对应的名字和值传递出去,可以实现数据存储
  • 调用getItem()方法,将名字传递出去,可以获取对应的值
  • 调用removeItem()方法,名称作为参数,可以删除对应的数据
  • 调用clear()方法,可以删除所有存储的数据
  • 使用length属性以及key()方法,传入0~length-1的数字,可以枚举所有存储数据的名字
localStorage.setItem("name", "haorooms");    // 以"name"为名字存储一个字符串
localStorage.getItem("name");            // 获取"name"的值

// 枚举所有存储的名字/值对
for(var i=0; i<localStorage.length; i++){        // length表示所有的名值对总数
    var name = localStorage.key(i);            // 获取第i对的名字
    var value = localStorage.getItem(name);        // 获取该对的值
}

localStorage.removeItem("name");        // 删除"name"项
localAStorage.clear();                // 全部删除

三、sessionStorage

sessionStorage用法和Localstorage用法一样,区别就是,sessionStorage是会话存储,关闭浏览器,存储内容就会被清除。

需要注意的是,HTML5本地存储只能存字符串,任何格式存储的时候都会被自动转为字符串,所以读取的时候,需要自己进行类型的转换

四、IndexDB

webStorage最显著的缺点是

  1. 存储空间太小
  2. 键值对总是以字符串形式存储,不支持其他数据格式,如Blob、ArrayBuffer
  3. 不支持异步操作,数据量大时进行I/O操作性能体验差

IndexDb成为了如上问题的解决方案。

空间大小

IndexDB存储空间根据不同的设备而定,因为不同设备存储空间大小不一致,浏览器的最大存储空间是动态的,它完全取决于你的磁盘大小。全局限制为可用磁盘空间的 50%。在 Firefox 中,一个名为 Quota Manager 的内部浏览器工具会跟踪每个源用尽的磁盘空间,并在必要时删除数据。

因此,如果你的硬盘驱动器是 500GB,那么浏览器的总存储容量为 250GB。如果超过此范围,则会发起称为源回收的过程,删除整个源的数据,直到存储量再次低于限制。删除源数据没有只删一部分的说法——因为这样可能会导致不一致的问题。

另外一个是当存储空间达到上限之后会执行LRU策略,当可用磁盘空间已满时,配额管理器将根据 LRU 策略开始清除数据——最近最少使用的源将首先被删除,然后是下一个,直到浏览器不再超过限制。

异步执行

IndexDB执行的操作都是异步完成的,以免阻塞应用程序。

由于存储不涉及到浏览器的Document操作,因此我们还可以配合Web Worker实现数据交互。把IndexDB的操作都放在Web Worker中,大大提升性能。

五、localforage

localForage 是基于 IndexedDB 封装的前端存储库,本质上是 IndexedDB 的 “易用版封装层”—— 它以 IndexedDB 为核心底层实现,同时兼容 WebSQL、localStorage(降级方案),目的是解决原生 IndexedDB API 繁琐、难用的问题。

localstorage 有几个问题:

  • 它是同步的,不管数据多大,我们需要等待数据从磁盘读取和解析,这会减慢我们的应用程序的响应速度,如果放到移动设备上,可想而之。
  • 仅支持字符串,如果是存对象还需要将对象JSON.stringify({name:”houyuewei”,age:20})下,用的时候再次转换,真是麻烦。
  • 不能加密存储到硬盘上,增加了很多危险性。
  • 永久存储,并且存储容量限制在10M

localForage出现的原因

LocalForage就解决了上面的问题,Mozilla 开发了一个叫 localForage 的库 ,使得离线数据存储在任何浏览器都是一项容易的任务。localForage 是一个使用非常简单的 JavaScript 库的,提供了 get,set,remove,clear 和 length 等等 API,还具有以下特点:

  • 支持回调的异步 API;
  • 支持 IndexedDB,WebSQL 和 localStorage 三种存储模式;
  • 支持 BLOB 和任意类型的数据,让您可以存储图片,文件等。
  • 支持 ES6 Promises
  • 支持angularjs,requires,embers等

eg:

  • 回调基本写法
localforage.setItem('key', 'value', function (err) {
  // if err is non-null, we got an error
  localforage.getItem('key', function (err, value) {
    // if err is non-null, we got an error. otherwise, value is the value
  });
});
  • Promise
localforage.setItem('key', 'value').then(function () {
  return localforage.getItem('key');
}).then(function (value) {
  // we got our value
}).catch(function (err) {
  // we got an error
});
  • async/await
try {
    const value = await localforage.getItem('somekey');
    // This code runs once the value has been loaded
    // from the offline store.
    console.log(value);
} catch (err) {
    // This code runs if there were any errors.
    console.log(err);
}