KV 儲存體 - 網頁';第一個內建模組

瀏覽器供應商和網頁效能專家在過去十年中一直表示,localStorage速度緩慢,網頁開發人員應停止使用。

不過坦白說,這並非錯誤的說法。localStorage 是會封鎖主執行緒的同步 API,因此每次存取 localStorage 都可能會導致網頁無法進行互動。

問題是 localStorage API 實在太簡單,而 localStorage 唯一的非同步替代方案是 IndexedDB,但 (坦白說) 這項 API 並不以易用性或友善 API 著稱。

因此,開發人員只能選擇使用難以使用的 API,或是效能不佳的 API。雖然程式庫可提供 localStorage API 的簡易性,但實際上會在幕後使用非同步儲存空間 API,因此在應用程式中加入其中一個程式庫會增加檔案大小,並可能影響效能預算

不過,如果能以 localStorage API 的簡易性,搭配非同步儲存空間 API 的效能,且不必支付檔案大小費用,那該有多好!

不過,我們可能會很快推出。Chrome 正在實驗一項名為「內建模組」的新功能,而我們計劃推出的第一個內建模組,是名為 KV Storage 的非同步鍵/值儲存模組。

不過,在深入探討 KV Storage 模組的詳細資料前,讓我先解釋「內建模組」的意思。

什麼是內建模組?

內建模組與一般 JavaScript 模組相同,只是內建模組隨瀏覽器提供,因此不必下載。

與傳統網頁 API 一樣,內建模組必須經過標準化程序,每個模組都會有專屬規格,需要經過設計審查,並獲得網頁開發人員和其他瀏覽器供應商的支持,才能發布。(在 Chrome 中,內建模組會遵循我們用來實作及發布所有新 API 的啟動程序)。

與傳統網路 API 不同,內建模組不會在全域範圍中公開,只能透過匯入取得。

不公開內建模組可帶來許多優點:不會增加任何開銷,也不會影響啟動新的 JavaScript 執行階段背景 (例如新分頁、worker 或服務 worker),而且除非實際匯入,否則不會耗用任何記憶體或 CPU。此外,這些變數不會與程式碼中定義的其他變數發生命名衝突。

如要匯入內建模組,請使用前置字串 std:,後面加上內建模組的 ID。舉例來說,在支援的瀏覽器中,您可以使用以下程式碼匯入 KV Storage 模組 (請參閱下文,瞭解如何在不支援的瀏覽器中使用 KV Storage polyfill):

import storage, {StorageArea} from 'std:kv-storage';

KV 儲存空間模組

KV Storage 模組的簡易性與 localStorage API 相似,但其 API 形狀實際上更接近 JavaScript Map。它包含 get()set()delete(),而非 getItem()setItem()removeItem()。它也提供 localStorage 無法使用的其他類似地圖方法,例如 keys()values()entries()。此外,與 Map 一樣,其鍵不必是字串。這些值可以是任何結構化可序列化類型

Map 不同,所有 KV Storage 方法都會傳回 promise非同步疊代器 (因為這個模組的主要重點是它不同步,與 localStorage 相反)。如要查看完整 API 的詳細資訊,請參閱規格

您可能已經從上述程式碼範例中注意到,KV Storage 模組有一個預設匯出項目 storage,以及一個名為 StorageArea 的匯出項目。

storageStorageArea 類別的例項,名稱為 'default',也是開發人員在應用程式程式碼中最常使用的項目。StorageArea 類別適用於需要額外隔離的情況 (例如儲存資料的第三方程式庫,且希望避免與透過預設 storage 例項儲存的資料發生衝突)。StorageArea 資料會儲存在名稱為 kv-storage:${name} 的 IndexedDB 資料庫中,其中名稱是 StorageArea 例項的名稱。

以下是如何在程式碼中使用 KV Storage 模組的範例:

import storage from 'std:kv-storage';

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

  document.querySelector('form').addEventListener('submit', async () => {
    const newPreferences = Object.assign({}, oldPreferences, {
      // Updated preferences go here...
    });

    await storage.set('preferences', newPreferences);
  });
};

main();

如果瀏覽器不支援內建模組,該怎麼辦?

如果您熟悉在瀏覽器中使用原生 JavaScript 模組,可能知道 (至少到目前為止) 匯入網址以外的任何內容都會產生錯誤。std:kv-storage 不是有效的網址。

這就引發了一個問題:我們是否必須等到所有瀏覽器都支援內建模組,才能在程式碼中使用這些模組?很高興告訴你,答案是否定的!

