KV Storage – pierwszy wbudowany moduł w sieci

Dostawcy przeglądarek i specjaliści ds. wydajności internetu przez większą część ostatniej dekady twierdzą, że localStorage działa wolno, a deweloperzy stron internetowych powinni przestać z niego korzystać.

Żeby nie było wątpliwości, ludzie twierdzą, że to nie jest błąd. localStorage to synchroniczny interfejs API, który blokuje wątek główny, a gdy go otworzysz, może uniemożliwić Ci interaktywność strony.

Problem polega na tym, że localStorage interfejs API jest tak kusząco prosty, a jedyną asynchroniczną alternatywą dla localStorage jest IndexedDB, które nie jest znane z łatwości obsługi i przyjaznego interfejsu API.

Deweloperzy nie muszą więc wybierać między czymś trudnym w użyciu, a czego niekorzystnym dla wydajności. Istnieją biblioteki, które oferują prostotę interfejsu API localStorage, a jednocześnie korzystają z podstawowych interfejsów API asynchronicznej pamięci masowej. Jedna z tych bibliotek w Twojej aplikacji ma koszt rozmiaru pliku i może wykorzystać budżet wydajności.

A co, gdyby udało się uzyskać wydajność asynchronicznego interfejsu API do przechowywania danych dzięki prostemu interfejsowi API localStorage bez konieczności ponoszenia kosztów związanych z rozmiarem pliku?

Może wkrótce będzie to możliwe. Chrome eksperymentuje z nową funkcją nazywaną modułami wbudowanymi. Pierwszą funkcją, którą planujemy wprowadzić, jest asynchroniczny moduł pamięci masowej klucz-wartość o nazwie KV Storage.

Zanim jednak przejdziemy do szczegółów modułu KV, wyjaśnijmy, co mam na myśli, mówiąc o modułach wbudowanych.

Co to są moduły wbudowane?

Moduły wbudowane są takie same jak zwykłe moduły JavaScript. Różnica polega na tym, że nie trzeba ich pobierać, bo jest dostarczany z przeglądarką.

Podobnie jak w przypadku tradycyjnych internetowych interfejsów API, wbudowane moduły muszą przejść proces standaryzacji – każdy z nich będzie miał własną specyfikację, która wymaga weryfikacji projektu oraz pozytywnego wsparcia ze strony programistów i innych dostawców przeglądarek. (W Chrome moduły wbudowane będą przebiegać tak samo jak proces wprowadzania na potrzeby wdrażania i dostarczania nowych interfejsów API).

W przeciwieństwie do tradycyjnych internetowych interfejsów API, moduły wbudowane nie są dostępne w zakresie globalnym – można je uzyskać tylko za pomocą importu.

Nieudostępnianie modułów wbudowanych na całym świecie ma wiele zalet: nie zwiększa nakładu pracy przy uruchamianiu nowego kontekstu środowiska wykonawczego JavaScript (np. nowej karty, instancji roboczej czy skryptu service worker) i nie zużywają żadnej pamięci ani procesora, chyba że zostaną rzeczywiście zaimportowane. Co więcej, nie stwarzają ryzyka konfliktu nazw z innymi zmiennymi zdefiniowanymi w kodzie.

Aby zaimportować moduł wbudowany, użyj prefiksu std:, po którym następuje jego identyfikator. Na przykład w obsługiwanych przeglądarkach można zaimportować moduł KV Storage za pomocą tego kodu (poniżej opisano, jak użyć kodu polyfill KV w nieobsługiwanych przeglądarkach):

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

Moduł KV – miejsce na dane

Moduł KV Storage jest w swojej prostocie podobny do localStorage API, ale jego kształt interfejsu API jest zbliżony do JavaScript Map. Zamiast getItem(), setItem() i removeItem() ma wartość get(), set() i delete(). Ma też inne metody podobne do map, niedostępne w przypadku localStorage, takie jak keys(), values() i entries(). Podobnie jak Map jego klucze nie muszą być ciągami tekstowymi. Mogą to być dowolny typ uporządkowany i serializowalny.

W przeciwieństwie do metody Map wszystkie metody pamięci KV zwracają obietnice lub iteratory asynchroniczne (ponieważ ten moduł nie jest synchroniczny, w przeciwieństwie do metody localStorage). Aby zobaczyć szczegóły pełnego interfejsu API, zapoznaj się z specyfikacją.

Jak można było zauważyć w przykładzie powyżej, moduł KV Storage ma jeden domyślny eksport storage i jeden eksport o nazwie StorageArea.

storage to wystąpienie klasy StorageArea o nazwie 'default'. Tej nazwy programiści używają najczęściej w kodzie aplikacji. Klasa StorageArea jest potrzebna w sytuacjach, gdy wymagana jest dodatkowa izolacja (np. biblioteka innej firmy, która przechowuje dane i chce uniknąć konfliktów z danymi przechowywanymi przez domyślną instancję storage). Dane StorageArea są przechowywane w bazie danych IndexedDB o nazwie kv-storage:${name}, gdzie nazwa to nazwa instancji StorageArea.

Oto przykład użycia w kodzie modułu 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();

Co zrobić, gdy przeglądarka nie obsługuje wbudowanego modułu?

Jeśli potrafisz korzystać z natywnych modułów JavaScript w przeglądarkach, pewnie wiesz, że importowanie czegoś innego niż adres URL (przynajmniej do tej pory) będzie powodować błąd. std:kv-storage nie jest prawidłowym adresem URL.

