kV-Speicher – das erste integrierte Modul im Web

In den letzten zehn Jahren haben Browseranbieter und Experten für Webleistung immer wieder darauf hingewiesen, dass localStorage langsam ist und dass Webentwickler das Tool nicht mehr verwenden sollten.

Um fair zu sein, sagen die Leute, dass das nicht falsch ist. localStorage ist eine synchrone API, die den Hauptthread blockiert. Jedes Mal, wenn Sie darauf zugreifen, verhindern Sie möglicherweise, dass Ihre Seite interaktiv ist.

Das Problem ist, dass die localStorage API einfach so verlockend einfach ist. Die einzige asynchrone Alternative zu localStorage ist IndexedDB, das nicht für seine Nutzerfreundlichkeit und freundliche API bekannt ist.

Entwicklern bleibt also die Wahl zwischen etwas schwer zu verwendendem und einem schlechten für die Leistung. Und obwohl es Bibliotheken gibt, die die Einfachheit der localStorage API bieten und gleichzeitig asynchrone Speicher-APIs im Hintergrund verwenden, verursacht das Einbinden einer dieser Bibliotheken in Ihrer Anwendung Kosten in der Dateigröße und kann Ihr Leistungsbudget stark beanspruchen.

Aber was wäre, wenn es möglich wäre, die Leistung einer asynchronen Storage API mit der Einfachheit der localStorage API zu nutzen, ohne die Kosten für die Dateigröße zu bezahlen?

Nun, vielleicht schon bald. Chrome experimentiert mit einer neuen Funktion, den sogenannten integrierten Modulen. Als Erstes möchten wir ein asynchrones Speichermodul für Schlüssel/Wert-Paare namens KV Storage veröffentlichen.

Bevor ich mich jedoch in die Details des KV-Speichermoduls eingehe, möchte ich kurz erklären, was ich mit integrierten Modulen meine.

Was sind integrierte Module?

Integrierte Module sind wie normale JavaScript-Module mit dem Unterschied, dass sie nicht heruntergeladen werden müssen, da sie mit dem Browser ausgeliefert werden.

Wie herkömmliche Web-APIs müssen auch integrierte Module einen Standardisierungsprozess durchlaufen. Jedes Modul hat eine eigene Spezifikation, die eine Designüberprüfung und positive Anzeichen von Support von Webentwicklern und anderen Browseranbietern erfordert, bevor sie versendet werden können. (In Chrome folgen die integrierten Module dem gleichen Einführungsprozess, den wir auch für die Implementierung und Auslieferung aller neuen APIs verwenden.)

Im Gegensatz zu herkömmlichen Web-APIs werden integrierte Module nicht auf globaler Ebene verfügbar gemacht, sondern sind nur über Importe verfügbar.

Integrierte Module nicht global verfügbar zu machen, hat viele Vorteile: Sie verursachen keinen Aufwand beim Starten eines neuen JavaScript-Laufzeitkontexts (z.B. ein neuer Tab, Worker oder Service Worker) und verbrauchen keinen Arbeitsspeicher oder keine CPU, sofern sie nicht tatsächlich importiert werden. Außerdem besteht keine Gefahr, dass Namenskonflikte mit anderen im Code definierten Variablen auftreten.

Verwenden Sie zum Importieren eines integrierten Moduls das Präfix std:, gefolgt von der ID des integrierten Moduls. In unterstützten Browsern könnten Sie beispielsweise das KV Storage-Modul mit dem folgenden Code importieren (siehe unten zur Verwendung eines KV Storage-Polyfill in nicht unterstützten Browsern):

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

Das KV-Speichermodul

Das KV-Speichermodul ist in seiner Einfachheit mit der localStorage API vergleichbar, seine API-Form entspricht jedoch eher einem JavaScript-Map. Anstelle von getItem(), setItem() und removeItem() gibt es get(), set() und delete(). Es gibt auch andere kartenähnliche Methoden, die für localStorage nicht verfügbar sind, z. B. keys(), values() und entries(). Wie Map müssen die Schlüssel keine Strings sein. Sie können einen beliebigen strukturierten-serialisierbaren Typ haben.

Im Gegensatz zu Map geben alle Methoden zur KV-Speicherung entweder Promise oder Asynchrone Iterationen zurück, da der Hauptpunkt dieses Moduls darin besteht, dass es im Gegensatz zu localStorage nicht synchron ist. Die vollständige API im Detail finden Sie in der Spezifikation.

Wie Sie vielleicht im obigen Codebeispiel bemerkt haben, hat das KV Storage-Modul einen Standardexport storage und einen namens Export StorageArea.

storage ist eine Instanz der StorageArea-Klasse mit dem Namen 'default'. Sie wird von Entwicklern am häufigsten in ihrem Anwendungscode verwendet. Die Klasse StorageArea wird für Fälle bereitgestellt, in denen zusätzliche Isolierung erforderlich ist (z.B. eine Drittanbieterbibliothek, die Daten speichert und Konflikte mit Daten vermeiden möchte, die über die Standardinstanz storage gespeichert werden). StorageArea-Daten werden in einer IndexedDB-Datenbank mit dem Namen kv-storage:${name} gespeichert, wobei Name der Name der StorageArea-Instanz ist.

Hier ein Beispiel für die Verwendung des KV Storage-Moduls in Ihrem Code:

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();

Was passiert, wenn ein Browser kein integriertes Modul unterstützt?

Wenn Sie mit der Verwendung nativer JavaScript-Module in Browsern vertraut sind, wissen Sie wahrscheinlich, dass das Importieren von anderen Elementen als einer URL (zumindest bisher) einen Fehler auslöst. Und std:kv-storage ist keine gültige URL.

