2022年10月5日

LocalStorage,sessionStorage

Web 存储对象 localStoragesessionStorage 允许在浏览器中保存键值对。

有趣的是,数据可以在页面刷新(对于 sessionStorage)甚至浏览器完全重启(对于 localStorage)后仍然存在。我们很快就会看到这一点。

我们已经有了 cookie。为什么还需要额外的对象呢?

  • 与 cookie 不同,Web 存储对象不会在每次请求时发送到服务器。因此,我们可以存储更多数据。大多数现代浏览器允许至少 5 兆字节的数据(或更多),并且具有设置来配置此数据。
  • 同样与 cookie 不同,服务器不能通过 HTTP 标头来操作存储对象。所有操作都在 JavaScript 中完成。
  • 存储绑定到来源(域名/协议/端口三元组)。也就是说,不同的协议或子域推断出不同的存储对象,它们不能相互访问数据。

这两个存储对象提供相同的方法和属性

  • setItem(key, value) – 存储键值对。
  • getItem(key) – 通过键获取值。
  • removeItem(key) – 删除键及其值。
  • clear() – 删除所有内容。
  • key(index) – 获取给定位置的键。
  • length – 存储项的数量。

如您所见,它类似于 Map 集合 (setItem/getItem/removeItem),但也允许使用 key(index) 按索引访问。

让我们看看它是如何工作的。

localStorage 演示

localStorage 的主要功能是

  • 在来自同一来源的所有选项卡和窗口之间共享。
  • 数据不会过期。它在浏览器重启甚至操作系统重启后仍然存在。

例如,如果您运行此代码…

localStorage.setItem('test', 1);

…然后关闭/打开浏览器或只是在另一个窗口中打开相同的页面,那么您可以像这样获取它

alert( localStorage.getItem('test') ); // 1

我们只需要在同一个来源(域/端口/协议)上,url 路径可以不同。

localStorage 在所有具有相同来源的窗口之间共享,因此如果我们在一个窗口中设置数据,更改将在另一个窗口中可见。

类似对象的访问

我们也可以使用类似普通对象的方式获取/设置键,例如

// set key
localStorage.test = 2;

// get key
alert( localStorage.test ); // 2

// remove key
delete localStorage.test;

这是出于历史原因允许的,并且大部分情况下有效,但通常不建议这样做,因为

  1. 如果键是用户生成的,它可以是任何东西,例如 lengthtoString,或者 localStorage 的另一个内置方法。在这种情况下,getItem/setItem 工作正常,而类似对象的访问失败

    let key = 'length';
    localStorage[key] = 5; // Error, can't assign length
  2. 有一个 storage 事件,当我们修改数据时会触发。该事件不会发生在类似对象的访问中。我们将在本章后面看到这一点。

遍历键

正如我们所见,这些方法提供了“按键获取/设置/删除”功能。但是如何获取所有保存的值或键呢?

不幸的是,存储对象不可迭代。

一种方法是像遍历数组一样遍历它们

for(let i=0; i<localStorage.length; i++) {
  let key = localStorage.key(i);
  alert(`${key}: ${localStorage.getItem(key)}`);
}

另一种方法是使用 for key in localStorage 循环,就像我们对普通对象一样。

它遍历键,但也输出一些我们不需要的内置字段

// bad try
for(let key in localStorage) {
  alert(key); // shows getItem, setItem and other built-in stuff
}

…所以我们需要使用 hasOwnProperty 检查来过滤原型中的字段

for(let key in localStorage) {
  if (!localStorage.hasOwnProperty(key)) {
    continue; // skip keys like "setItem", "getItem" etc
  }
  alert(`${key}: ${localStorage.getItem(key)}`);
}

…或者只使用 Object.keys 获取“自己的”键,然后根据需要遍历它们

let keys = Object.keys(localStorage);
for(let key of keys) {
  alert(`${key}: ${localStorage.getItem(key)}`);
}

后者有效,因为 Object.keys 只返回属于该对象的键,忽略原型。

仅字符串

请注意,键和值都必须是字符串。

如果它们是其他类型,例如数字或对象,它们将自动转换为字符串。

localStorage.user = {name: "John"};
alert(localStorage.user); // [object Object]

我们可以使用JSON来存储对象。

localStorage.user = JSON.stringify({name: "John"});

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

也可以将整个存储对象字符串化,例如用于调试目的。

// added formatting options to JSON.stringify to make the object look nicer
alert( JSON.stringify(localStorage, null, 2) );

sessionStorage

