一、Web 存储对象localStorage和sessionStorage允许我们在浏览器上保存键/值对。

  • key和value都必须为字符串。
  • 存储大小限制为 5MB+,具体取决于浏览器。
  • 它们不会过期。
  • 数据绑定到源(域/端口/协议)。

二、在页面刷新后(对于sessionStorage)甚至浏览器完全重启(对于localStorage)后,数据仍然保留在浏览器中。我们很快就会看到。
三、API:两个存储对象都提供相同的方法和属性:

  • setItem(key, value)—— 存储键/值对。
  • getItem(key)—— 按照键获取值。
  • removeItem(key)—— 删除键及其对应的值。
  • clear()—— 删除所有数据。
  • key(index)—— 获取该索引下的键名。
  • length—— 存储的内容的长度。

正如你所看到的,它就像一个Map集合(setItem/getItem/removeItem),但也允许通过key(index)来按索引访问。

访问方式

setItem/getItem

类对象形式访问

一、可以像使用一个普通对象那样,读取/设置键,像这样:

  1. // 设置 key
  2. localStorage.test = 2;
  3. // 获取 key
  4. alert( localStorage.test ); // 2
  5. // 删除 key
  6. delete localStorage.test;

1、这是历史原因造成的,并且大多数情况下都可行,但通常不建议这样做,因为:
(1)如果键是由用户生成的,那么它可以是任何内容,例如 length 或 toString ,也可以是localStorage的另一种内建方法。在这种情况下,getItem/setItem可以正常工作,而类对象访问的方式则会失败:

  1. let key = 'length';
  2. localStorage[key] = 5; // Error,无法对 length 进行赋值

遍历键

一、如何获取所有保存的值或键呢?因为存储对象是不可迭代的,所以有以下方法。

  • for循环
  • for…in + hasOwnProperty()
  • for…in + Object.keys()

    for循环

    1、方法1:像遍历数组那样遍历它们:
    1. for(let i = 0; i < localStorage.length; i++) {
    2. let key = localStorage.key(i);
    3. alert(`${key}: ${localStorage.getItem(key)}`);
    4. }

    for…in + hasOwnproperty

    1、方法2:使用for key in localStorage循环,就像处理常规对象一样。(不可行)
    (1)缺点:它会遍历所有的键,但也会输出一些我们不需要的内建字段。
    1. // 不好的尝试
    2. for(let key in localStorage) {
    3. alert(key); // 显示 getItem,setItem 和其他内建的东西
    4. }
    2、方法3:我们需要使用hasOwnProperty检查来过滤掉原型中的字段:
    for(let key in localStorage) {
    if (!localStorage.hasOwnProperty(key)) {
      continue; // 跳过像 "setItem","getItem" 等这样的键
    }
    alert(`${key}: ${localStorage.getItem(key)}`);
    }
    

    for…in + Object.keys

    1、方法4:使用Object.keys获取只属于“自己”的键,然后如果需要,可以遍历它们:
    let keys = Object.keys(localStorage); // Object.keys只返回属于对象的键,会忽略原型上的
    for(let key of keys) {
    alert(`${key}: ${localStorage.getItem(key)}`);
    }
    

    仅字符串

    一、键和值都必须是字符串。
    1、如果是任何其他类型,如数字或对象,它会被自动转换为字符串。

| 【示例】```javascript // 类对象形式 sessionStorage.user = {name: “John”}; alert(sessionStorage.user); // [object Object]

// setItem方法 sessionStorage.setItem(‘user1’, {name: ‘test’ }; alert(sessionStorage.user1); // [object Object]

![image.png](https://cdn.nlark.com/yuque/0/2022/png/355497/1648171360347-fb8b5984-50c6-471a-8bd9-a6b98c9f3950.png#clientId=u1aa0bcac-573c-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=602&id=ucf5c29b5&margin=%5Bobject%20Object%5D&name=image.png&originHeight=602&originWidth=3070&originalType=binary&ratio=1&rotation=0&showTitle=false&size=378989&status=done&style=none&taskId=u2fe706fb-2743-48df-a750-37cd29381b3&title=&width=3070) |
| --- |

<a name="sYyyz"></a>
## 对象处理
一、我们可以使用JSON来存储对象:

| 【示例】```javascript
sessionStorage.user = JSON.stringify({name: "John"});

// sometime later
let user = JSON.parse( sessionStorage.user );
alert( user.name ); // John