Das wirft also die Frage auf: Müssen wir warten, bis alle Browser integrierte Module unterstützen, bevor wir sie in unserem Code verwenden können? Glücklicherweise lautet die Antwort nein.

Dank einer anderen Funktion, mit der wir gerade experimentieren, Karten importieren, können integrierte Module bereits von einem Browser unterstützt werden.

Karten importieren

Zuordnungen importieren sind im Wesentlichen ein Mechanismus, mit dem Entwickler Kennungen mit einer oder mehreren alternativen Kennungen importieren können.

Dies ist sehr leistungsfähig, da Sie damit (zur Laufzeit) ändern können, wie ein Browser eine bestimmte Import-ID in der gesamten Anwendung auflöst.

Bei integrierten Modulen können Sie auf einen Polyfill des Moduls in Ihrem Anwendungscode verweisen. Allerdings kann stattdessen ein Browser, der das integrierte Modul unterstützt, diese Version laden.

So deklarieren Sie eine Importkarte, damit dies mit dem KV-Speichermodul funktioniert:

<!-- 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>

Wichtig im Code oben ist, dass die URL /path/to/kv-storage-polyfill.mjs zwei verschiedenen Ressourcen zugeordnet wird: std:kv-storage und noch einmal der Original-URL /path/to/kv-storage-polyfill.mjs.

Wenn der Browser also auf eine Importanweisung stößt, die auf diese URL verweist (/path/to/kv-storage-polyfill.mjs), versucht er zuerst, std:kv-storage zu laden. Ist dies nicht möglich, wird /path/to/kv-storage-polyfill.mjs geladen.

Das Besondere daran ist, dass der Browser keine Importkarten oder integrierten Module unterstützen muss, damit diese Technik funktioniert. Dies liegt daran, dass die an die Importanweisung übergebene URL die URL für den Polyfill ist. Der Polyfill ist die Standardeinstellung, sondern kein Fallback. Das integrierte Modul ist eine schrittweise Verbesserung.

Was ist mit Browsern, die keine Module unterstützen?

Wenn Sie Importkarten verwenden möchten, um integrierte Module bedingt zu laden, müssen Sie import-Anweisungen verwenden. Das bedeutet auch, dass Sie Modulskripts verwenden müssen, z. B. <script type="module">.

Derzeit unterstützen mehr als 80% der Browser Module. In Browsern ohne Modul können Sie das Verfahren „module/nomodule“ verwenden, um ein Legacy-Bundle bereitzustellen. Beim Generieren des nomodule-Builds musst du alle Polyfills einbeziehen, da du sicher bist, dass Browser, die keine Module unterstützen, auf keinen Fall integrierte Module unterstützen.

Demo für KV-Speicher

Ich habe eine Demo zusammengestellt, in der alle oben beschriebenen Techniken angewendet werden und die derzeit in allen Browsern ausgeführt werden, um zu zeigen, dass es möglich ist, integrierte Module zu verwenden und gleichzeitig ältere Browser zu unterstützen:

  • Browser, die Module unterstützen, Karten importieren und das integrierte Modul importieren, laden keinen unnötigen Code.
  • Browser, die Module unterstützen und Karten importieren, aber nicht das integrierte Modul unterstützen, laden den KV-Speicher-Polyfill über das Modulladeprogramm des Browsers.
  • Browser, die Module, aber keine Kartenimporte unterstützen, laden auch den KV-Speicher-Polyfill (über das Modulladeprogramm des Browsers).
  • Browser, die keine Module unterstützen, erhalten den KV-Speicher-Polyfill in ihrem Legacy-Bundle, das über <script nomodule> geladen wird.

Die Demo wird bei Glitch gehostet, sodass du den Quellcode ansehen kannst. Die Implementierung wird in der Readme-Datei ausführlich erläutert. Werfen Sie einen Blick darauf, wenn Sie neugierig sind, wie er aufgebaut ist.

Um das integrierte native Modul in Aktion zu sehen, müssen Sie die Demo in Chrome 74 oder höher laden und das Flag für experimentelle Webplattformfunktionen aktivieren (chrome://flags/#enable-experimental-web-platform-features).

Du kannst prüfen, ob das integrierte Modul geladen wird, da das Polyfill-Skript im Quellfeld in den Entwicklertools nicht angezeigt wird. Stattdessen wird die Version des integrierten Moduls angezeigt. (Zusatzinfo: Du kannst den Quellcode des Moduls einsehen oder sogar Haltepunkte einfügen.):

Die Quelle des KV-Speichermoduls in den Chrome-Entwicklertools

Bitte gib uns Feedback.

Diese Einführung sollte Ihnen einen Vorgeschmack darauf geben, was mit integrierten Modulen möglich ist. Und hoffentlich sind Sie schon gespannt! Wir würden uns freuen, wenn Entwickler das KV-Speichermodul sowie alle hier besprochenen neuen Funktionen ausprobieren und uns Feedback geben würden.

Hier sind die GitHub-Links, über die Sie uns Feedback zu den in diesem Artikel genannten Funktionen geben können:

Wenn Ihre Website derzeit localStorage verwendet, sollten Sie zur KV Storage API wechseln, um zu sehen, ob sie all Ihre Anforderungen erfüllt. Wenn Sie sich für den Ursprungstest für KV-Speicher registrieren, können Sie diese Funktionen noch heute bereitstellen. Alle Nutzer profitieren in der Regel von einer besseren Speicherleistung und Nutzer von Chrome 74 oder höher müssen keine zusätzlichen Downloadkosten zahlen.