sessionStorage 对象的使用频率远低于 localStorage

属性和方法相同,但限制更多。

  • sessionStorage 仅存在于当前浏览器标签页中。
    • 另一个具有相同页面的标签页将拥有不同的存储。
    • 但它在同一标签页中的 iframe 之间共享(假设它们来自相同的来源)。
  • 数据在页面刷新后仍然存在,但在关闭/打开标签页后则不存在。

让我们看看实际操作。

运行这段代码…

sessionStorage.setItem('test', 1);

…然后刷新页面。现在您仍然可以获取数据。

alert( sessionStorage.getItem('test') ); // after refresh: 1

…但是,如果您在另一个标签页中打开相同的页面,然后再次尝试,上面的代码将返回 null,表示“未找到”。

这正是因为 sessionStorage 不仅绑定到来源,还绑定到浏览器标签页。因此,sessionStorage 的使用非常有限。

存储事件

localStoragesessionStorage 中的数据更新时,存储 事件将触发,并具有以下属性:

  • key – 已更改的键(如果调用 .clear(),则为 null)。
  • oldValue – 旧值(如果键是新添加的,则为 null)。
  • newValue – 新值(如果键被删除,则为 null)。
  • url – 发生更新的文档的 URL。
  • storageArea – 发生更新的 localStoragesessionStorage 对象。

重要的是:该事件将在所有可以访问存储的 window 对象上触发,除了导致该事件的 window 对象。

让我们详细说明。

假设您有两个窗口,每个窗口都包含相同的网站。因此,localStorage 在它们之间共享。

您可能希望在两个浏览器窗口中打开此页面以测试下面的代码。

如果两个窗口都监听 window.onstorage,那么每个窗口都会对另一个窗口中发生的更新做出反应。

// triggers on updates made to the same storage from other documents
window.onstorage = event => { // can also use window.addEventListener('storage', event => {
  if (event.key != 'now') return;
  alert(event.key + ':' + event.newValue + " at " + event.url);
};

localStorage.setItem('now', Date.now());

请注意,该事件还包含:event.url – 更新数据的文档的 URL。

此外,event.storageArea 包含存储对象 – 该事件对于 sessionStoragelocalStorage 都是相同的,因此 event.storageArea 引用了被修改的对象。我们甚至可能希望在其中设置一些内容,以“响应”更改。

这允许来自相同来源的不同窗口交换消息。

现代浏览器还支持 广播通道 API,这是一个用于相同来源的窗口间通信的专用 API,它功能更强大,但支持度较低。有一些库基于 localStorage 对该 API 进行填充,使其在任何地方都可用。

总结

Web 存储对象 localStoragesessionStorage 允许在浏览器中存储键值对。

  • keyvalue 都必须是字符串。
  • 限制为 5mb+,取决于浏览器。
  • 它们不会过期。
  • 数据绑定到源(域/端口/协议)。
localStorage sessionStorage
在具有相同源的所有选项卡和窗口之间共享 在浏览器选项卡内可见,包括来自相同源的 iframe
浏览器重启后仍然存在 页面刷新后仍然存在(但关闭选项卡后消失)

API

  • setItem(key, value) – 存储键值对。
  • getItem(key) – 通过键获取值。
  • removeItem(key) – 删除键及其值。
  • clear() – 删除所有内容。
  • key(index) – 获取键号 index
  • length – 存储项的数量。
  • 使用 Object.keys 获取所有键。
  • 我们以对象属性的方式访问键,在这种情况下,storage 事件不会触发。

存储事件

  • setItemremoveItemclear 调用时触发。
  • 包含有关操作的所有数据(key/oldValue/newValue)、文档 url 和存储对象 storageArea
  • 在所有可以访问存储的 window 对象上触发,除了生成它的那个对象(对于 sessionStorage,在选项卡内;对于 localStorage,在全局范围内)。

任务

创建一个 textarea 字段,该字段在每次更改时“自动保存”其值。

因此,如果用户意外关闭页面并再次打开它,他将在该位置找到他未完成的输入。

像这样

为任务打开一个沙箱。

教程地图

评论

在评论之前阅读此内容…
  • 如果您有改进建议,请提交 GitHub 问题或拉取请求,而不是评论。
  • 如果您不理解文章中的某些内容,请详细说明。
  • 要插入几行代码,请使用 <code> 标签;对于多行代码,请将它们包装在 <pre> 标签中;对于超过 10 行的代码,请使用沙箱(plnkrjsbincodepen…)。