2022年10月5日

LocalStorage、sessionStorage

Web ストレージオブジェクト `localStorage` と `sessionStorage` を使用すると、ブラウザにキー/値のペアを保存できます。

興味深いのは、データがページの更新後(`sessionStorage` の場合)やブラウザの完全な再起動後も残ることです(`localStorage` の場合)。すぐに確認しましょう。

すでにクッキーがあります。なぜ追加のオブジェクトが必要なのでしょうか?

  • クッキーとは異なり、Web ストレージオブジェクトは各リクエストでサーバーに送信されません。そのため、はるかに多くのデータを保存できます。最新のブラウザのほとんどでは、少なくとも5メガバイトのデータ(またはそれ以上)を許可し、それを設定するための設定があります。
  • また、クッキーとは異なり、サーバーはHTTPヘッダーを介してストレージオブジェクトを操作できません。すべてがJavaScriptで行われます。
  • ストレージはオリジン(ドメイン/プロトコル/ポートのトリプレット)にバインドされています。つまり、異なるプロトコルやサブドメインは異なるストレージオブジェクトを意味し、互いのデータにアクセスできません。

両方のストレージオブジェクトは、同じメソッドとプロパティを提供します。

  • `setItem(key, value)` – キー/値のペアを保存します。
  • `getItem(key)` – キーで値を取得します。
  • `removeItem(key)` – キーとその値を削除します。
  • `clear()` – すべてを削除します。
  • `key(index)` – 指定された位置のキーを取得します。
  • `length` – 保存されているアイテムの数。

ご覧のとおり、これは `Map` コレクション (`setItem/getItem/removeItem`) と似ていますが、`key(index)` を使用してインデックスによるアクセスも可能です。

どのように機能するか見てみましょう。

localStorage デモ

`localStorage` の主な機能は次のとおりです。

  • 同じオリジンからのすべてのタブとウィンドウで共有されます。
  • データは期限切れになりません。ブラウザの再起動後やOSの再起動後も残ります。

たとえば、このコードを実行した場合…

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. キーがユーザー生成の場合、`length` や `toString`、または `localStorage` の他の組み込みメソッドなど、何でもかまいません。その場合、`getItem/setItem` は正常に機能しますが、オブジェクトのようなアクセスは失敗します。

    let key = 'length';
    localStorage[key] = 5; // Error, can't assign length
  2. `storage` イベントがあります。データを変更するとトリガーされます。このイベントは、オブジェクトのようなアクセスに対しては発生しません。この章の後半で説明します。

キーのループ処理

ご覧のとおり、メソッドは「キーによる取得/設定/削除」機能を提供します。しかし、保存されているすべての値またはキーを取得するにはどうすればよいでしょうか?

残念ながら、ストレージオブジェクトは反復可能ではありません。

1つの方法は、配列のようにループすることです。

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` は控えめに使用されます。

ストレージイベント

`localStorage` または `sessionStorage` のデータが更新されると、storage イベントがトリガーされ、次のプロパティが含まれます。

  • `key` – 変更されたキー(`.clear()` が呼び出された場合は `null`)。
  • `oldValue` – 古い値(キーが新しく追加された場合は `null`)。
  • `newValue` – 新しい値(キーが削除された場合は `null`)。
  • `url` – 更新が発生したドキュメントのURL。
  • `storageArea` – 更新が発生した `localStorage` または `sessionStorage` オブジェクトのいずれか。

重要なのは、イベントが、それを発生させたもの(`sessionStorage` の場合はタブ内、`localStorage` の場合はグローバル)を除く、ストレージにアクセスできるすべての `window` オブジェクトでトリガーされることです。

詳しく説明しましょう。

各々に同じサイトを含む2つのウィンドウがあるとします。そのため、`localStorage` はそれらの間で共有されます。

以下のコードをテストするには、このページを2つのブラウザウィンドウで開くことをお勧めします。

両方のウィンドウが `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` にはストレージオブジェクトが含まれます。イベントは `sessionStorage` と `localStorage` の両方で同じであるため、`event.storageArea` は変更されたものを参照します。変更に「応答」するために、そこに何かを設定したい場合もあります。

これにより、同じオリジンからの異なるウィンドウがメッセージを交換できます。

最新のブラウザは、Broadcast channel API (同じオリジンのウィンドウ間通信のための特別なAPI)もサポートしています。これは機能が充実していますが、サポートされている範囲は狭いです。`localStorage`に基づいて、このAPIをあらゆる場所で利用可能にするポリフィルを提供するライブラリがあります。

まとめ

Web ストレージオブジェクト `localStorage` と `sessionStorage` を使用すると、ブラウザにキー/値のペアを保存できます。

  • `key` と `value` の両方が文字列でなければなりません。
  • 制限は5MB以上で、ブラウザによって異なります。
  • 期限切れになりません。
  • データはオリジン(ドメイン/ポート/プロトコル)にバインドされます。
localStorage sessionStorage
同じオリジンを持つすべてのタブとウィンドウで共有されます。 同じオリジンからのiframeを含む、ブラウザタブ内で表示されます。
ブラウザの再起動後も残ります。 ページの更新後も残ります(ただし、タブを閉じると残らない)。

API

  • `setItem(key, value)` – キー/値のペアを保存します。
  • `getItem(key)` – キーで値を取得します。
  • `removeItem(key)` – キーとその値を削除します。
  • `clear()` – すべてを削除します。
  • `key(index)` – `index` 番目のキーを取得します。
  • `length` – 保存されているアイテムの数。
  • `Object.keys` を使用してすべてのキーを取得します。
  • オブジェクトのプロパティとしてキーにアクセスする場合、`storage` イベントはトリガーされません。

ストレージイベント

  • `setItem`、`removeItem`、`clear` の呼び出しでトリガーされます。
  • 操作に関するすべてのデータ(`key/oldValue/newValue`)、ドキュメントの `url`、およびストレージオブジェクトの `storageArea` が含まれます。
  • ストレージにアクセスできるすべての `window` オブジェクトでトリガーされます(それを生成したもの(`sessionStorage` の場合はタブ内、`localStorage` の場合はグローバル)を除く)。

課題

変更ごとにその値を「自動保存」する `textarea` フィールドを作成します。

そのため、ユーザーが誤ってページを閉じても、もう一度開くと、未完成の入力がその場所に表示されます。

このように

課題用のサンドボックスを開きます。

チュートリアルマップ

コメント

コメントする前にこれを読んでください…