ブラウザの新しいstorage moduleのKV Storageを試してみた！

# 参考・関連リンク

- [KV Storage: the Web's First Built-in Module &nbsp;|&nbsp; Web](https://developers.google.com/web/updates/2019/03/kv-storage)
- [WICG/kv-storage - github](https://github.com/WICG/kv-storage)
- [GoogleChromeLabs/kv-storage-polyfill - github](https://github.com/GoogleChromeLabs/kv-storage-polyfill)
- [tc39/proposal-javascript-standard-library - github](https://github.com/tc39/proposal-javascript-standard-library)
- [WICG/import-maps - github](https://github.com/WICG/import-maps)

**注意**

- **この記事内での引用は以下の参考サイトのgoogle翻訳です**
    - [KV Storage: the Web's First Built-in Module &nbsp;|&nbsp; Web](https://developers.google.com/web/updates/2019/03/kv-storage)


**記事の内容に間違いなどありましたらご指摘お願いします:bow:**

# KV Storageとは

> ブラウザベンダやWebパフォーマンスの専門家は、この10年間の大部分でlocalStorageは動作が遅く、Web開発者はそれを使うのをやめるべきだと言ってきました。

> 公平に言うと、これを言っている人々は間違っていません。 localStorageは、メインスレッドをブロックする同期APIです。アクセスするたびに、ページがインタラクティブに表示されなくなる可能性があります。

> 問題は、localStorage APIが非常に単純で、localStorageの唯一の非同期的な代替方法がIndexedDBであることです。これは（使いやすさや使いやすさのために知られていません）。

> そのため、開発者は使いにくいものとパフォーマンスに悪いもののどちらかを選択できます。そして、実際には非同期ストレージAPIを実際に使用しながらlocalStorage APIのシンプルさを提供するライブラリもありますが、その中にはファイルサイズのコストがかかり、パフォーマンスの予算を浪費する可能性があります。

> しかし、ファイルサイズのコストを支払う必要なしに、localStorage APIの単純さで非同期ストレージAPIのパフォーマンスを得ることが可能だったらどうでしょうか。

> まあ、すぐにあるかもしれません。 Chromeは組み込みモジュールと呼ばれる新機能を試しています。最初に出荷する予定の機能は、KV Storageという非同期のキー/値ストレージモジュールです。

localStorageと違い非同期で、かつ簡単に扱える、KV Storage ([Key-Value型データベース](https://www.google.com/search?q=Key-Value+Store))の機能のようです。

# 動くサンプル

chromeのバージョン73だと動かすことができません。※2019/03/31現在

> Note: The KV Storage module is currently available in Chrome 74 if you have the experimental web platform features flag turned on: (chrome://flags/#enable-experimental-web-platform-features).


![image.png](https://qiita-image-store.s3.amazonaws.com/0/142910/96fc1e24-7a96-8179-8a15-9330396e344c.png)

参考ページにある通りに、実験用Webプラットフォーム機能フラグをonにすると動かすことができたので、この記事ではその状態で試していきます。

chrome://flags/#enable-experimental-web-platform-features

こちらを `Enabled` にしてchromeを再起動する

![image.png](https://qiita-image-store.s3.amazonaws.com/0/142910/7d304a21-cd26-dded-9e76-945905efbf55.png)

ほぼ参考ページのサンプル通りですが、このように簡単に動かすことができました

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script type="module">
        import { storage } from "std:kv-storage";

        (async () => {
            const oldPreferences = await storage.get("preferences");

            document.querySelector("form").addEventListener("submit", async e => {
                e.preventDefault();
                const newPreferences = Object.assign({}, oldPreferences, {
                    date: new Date().toLocaleString()
                });

                await storage.set("preferences", newPreferences);
                console.log(await storage.get("preferences"));
            });
        })();
    </script>
</head>
<body>
    <form><input type="submit"></form>
</body>
</html>
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/142910/d79c511f-7027-49ae-7358-61e49502036b.png)

# ブラウザ対応

polyfillを使う

```bash
$ npm i kv-storage-polyfill
```

参考ページの通りだとこういう書き方になるようです

[コードはこちらにも用意しました](https://github.com/okumurakengo/00013_kv)

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>

    <script type="importmap">
    {
        "imports": {
            "/node_modules/kv-storage-polyfill/dist/kv-storage-polyfill.mjs": [
                "std:kv-storage",
                "/node_modules/kv-storage-polyfill/dist/kv-storage-polyfill.mjs"
            ]
        }
    }
    </script>

    <script type="module">
        import { storage } from '/node_modules/kv-storage-polyfill/dist/kv-storage-polyfill.mjs';

        (async () => {
            const oldPreferences = await storage.get('preferences');

            document.querySelector('form').addEventListener('submit', async e => {
                e.preventDefault();
                const newPreferences = Object.assign({}, oldPreferences, {
                    date: new Date().toLocaleString()
                });

                await storage.set('preferences', newPreferences);
                console.log(await storage.get('preferences'));
            });
        })();
    </script>
</head>
<body>
    <form><input type="submit"></form>
</body>
</html>
```

参考：https://github.com/WICG/import-maps#for-built-in-modules-in-browsers-without-import-maps

参考に書いてあることのまんまですが、以下のように動作してくれるとのことです

- importmapをサポートしていないブラウザはpolyfillを受け取ります
- importmapをサポートしているがKV Storageをサポートしていないブラウザは、polifillのURLからそれ自体へのマッピングになるので、polifillを受け取ります
- importmapとKV Storageの両方をサポートするブラウザは、polyfill URLからstd：kv-storageへのマッピングになるため、組み込みモジュールを受け取ります

polyfillを使うことで、Safariでも動作することが確認できました

![image.png](https://qiita-image-store.s3.amazonaws.com/0/142910/67196409-4d39-ee2f-aebc-32a280fe850a.png)

# API

※以下を参考にいくつか試してみました、詳細知りたい方は以下を参考にしてください:bow:

https://github.com/WICG/kv-storage

### 基本的な使い方

```html
    <script type="module">
        import { storage } from "std:kv-storage";

        (async () => {
            console.log(await storage.get("category")) // undefind
            await storage.set("category", "hoge"); // 値をセット
            console.log(await storage.get("category")) // hoge
            await storage.delete("category"); // 値を削除
            console.log(await storage.get("category")) // undefind

            await storage.set("foo", 1);
            await storage.set("bar", 2);
            for await (const [key, value] of storage.entries()) {
                console.log(key, value); // 「foo 1」「bar 2」がそれぞれ出力
            }
            await storage.clear() // 全て削除
            console.log(await storage.get("foo")) // undefind
            console.log(await storage.get("bar")) // undefind
        })();
    </script>
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/142910/9f5c5066-a286-9eb3-fb52-7958bddfa425.png)

### `new StorageArea()`

`new StorageArea` とすることで、データをそこに保存することができます。
キー名が衝突する可能性が低くなります

```html
    <script type="module">
        import { storage, StorageArea } from "std:kv-storage";

        (async () => {
            await storage.set("mycat", "Tom");
            console.assert(await storage.get("mycat") === "Tom");

            const otherStorage = new StorageArea("unique string");
            console.assert(await otherStorage.get("mycat") === undefined);
            await otherStorage.set("mycat", "Jerry");
            console.assert(await otherStorage.get("mycat") === "Jerry");
        })();
    </script>
```

---

最後まで読んでいただいてありがとうございましたm(_ _)m