| | —- |

二、也可以对整个存储对象进行字符串化处理

| 【示例】出于调试目的:```javascript // 为 JSON.stringify 增加了格式设置选项,以使对象看起来更美观 alert( JSON.stringify(localStorage, null, 2) );

 |
| --- |


<a name="TDiYZ"></a>
# 本地存储的空间(size)
一、浏览器存储量测试:[http://dev-test.nemikor.com/web-storage/support-test/](http://dev-test.nemikor.com/web-storage/support-test/)

| 【示例】localStorage不同浏览器支持大小的统计方法```javascript

(function() {
    if(!window.localStorage) {
        console.log('当前浏览器不支持localStorage!')
    }    
    var test = '0123456789';
    var add = function(num) {
        num += num;
        if(num.length == 10240) {
            test = num;
            return;
        }
        add(num);
    }
    add(test);
    var sum = test;
    var show = setInterval(function(){
        sum += test;
        try {
            window.localStorage.removeItem('test');
            window.localStorage.setItem('test', sum);
            console.log(sum.length / 1024 + 'KB');
        } catch(e) {
            alert(sum.length / 1024 + 'KB超出最大限制');
            clearInterval(show);
        }
    }, 0.1)

| | —- |

二、存储空间

  • 主流浏览器(包含PC、移动端)都是5M的数量级。
  • 手机QQ、手机QQ浏览器、微信中是2.5M

超过最大值的行为

一、各浏览器都抛出一个错误 QUOTA_EXCEEDED_ERR
二、firefox、opera中,用户可以自己设置本地存储的大小

localStorage

一、localStorage最主要的特点是:
1、在同源的所有标签页和窗口之间共享数据。
(1)我们只需要在同一个源(域/端口/协议),URL 路径可以不同。
(2)在所有同源的窗口之间,localStorage数据可以共享。因此,如果我们在一个窗口中设置了数据,则在另一个窗口中也可以看到数据变化。
2、数据不会过期。它在浏览器重启甚至系统重启后仍然存在。

| 【示例】```javascript // 运行此代码 localStorage.setItem(‘test’, 1);

// 然后关闭/重新打开浏览器,或者只是在不同的窗口打开同一页面,然后你可以这样获取它: alert( localStorage.getItem(‘test’) ); // 1

 |
| --- |

<a name="lCtqZ"></a>
## 整站本地存储的规划
一、客户端的存储空间宝贵,然而站点也因为业务的不同,很难有一个统一的实施细则,但是有几个大原则不会变。

- 只保存重要页面的重要数据
   - 典型的,首页首屏
   - 对业务庞大的站点,这点尤其重要
- 极大提高用户体验的数据
   - 比如表单的状态,可以提交之前保存,当用户刷新页面时可以还原
   - 静态资源,比如 js 和 css
- 一个请求一个 key 值(一个 cgi 一个 key 值)
   - 避免请求链接加参数的 key (http://request-ajax.cgi[params]),这样必然让 key 值趋于冗余从而撑爆空间

以上几大原则仅作参考,一切从实际业务出发。

<a name="Lat4H"></a>
## 本地存储注意事项
<a name="h8Fwg"></a>
### 不总是有用
一、目前来说,localStorage 只能做为提升用户体验的手段,而不能成为客户端逻辑的可靠的、唯一的依赖,毕竟用户环境千差万别。 当不使用通用 local 库的情况下,务必作如下检查:
```javascript
if (window.localStorage) {
    try {
        localStorage.setItem('bla', 'bla');
    } catch (e) {
        if (e.name === 'QUOTA_EXCEEDED_ERR' || e.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
            // todo
        } else {
            // todo
        }
    }
}

key 过多

一、这个在上面的“原则”里面也有说,key 值应该整站统一约束。先整理如下规则,待讨论。

  • 页面 path 做为 key 值,该页面上的数据统一在 value 处理
  • 一条 cgi 一个 key 值,不同参数在 value 中处理(性能问题)

    序列化

  • 过于依赖 JSON.stringify ,性能问题,移动端尤其明显

    • value 尽量使用 string ```javascript // before function store(key, val) { localStorage.setItem(key, JSON.stringify(val)); } store(‘num’, 1); store(‘on’, true); store(‘name’, ‘pamela’);

    // after function store(key, val) {

    localStorage.setItem(key, val);
    

    } store(‘num’, ‘1’); store(‘on’, ‘true’); store(‘name’, ‘pamela’); ```

    频繁 set/get

  • 尽量将数据缓存进内存,然后页面卸载的时候一次写入。 ```javascript // before $(‘input[type=”checkbox”]’).click(function () { localStorage.setItem($(this).attr(‘name’), $(this).is(‘:checked’)); });

// after window.onunload = function () { $(‘input[type=”checkbox”]’).each(function () { localStorage.setItem($(this).attr(‘name’), $(this).is(‘:checked’)); }); };


<a name="MD2J8"></a>
### 阻塞UI
按钮在点击过程中会有 UI 变化,这个时候同步操作 local 就会阻塞 UI 。
```javascript
// before
$('button').click(function () {
    var name = localStorage.getItem('name');
    $('#name').html(name);
});

// after
$('button').click(function () {
    window.setTimeout(function () {
        var name = localStorage.getItem('name');
        $('#name').html(name);
    }, 10);
});

Storage 事件:同源的不同窗口通信

一、当localStorage或sessionStorage中的数据更新(调用setItem, removeItem, clear方法)后,storage事件就会触发,它具有以下属性:

  • key—— 发生更改的数据的key(如果调用的是.clear()方法,则为null)。
  • oldValue—— 旧值(如果是新增数据,则为null)。
  • newValue—— 新值(如果是删除数据,则为null)。
  • url—— 发生数据更新的文档的 url。
  • storageArea—— 发生数据更新的localStorage或sessionStorage对象。

二、storage事件会在所有可访问到存储对象的window对象上触发,导致当前数据改变的window对象除外。

| 【示例】详细解释。
1、想象一下,你有两个窗口,它们具有相同的页面。所以localStorage在它们之间是共享的。
2、在浏览器的两个窗口中打开此页面来测试下面的代码。
3、如果两个窗口都在监听window.onstorage事件,那么每个窗口都会对另一个窗口中发生的更新作出反应。```javascript // 在其他文档对同一存储进行更新时触发 window.onstorage = event => { // 等同于 window.addEventListener(‘storage’, () => { if (event.key != ‘now’) return; alert(event.key + ‘:’ + event.newValue + “ at “ + event.url); // event.url:发生数据更新的文档的 url };

localStorage.setItem(‘now’, Date.now());

4、event.storageArea包含存储对象 —— sessionStorage 和 localStorage 具有相同的事件,所以event.storageArea引用了被修改的对象。我们可能会想设置一些东西,以“响应”更改。<br />(1)这允许同源的不同窗口交换消息。 |
| --- |

三、如果以类对象方式访问时,不会触发该事件。<br />四、现代浏览器还支持[Broadcast channel API](https://developer.mozilla.org/zh/docs/Web/api/Broadcast_Channel_API),这是用于同源窗口之间通信的特殊 API,它的功能更全,但被支持的情况不好。有一些库基于localStorage来 polyfill 该 API,使其可以用在任何地方。

<a name="dbJ45"></a>
# sessionStorage
一、sessionStorage对象的使用频率比localStorage对象低得多。<br />二、属性和方法是相同的,但是它有更多的限制:<br />1、sessionStorage的数据只存在于当前浏览器标签页。<br />(1)具有相同页面的另一个标签页中将会有不同的存储。<br />(2)但是,它在同一标签页下的 iframe 之间是共享的(假如它们来自相同的源)。<br />2、数据在页面刷新后仍然保留,但在关闭/重新打开浏览器标签页后不会被保留。

| 【示例】运行效果。```javascript
// 运行此代码
sessionStorage.setItem('test', 1);

// 然后刷新页面。这时你仍然可以获取到数据:
alert( sessionStorage.getItem('test') ); // after refresh: 1

// 如果你在另一个新的标签页中打开此页面,然后在新页面中再次运行上面这行代码,则会得到null,表示“未找到数据”

| | —- |

三、sessionStorage不仅绑定到源,还绑定在同一浏览器标签页。因此,sessionStorage很少被使用。

第三方库

  • store.js :跨浏览器,ie6+ 。
  • lawnchair :轻量,为移动而生。
  • locache :又一通用库,优雅降级。提供超时 api 。
  • lscache:A localStorage-based memcache-inspired client-side caching library.
  • 自动保存表单
  • Inject:CommonJS and AMD module loading, cross-origin fetching, localStorage caching, and more.
  • basket.js:A script and resource loader for caching & loading files with localStorage