只要有一個瀏覽器支援內建模組,您就可以使用內建模組,這要歸功於我們實驗的另一項功能,也就是匯入地圖

匯入地圖

匯入對應基本上是一種機制,可讓開發人員將匯入 ID 別名為一或多個替代 ID。

這項功能非常強大,因為您可以透過這項功能,在執行階段變更瀏覽器在整個應用程式中解析特定匯入 ID 的方式。

在內建模組的情況下,這可讓您在應用程式程式碼中參照模組的 polyfill,但支援內建模組的瀏覽器可以改為載入該版本!

以下說明如何宣告匯入對照表,讓這項功能與 KV Storage 模組搭配運作:

<!-- The import map is inlined into your page -->
<script type="importmap">
{
  "imports": {
    "/path/to/kv-storage-polyfill.mjs": [
      "std:kv-storage",
      "/path/to/kv-storage-polyfill.mjs"
    ]
  }
}
</script>

<!-- Then any module scripts with import statements use the above map -->
<script type="module">
  import storage from '/path/to/kv-storage-polyfill.mjs';

  // Use `storage` ...
</script>

上述程式碼的重點在於,網址 /path/to/kv-storage-polyfill.mjs 會對應至兩個不同的資源:std:kv-storage 和原始網址 /path/to/kv-storage-polyfill.mjs

因此,當瀏覽器遇到參照該網址 (/path/to/kv-storage-polyfill.mjs) 的匯入陳述式時,會先嘗試載入 std:kv-storage,如果無法載入,則會改為載入 /path/to/kv-storage-polyfill.mjs

再次強調,這裡的魔法在於瀏覽器不需要支援匯入地圖內建模組,即可使用這項技巧,因為傳遞至匯入陳述式的網址是 polyfill 的網址。polyfill 實際上並非備用方案,而是預設方案。內建模組是漸進式增強功能!

如果瀏覽器完全不支援模組,該怎麼辦?

如要使用匯入對應來有條件地載入內建模組,您必須實際使用 import 陳述式,這也表示您必須使用模組指令碼,也就是 <script type="module">

目前,超過 80% 的瀏覽器支援模組,如果是未支援的瀏覽器,您可以使用 module/nomodule 技巧來提供舊版套件。請注意,產生 nomodule 版本時,您必須加入所有 polyfill,因為您可以確定,不支援模組的瀏覽器絕對不會支援內建模組。

KV 儲存空間示範

為了說明您可以使用內建模組,同時支援舊版瀏覽器,我已整合上述所有技術並製作這部示範影片,讓您可以在現今的所有瀏覽器中執行:

  • 支援模組、匯入地圖和內建模組的瀏覽器不會載入任何不必要的程式碼。
  • 支援模組和匯入對應,但不支援內建模組的瀏覽器會載入 KV Storage polyfill (透過瀏覽器的模組載入器)。
  • 支援模組但不支援匯入地圖的瀏覽器也會載入 KV Storage polyfill (透過瀏覽器的模組載入器)。
  • 完全不支援模組的瀏覽器會在舊版套件中取得 KV Storage polyfill (透過 <script nomodule> 載入)。

這個示範程式由 Glitch 代管,因此您可以查看原始碼。我也會在 README 中詳細說明導入方式。如想瞭解如何建構,歡迎隨時查看。

如要實際查看原生內建模組的運作情形,您必須在 Chrome 74 以上版本中載入示範,並開啟實驗性網路平台功能標記 (chrome://flags/#enable-experimental-web-platform-features)。

您可以確認內建模組是否已載入,因為您不會在 DevTools 的來源面板中看到 polyfill 指令碼,而是會看到內建模組版本 (有趣的事實:您實際上可以檢查模組的來源程式碼,甚至可以在其中加入中斷點!):

Chrome 開發人員工具中的 KV Storage 模組來源

請提供意見回饋

這篇文章的介紹內容應該讓您瞭解內建模組的可能用途。希望你也感到興奮!我們非常歡迎開發人員試用 KV Storage 模組 (以及本文討論的所有新功能),並提供寶貴意見。

以下是 GitHub 連結,您可以在這裡針對本文提及的各項功能提供意見:

如果您的網站目前使用 localStorage,請嘗試改用 KV Storage API,看看是否符合所有需求。只要註冊 KV Storage 來源測試版,您就能立即部署這些功能。所有使用者都應能享有更出色的儲存空間效能,而 Chrome 74 以上版本的使用者也不必支付任何額外的下載費用。