JavaScriptでデータを保存する方法の比較
sessionStorage、localStorage、cookie、IndexedDBはそれぞれブラウザ上でデータを保存することができる点では似ていますが、機能の特徴から以下のような使い分けをすることができます。
- ページ表示中のみの短期的なデータ保存はsessionStorageを使う
- ブラウザを閉じた後もデータを保持したいときや、タブ間/ウインドウ間でデータを共有したいときはlocalStorageを使う
- サーバーにデータを送信したいときはcookieを使う
- オンライン/オフライン問わずブラウザで動作するアプリケーションのデータ保存、または大容量のデータを扱いたいときはIndexedDBを使う
以上を踏まえつつ、まずはWeb Storage (sessionStorageとlocalStorage)とcookie、そしてIndexedDBの特徴を比較していきます。
| 保存方法 | 保存期間 | 容量制限 | サーバーへのデータ送信 | アクセス範囲 | 特徴 | 
|---|---|---|---|---|---|
| sessionStorage | タブやウィンドウを閉じるときにデータ消去 | 5MB(Firefoxのみ10MB) | 送信しない | 同じタブ/ウインドウ内のみ | ページを表示している間のみなど、短期的なデータ保存に向いている | 
| localStorage | 手動で削除するまで | 5MB(Firefoxのみ10MB) | 送信しない | 同じオリジンのタブ/ウインドウ | 複数タブ間でデータを共有できる。ブラウザを閉じてもデータが残るため、ユーザーの設定情報を保存する用途に向いている。 | 
| cookie | 設定した期限まで | 4KB | 自動送信する | リクエストに含まれるため、サーバーからもデータを参照することができる | リクエストに含まれるため、サーバーとデータを共有する。ログインセッションなどサーバーと状態を共有するときに使用する | 
| IndexedDB | 永続 | 数GB(ブラウザによって異なる) | 送信しない | 同じオリジンのタブ/ウインドウ | 文字列以外にファイルや blobを含む大容量のデータを、キー&値のペアで格納するデータベース。トランザクションを使用することができる。オンライン/オフラインに関わらず動作するアプリケーションの構築に有用。 | 
それぞれの特徴を確認し、実装に適した方法を選択することになります。
以下は簡単に、それぞれの保存方法の使用に適した用途を挙げてみます。
| 保存方法 | 適した用途 | 
|---|---|
| sessionStorage | ログイン状態の保持など、ページのセッションとデータの保存期間を関連付けたくないときは localStorageやcookieが適していますが、それ以外のページセッション中のみの一時的なデータの保存に適しています。 | 
| localStorage | 同一オリジンであれば、タブ間/ウインドウ間でもデータを共有することができ、タブ/ウインドウを閉じたり、ブラウザを閉じてもデータは削除されません。 そのため、ログイン状態の保持、個人情報取り扱いの同意ポップアップへの同意状況などの使用に向いています。 | 
| cookie | 指定した期間の間はデータが削除されず、同一オリジンであればタブ間/ウインドウ間でもデータを共有することができます。 リクエスト毎にサーバーに自動的に送信され、ユーザーのセッション状態の管理などサーバーのセッションと関連付けたいときに有用な方法です。 サーバーにデータを送信する特徴がプライバシー上の懸念事項となるため、使用と扱いは慎重に検討する必要があります。 | 
| IndexedDB | 同一オリジンであれば、タブ間/ウインドウ間でもデータを共有することができ、タブ/ウインドウを閉じたり、ブラウザを閉じてもデータは削除されません。 扱うことができるデータの種類と保存できる容量が大きく、オンライン/オフライン関わらずに使用できるためブラウザで動作するアプリケーションのデータ保存に向いています。 Google Office、Microsoft To Doでも使用されています。 | 
sessionStorage
sessionStorageは同一タブ内でのセッション中にデータを保存、取得することができます。
同一タブ内であればページの再読み込み、同一ドメイン上のページ遷移でもデータは保持され共有することができます。
タブ/ウインドウを閉じたとき、ブラウザが終了したときにデータは自動的に削除されます。
sessionStorageの基本的な使い方
以下はsessionStorageの基本的な操作をするコード例です。
JavaScript コード例
window.addEventListener('DOMContentLoaded', () => {
  // (1)sessionStorage のデータの数を取得
  console.log( sessionStorage.length );
  for (let i=0; i < sessionStorage.length; i++) {
    // (2)sessionStorage のデータのキーを取得 (setItem()した順にはならないので注意)
    console.log( sessionStorage.key(i) );
  }
  // (3)sessionStorage に値をセット
  sessionStorage.setItem('key', 'value');
  sessionStorage.setItem('name', 'Taro');
  sessionStorage.setItem('status', '1');
  // (4)sessionStorage から値を取得
  console.log(sessionStorage.getItem("key"));
  console.log(sessionStorage.getItem("name"));
  console.log(sessionStorage.getItem("status"));
  // (5)指定したキーを持つ sessionStorage のみ削除
  sessionStorage.removeItem('key');
  // (6)セッション内の sessionStorage を全て削除
  sessionStorage.clear();
});setItemに指定したキーが存在しないときは新しくキー&値のペアを作成し、キーが既に使用されている場合は値を上書きします。localStorageとは切り離されているため、sessionStorageとlocalStorageでキーが重複していても別々の値を保存することができます。
localStorage
localStorageはsessionStorageと同様にキーと値のペアで文字列のデータを保存することができます。
値の設定、取得など基本的な使い方はsessionStorageと共通しています。
localStorageとsessionStorageの違いはデータの保存期間とアクセス範囲です。localStorageは設定した値はコードで明示的に削除を行うか、もしくはユーザーによるブラウザ上での削除操作やブラウザによる自動削除が実行されるまでデータが残ります。
もう一つのアクセス範囲、localStorageでは同一オリジン(同じURL)であればタブ間/ウインドウ間でデータを共有することができる点も大きな特徴です。
セッションを閉じても以前設定したデータを引き継ぐことができるため、昨今のWEBサイトで表示される「個人情報取り扱い」への同意状況を保持したり、ログイン状況を保持することに向いています。
ただし、localStorageはサーバーにデータを送信することはないため、cookieのようにサーバー側で管理することはできません。
localStorageの基本的な使い方
以下はlocalStorageの基本的な操作をするコード例です。
JavaScript コード例
window.addEventListener('DOMContentLoaded', function(){
  // (1)localStorage のデータの数を取得
  console.log( localStorage.length );
  for (let i=0; i < localStorage.length; i++) {
    // (2)localStorage のデータのキーを取得 (setItem()した順にはならないので注意)
    console.log( localStorage.key(i) );
  }
  // (3)localStorage に値をセット
  localStorage.setItem('key', 'value');
  localStorage.setItem('name', 'Taro');
  localStorage.setItem('status', '7');
  // (4)localStorage から値を取得
  console.log(localStorage.getItem("key"));
  console.log(localStorage.getItem('name', 'Taro'));
  console.log(localStorage.getItem("status"));
  // (5)特定のキーを持つ localStorage のみ削除
  localStorage.removeItem('temp');
  localStorage.removeItem("key");
  // (6)localStorage から全てのデータを削除
  localStorage.clear();
});setItemの挙動はsessionStorageと同様に、指定したキーが存在しないときは新しくキー&値のペアを作成し、キーが既に使用されている場合は値を上書きします。
また先述の通り、sessionStorageとは切り離されているため、localStorageとsessionStorageでキーが重複していても別々の値を保存することができます。
Cookie
CookieはWeb Storageが登場するよりも以前からデータ保存の仕組みとして使用されてきました。
容量は4KBと他の保存方法と比較すると小さいですが、手軽にテキストデータを保存・取得できるためとても便利です。
しかし近年プライバシーに配慮する流れが強くなり、HTTPヘッダー内にCookieを含めてサーバーに送信する性質を持つことから、ユーザーの同意なくデータを送信してしまうことが問題視されることも多くなっています。
そのため昨今はサイトアクセス時にユーザーにCookieの使用について利用目的を表示し、利用に同意した場合のみ使用するような制御が求められています。
Cookieはサーバーにデータを送信することができ、またHTTPレスポンスにSet-Cookieとしてデータを受け取ることもできることから、下記のような用途で使用することができます。
- セッション管理
- ログイン情報の管理、ショッピングカート情報の管理
- トラッキング
- ユーザー行動の記録や解析
Cookieの保存期間
Cookieの保存期間は「セッション」と「有効期限」の2種類あります。
さらに「有効期限」はexpiresにUTCタイムゾーンに基づいた日時を指定する方法と、max-ageで有効期限を秒単位で指定する2種類が使用できます。expiresとmax-ageのいずれも指定しなかった場合は自動的に「セッション」になり、セッションが終了するとき(ブラウザが閉じられたとき)にデータを削除する設定にすることができます。
| 保存期間の種類 | 保存期間 | 
|---|---|
| セッション | ページのセッションが終了するまで保存する。 ブラウザを閉じるとセッションが終了したこととなりデータは削除される。 ただし、タブを閉じて別タブで開いたり、再読み込みを行った際は削除されない。 | 
| 有効期限 max-age | クッキーの保存期間を秒単位で指定し、設定した秒数を経過するとデータを削除する。 | 
| 有効期限 expires | UTCタイムゾーンに基づいた日時を指定し、設定した日時を過ぎるとデータを削除する。 | 
Cookieの基本的な使い方
以下はCookieの基本的な操作を実行するコード例です。
JavaScript コード例
window.addEventListener('DOMContentLoaded', function(){
  // (1)Cookie に値をセット
  document.cookie = 'key=value';
  document.cookie = 'name=Taro';
  // (2)有効期限を現時刻から10秒後にした Cookie を作成 (キー status に有効期限を設定)
  const expiresDate = new Date(new Date().getTime() + 10 * 1000);
  document.cookie = 'status=1; expires=' + expiresDate.toUTCString();
  // (3)全ての Cookie を取得
  const allCookie = document.cookie;
  // (4)Cookie のキーと値のペアをオブジェクトに変換して取得
  const cookies = document.cookie.split(';');
  const allCookieData = new Object;
  if (cookies) {
    for (let i = 0; i < cookies.length; i++) {
      const cookie = cookies[i].trim();
      const cookieData = cookie.split('=');
      if (cookieData[0] && cookieData[1]) allCookieData[cookieData[0]] = cookieData[1];
    }
  }
  // (5)上記で作成したオブジェクトから特定の Cookie のデータを取得
  console.log(cookieData.name);
  // (6)Cookie から指定したキーのデータを削除 (キー name を削除)
  document.cookie = "key=; max-age=0;";
  // (7)Cookie から全てのデータを削除
  if (cookies) {
    for (let i = 0; i < cookies.length; i++) {
      const cookie = cookies[i].trim();
      const cookieData = cookie.split('=');
      if (cookieData[0]) document.cookie = cookieData[0] + "=; max-age=0;";
    }
  }
});(2)ではDateオブジェクトのgetTime()で現時刻をミリ秒単位で取得し、10 * 1000の10秒を足した時間をexpiresDateに格納します。(1000ミリ秒 = 1秒)
その後、取得した時刻からtoUTCStringメソッドより協定世界時 (UTC) のタイムゾーンに基づいた時刻を文字列で取得し、status=1に対してexpires=で有効期限を設定します。
Cookieは全てのデータを取得するときはdocument.cookieで一括で取得できるのですが、設定したデータを1つずつ取り出すときは(4)のように一手間必要です。
そのため、より直感的にCookieを操作できるライブラリ「js-cookie」というライブラリが広く使われています。
作成したCookieのデータを削除するときはmax-age=0;を設定する方法と、expires=に現時刻よりも以前の時刻を入れて削除する方法で削除することが可能です。
どちらでも削除は可能ですが、手軽でミスが起こりづらいmax-age=0;による削除がオススメです。
IndexedDB
IndexedDB(インデックス データベース)はブラウザで動作するNoSQLデータベースです。
オンライン/オフライン問わず使用することができ、Web StorageやCookieよりも大容量のデータを扱うことが可能です。
主な用途はブラウザで動作するアプリケーションのデータ保存、大容量なデータの保存になります。
高度な使い方ができる一方で、他の方法と比較すると使用手順は複雑になってしまいます。
IndexedDBの基本的な使い方
以下はIndexedDBの基本的な操作を実行するコード例です。
JavaScript コード例
const objectStoreName = "users";
const request = indexedDB.open("testDb");
request.onerror = (e) => {
  console.error(`Database error: ${e.target.error?.message}`);
};
request.onupgradeneeded = (e) => {
  // IndexedDB インターフェースを db に保存
  const db = e.target.result;
  // オブジェクトストアを作成する
  const objectStore = db.createObjectStore( objectStoreName, { keyPath: "uid"});
  objectStore.createIndex("name", "name", {unique: false});
  objectStore.createIndex("email","email", {unique: true});
  db.close();
};
request.onsuccess = (e) => {
  // IndexedDB インターフェースを db に保存
  const db = e.target.result;
  // 該当するオブジェクトストアがない場合は終了
  if (!db.objectStoreNames.contains(objectStoreName)) return;
  // 登録するデータを用意
  const userData = [
    { uid: "s001-52943", name: "Taro", email: "taro2@gray-code.com" },
    { uid: "s001-52944", name: "Emily", email: "emily2@gray-code.com" },
    { uid: "s001-52945", name: "Eiji", email: "eiji2@gray-code.com" },
    { uid: "s001-52947", name: "Yuko", email: "yuko2@gray-code.com" },
    { uid: "s001-52949", name: "Ryota", email: "ryota2@gray-code.com" },
    { uid: "s001-52950", name: "Akiko", email: "akiko2@gray-code.com" },
  ];
  try {
    const transaction = db.transaction( objectStoreName, "readwrite");
    const userObjectStore = transaction.objectStore(objectStoreName);
    // 用意したデータの件数分、データの登録を実行
    userData.forEach((user) => {
      if (!user.uid) return;
      userObjectStore.get(user.uid).onsuccess = (e) => {
        // 指定したキーのデータがすでに登録されていたら登録をスキップ
        if (e.target.result) return;
        // データを登録
        userObjectStore.add(user);
      };
    });
    // データ数を取得して console.log() で出力
    userObjectStore.count().onsuccess = (e) => {
      if (e.target.result) console.log(`Number of data: ${e.target.result}`);
    };
    // 全てのデータを取得して console.log() で出力
    userObjectStore.getAll().onsuccess = (e) => {
      if (e.target.result) {
        const allData = e.target.result;
        for (let i = 0; i < allData.length; i++) {
          console.log(allData[i]);
        }
      }
    };
    // 指定したキーを持つデータを取得して console.log() で出力
    userObjectStore.get("s001-52944").onsuccess = (e) => {
      if (e.target.result) console.log(`Name: ${e.target.result.name}`);
    };
    // 指定したキーを持つデータを更新
    const updateUid = "s001-52945";
    userObjectStore.get(updateUid).onsuccess = (e) => {
      // 指定したキーのデータを取得できないときは終了
      if (!e.target.result) return;
      // 更新するデータをセット
      const userData = e.target.result;
      userData.name = 'eiji3';
      userData.email = 'eiji.third@gray-code.com';
      // データの更新を実行
      const resultUpdate = userObjectStore.put(userData);
      resultUpdate.onsuccess = (e) => {
        console.log('更新しました');
      };
    };
    // 指定したキーを持つデータを削除
    const deleteUid = "s001-52949";
    userObjectStore.get(deleteUid).onsuccess = (e) => {
      // 指定したキーのデータを取得できないときは終了
      if (!e.target.result) return;
      // データの削除を実行
      const deleteResult = userObjectStore.delete(deleteUid)
      deleteResult.onsuccess = (e) => {
        console.log(`Deleted: ${deleteUid}`);
      };
    };
    // データを全て削除
    userObjectStore.clear();
    
  } catch (error) {
    console.error(`Error: ${error.message}`);
    return;
  }
  // DBの接続を解除
  const closeResult = db.close();
};ブラウザで保存されたデータを確認する方法
上記の方法で保存したデータはブラウザ上から値を確認したり、データの編集や削除を行うことができます。
今回はChrome、Firefox、Safari、Edgeの4種類のブラウザから確認する方法を紹介していきます。
これらのブラウザは同じことができるのですが、機能の名称が異なる点に注意してください。
Chromeでの確認方法
Chromeでは、右クリックをして開いたメニューから「検証」を選択するか、メニューの「表示」にある「開発 / 管理」から「デベロッパー ツール」を選択すると、ブラウザのツールが表示されます。
開いたツールから「Application」タブを選択すると、左側に「Storage」の項目があります。
「Storage」ではSession storage(sessionStorage)、Local storage(localStorage、Cookies(cookie)、IndexedDBから作成されたデータを確認することができます。


Firefoxでの確認方法
Firefoxでは、右クリックをして開いたメニューから「調査」を選択するか、メニューの「ツール」にある「ブラウザーツール」から「ウェブ開発ツール」を選択すると、ブラウザのツールが表示されます。
開いたツールから「ストレージ」タブを選択すると、左側にストレージ一覧が表示されます。
そこではCookie、Indexed DB、セッションストレージ(sessionStorageの値)、ローカルストレージ(localStorageの値)のそれぞれの値を確認することができます。


Safariでの確認方法
Safariでは、右クリックをして開いたメニューから「要素の詳細を表示」を選択するか、メニューの「開発」から「Webインスペクタを表示」を選択するとブラウザのツールが表示されます。
開いたツールから「ストレージ」タブを開くと、左側にストレージ一覧が表示されます。
そこではCookie、インデックス付きデータベース(IndexedDB)、セッションストレージ(sessionStorageの値)、ローカルストレージ(localStorageの値)からそれぞれの値を確認することができます。


Edgeでの確認方法
Edgeでは、右クリックをして開いたメニューから「開発者ツールで調査する」を選択するか、メニューの「その他のツール」から「開発者ツール」を選択するとブラウザのツールが表示されます。
開いたツールから「アプリケーション」タブを開き、左側の「ストレージ」から確認したいストレージを選択することでデータを確認することができます。ローカルストレージ(localStorageの値)、セッションストレージ(sessionStorageの値)、IndexedDB、Cookieから各ストレージの値を確認できます。