Tu pojawia się pytanie: czy musimy czekać, aż wszystkie przeglądarki będą obsługiwać moduły wbudowane, zanim będzie można użyć ich w kodzie? Na szczęście odpowiedź brzmi: nie.

Modułów wbudowanych możesz używać od razu, gdy tylko jedna przeglądarka je obsługuje. Jest to możliwe dzięki funkcji importowania map, którą obecnie eksperymentujemy.

Importowanie map

Mapy importu to zasadniczo mechanizm, dzięki któremu programiści mogą tworzyć aliasy importowanych identyfikatorów do jednego lub kilku alternatywnych identyfikatorów.

Ta zaawansowana opcja pozwala zmienić (w czasie działania) sposób, w jaki przeglądarka rozpoznaje określony identyfikator importu w całej aplikacji.

W przypadku modułów wbudowanych można umieścić odwołanie do modułu polyfill w kodzie aplikacji, ale przeglądarka obsługująca ten moduł może wczytać tę wersję.

Aby zadeklarować mapę importu, aby działała z modułem 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>

Najważniejsze w tym kodzie jest to, że adres URL /path/to/kv-storage-polyfill.mjs jest mapowany na 2 różne zasoby: std:kv-storage, a następnie ponownie oryginalny adres URL: /path/to/kv-storage-polyfill.mjs.

Gdy więc przeglądarka napotka instrukcję importowania odwołującą się do tego adresu URL (/path/to/kv-storage-polyfill.mjs), najpierw próbuje wczytać element std:kv-storage, a jeśli to się nie uda, wraca do wczytywania /path/to/kv-storage-polyfill.mjs.

Magia polega na tym, że przeglądarka nie musi obsługiwać map importu ani wbudowanych modułów, by ta technika działała. Adres URL przekazywany do instrukcji importowania to adres URL programu polyfill. Polyfill to nie jest kod zastępczy, tylko domyślny. Wbudowany moduł to progresywne ulepszenie.

Co z przeglądarkami, które w ogóle nie obsługują modułów?

Aby używać map importu do warunkowego wczytywania modułów wbudowanych, musisz użyć instrukcji import, co oznacza również, że musisz użyć skryptów modułu, np. <script type="module">.

Obecnie ponad 80% przeglądarek obsługuje moduły, a w przeglądarkach, które ich nie obsługują, możesz wyświetlać starsze pakiety, korzystając z metody „module/nomodule”. Pamiętaj, że podczas generowania kompilacji nomodule musisz uwzględnić wszystkie elementy polyfill, ponieważ przeglądarki, które nie obsługują modułów, na pewno nie będą obsługiwać modułów wbudowanych.

Wersja demonstracyjna pamięci KV

Aby pokazać, że można używać modułów wbudowanych, nadal obsługiwać starsze przeglądarki, przygotowałem prezentację, która obejmuje wszystkie opisane powyżej metody i działa obecnie we wszystkich przeglądarkach:

  • Przeglądarki obsługujące moduły, importujące mapy i wbudowany moduł nie wczytują żadnego zbędnego kodu.
  • Przeglądarki, które obsługują moduły i importują mapy, ale nie obsługują wbudowanego modułu, wczytują kod polyfill pamięci masowej KV (przez program ładujący modułu przeglądarki).
  • Przeglądarki, które obsługują moduły, ale nie obsługują map importu, wczytują też kod polyfill KV Storage (przez program ładujący modułu przeglądarki).
  • Przeglądarki, które w ogóle nie obsługują modułów, otrzymują pakiet polyfill KV Storage w starszym pakiecie (pobieranym przez <script nomodule>).

Wersja demonstracyjna jest hostowana w Glitch, więc możesz zobaczyć jej źródło. Szczegółowe objaśnienie wdrożenia znajdziesz też w README. Rzuć okiem, jeśli chcesz się dowiedzieć, jak przebiega konstrukcja.

Aby zobaczyć, jak działa natywny moduł wbudowany, musisz załadować wersję demonstracyjną do Chrome 74 lub nowszej wersji z włączoną eksperymentalnej flagi funkcji platformy internetowej (chrome://flags/#enable-experimental-web-platform-features).

Możesz sprawdzić, czy moduł wbudowany w moduł się wczytuje, ponieważ w panelu źródłowym nie ma skryptu polyfill w Narzędziach deweloperskich. Zamiast tego zobaczysz wersję tego modułu (co ciekawostek: możesz sprawdzić kod źródłowy modułu, a nawet umieszczać w nim punkty przerwania):

Źródło modułu KV Storage w Narzędziach deweloperskich w Chrome

Przekaż nam swoją opinię

To wprowadzenie pozwoliło Ci zorientować się, co mogą być dostępne dzięki modułom wbudowanym. Mamy nadzieję, że cieszysz się z niego. Będziemy wdzięczni, jeśli deweloperzy wypróbują moduł KV Storage (oraz wszystkie nowe funkcje omówione tutaj) i podzielą się z nami swoją opinią.

Oto linki do GitHuba, za pomocą których możesz przesłać nam swoją opinię na temat poszczególnych funkcji wymienionych w tym artykule:

Jeśli Twoja witryna korzysta obecnie z interfejsu localStorage, przejdź na interfejs KV Storage API, aby sprawdzić, czy spełnia on wszystkie Twoje potrzeby. Jeśli zarejestrujesz się w wersji próbnej origin pamięci masowej KV, możesz wdrożyć te funkcje już dziś. Wszyscy użytkownicy powinni korzystać z większej ilości miejsca na dane, a użytkownicy Chrome w wersji 74 i nowszych nie będą musieli płacić żadnych dodatkowych kosztów pobierania.