Rozdział 8. Local storage

> Dodaj do ulubionych

Przesz┼éo┼Ť─ç, tera┼║niejszo┼Ť─ç i przysz┼éo┼Ť─ç lokalnego sk┼éadowania danych przez aplikacje sieciowe

Do rzeczy

Mo┼╝liwo┼Ť─ç trwa┼éego przechowywania danych to jedna z cech daj─ůca aplikacjom instalowanym na komputerze przewag─Ö nad aplikacjami sieciowymi. Programy lokalne mog─ů korzysta─ç z warstwy abstrakcji dostarczanej przez system operacyjny i przy jej u┼╝yciu zapisywa─ç oraz odczytywa─ç takie dane, jak preferencje czy stan wykonawczy. Informacje mog─ů by─ç zapisane w rejestrze, plikach INI, plikach XML oraz w wielu innych miejscach. Je┼Ťli w programie trzeba przechowywa─ç wi─Öcej informacji ni┼╝ tylko pary klucz-warto┼Ť─ç, mo┼╝na utworzy─ç w┼éasn─ů baz─Ö danych, wynale┼║─ç w┼éasny format plik├│w oraz skorzysta─ç z wielu innych rozwi─ůza┼ä.

Kiedy┼Ť luksusy te by┼éy niedost─Öpne dla aplikacji sieciowych. Wprawdzie od dawana mo┼╝na u┼╝ywa─ç plik├│w cookie, ale s┼éu┼╝─ů one do przechowywania tylko bardzo ma┼éych ilo┼Ťci danych. Ponadto pliki te maj─ů trzy powa┼╝ne wady:

  • Dane cookie s─ů przesy┼éane w ka┼╝dym ┼╝─ůdaniu HTTP, przez co spowalniaj─ů dzia┼éanie aplikacji zmuszaj─ůc j─ů do niepotrzebnego przesy┼éania w t─Ö i z powrotem tych samych danych
  • Dane cookie s─ů przesy┼éane w ka┼╝dym ┼╝─ůdaniu HTTP w niezaszyfrowanej formie (chyba ┼╝e ca┼éa aplikacja jest serwowana poprzez SSL)
  • W cookie mo┼╝na przechowywa─ç maksymalnie oko┼éo 4 KB danych — wystarczy, aby spowolni─ç aplikacj─Ö, ale za ma┼éo, aby zrobi─ç z tego dobry u┼╝ytek

My natomiast potrzebujemy:

  • du┼╝o miejsca na dane;
  • miejsca na kliencie;
  • magazynu, kt├│ry nie znika po od┼Ťwie┼╝eniu strony;
  • magazynu, kt├│ry nie jest wysy┼éany na serwer.

Przed powstaniem j─Özyka HTML5 wszelki pr├│by opracowania satysfakcjonuj─ůcego rozwi─ůzania spe┼éz┼éy na niczym z r├│┼╝nych powod├│w.

Kr├│tka historia lokalnego przechowywania danych w czasach przed powstaniem HTML5

Na pocz─ůtku by┼é tylko Internet Explorer. A przynajmniej Microsoft chcia┼é, aby ┼Ťwiat tak my┼Ťla┼é. Firma ta w ramach dzia┼éa┼ä na polu pierwszej wielkiej wojny przegl─ůdarek opracowa┼éa wiele wynalazk├│w, kt├│re zaimplementowa┼éa w swojej maj─ůcej zako┼äczy─ç wszelkie wojny przegl─ůdarce Internet Explorer. Jedn─ů z tych nowo┼Ťci by┼éy zachowania DHTML, a jednym z tych zachowaniem by┼éo userData.

userData pozwala stronom na przechowywanie do 64 KB danych na domen─Ö w hierarchicznej strukturze XML. (Zaufane domeny, np. witryny intranetowe, mog─ů przechowywa─ç dziesi─Ö─ç razy wi─Öcej danych. A 640 KB powinno wystarczy─ç ju┼╝ ka┼╝demu.) IE nie wy┼Ťwietla ┼╝adnego okna z pytaniem o pozwolenie i nie ma mo┼╝liwo┼Ťci zwi─Ökszenia limitu.

W 2002 r. firma Adobe wprowadzi┼éa we Flashu 6 funkcj─Ö, kt├│ra nieszcz─Ö┼Ťliwie zosta┼éa nazwana „cookie Flasha”. W samym ┼Ťrodowisku Flasha funkcja ta wyst─Öpuje pod prawid┼éow─ů nazw─ů Lokalne obiekty wsp├│lne (Local Shared Objects). M├│wi─ůc kr├│tko funkcja ta umo┼╝liwia Flashowi przechowywanie do 100 KB danych na domen─Ö. Brad Neuberg opracowa┼é jeden z pierwszych prototyp├│w most├│w ┼é─ůcz─ůcych Flasha i JavaScript o nazwie AMASS (AJAX Massive Storage System), ale jego mo┼╝liwo┼Ťci by┼éy ograniczone ze wzgl─Ödu na niedoskona┼éo┼Ťci projektowe Flasha. W 2006 r., gdy pojawi┼é si─Ö ExternalInterface we Flashu 8, dost─Öp do LSO z poziomu JavaScriptu sta┼é si─Ö znacznie ┼éatwiejszy i szybszy. Neuberg od nawa napisa┼é AMASS i wcieli┼é go do popularnej biblioteki Dojo Toolkit pod przydomkiem dojox.storage. Flash daje ka┼╝dej domenie 100 KB darmowego miejsca na dane. Ponadto umo┼╝liwia u┼╝ytkownikowi zwi─Ökszenie tego limitu o rz─Ödy wielko┼Ťci — do 1 Mb, 10 Mb itd.

W 2007 r. Google uruchomi┼éo Gears, otwart─ů wtyczk─Ö rozszerzaj─ůc─ů mo┼╝liwo┼Ťci przegl─ůdarek internetowych. (O Gears wspomina┼éem ju┼╝ przy omawianiu API geolokalizacji w Internet Explorerze.) Gears udost─Öpnia API umo┼╝liwiaj─ůce osadzanie baz danych SQL na silniku SQLite. Po uzyskaniu zgody od u┼╝ytkownika, Gears mo┼╝e przechowywa─ç nieograniczon─ů ilo┼Ť─ç danych na domen─Ö w bazie danych SQL.

W międzyczasie Brad Neuberg i inni kontynuowali pracę nad dojox.storage z zamiarem utworzenia jednolitego interfejsu do tych wszystkich różnych wtyczek i API. W 2009 r., dojox.storage automatycznie wykrywał (i udostępniał jednolity interfejs) Adobe Flash, Gears, Adobe AIR oraz wczesny prototyp magazynu HTML5, który był zaimplementowany tylko w starszych wersjach Firefoksa.

Gdy przyjrze─ç si─Ö tym wszystkim rozwi─ůzaniom, mo┼╝na zauwa┼╝y─ç pewne prawid┼éowo┼Ťci: wszystkie s─ů przypisanej jednej konkretnej przegl─ůdarce albo wymagaj─ů zewn─Ötrznych wtyczek. Mimo heroicznych wysi┼ék├│w, aby je ujednolici─ç w dojox.storage, rozwi─ůzania te radykalnie si─Ö mi─Ödzy sob─ů r├│┼╝ni─ů interfejsami, limitami pami─Öci oraz sposobem dzia┼éania. Dlatego problem postanowiono rozwi─ůza─ç w HTML5: opracowano standardowe API, kt├│re powinno by─ç jednakowo zaimplementowane we wszystkich przegl─ůdarkach i nie wymaga┼éo do obs┼éugi ┼╝adnych zewn─Ötrznych wtyczek.

Wprowadzenie do magazynu HTML5

Magazynem HTML5 nazywam specyfikacj─Ö o nazwie Web Storage, kt├│ra kiedy┼Ť wchodzi┼éa w sk┼éad specyfikacji HTML5, ale zosta┼éa z niej wydzielona z ma┼éo ciekawych politycznych powod├│w. Niekt├│rzy producenci przegl─ůdarek nazywaj─ů to te┼╝ „Magazynem lokalnym” (Local Storage) i „Magazynem DOM” (DOMStorage). Kwesti─Ö nazw dodatkowo jeszcze komplikuj─ů pojawiaj─ůce si─Ö powi─ůzane i podobnie nazwane nowe standardy, o kt├│rych pisz─Ö w dalszej cz─Ö┼Ťci rozdzia┼éu.

Czym jest Magazyn HTML5? M├│wi─ůc najpro┼Ťciej jest to technika umo┼╝liwiaj─ůca stronom internetowym przechowywanie nazwanych par klucz-warto┼Ť─ç w przegl─ůdarce internetowej. Dane te, podobnie jak cookie, nie s─ů usuwane po przej┼Ťciu przez u┼╝ytkownika na inn─ů stron─Ö, zamkni─Öciu karty, wy┼é─ůczeniu przegl─ůdarki itd. Jednak w odr├│┼╝nieniu od cookie, informacje te nie s─ů wysy┼éane na serwer (chyba, ┼╝e zdecydujemy sie to zrobi─ç r─Öcznie). W odr├│┼╝nieniu od wcze┼Ťniejszych pr├│b dostarczenia trwa┼éego lokalnego magazynu, ta technologia jest standardowo zaimplementowana w przegl─ůdarkach, dzi─Öki czemu mo┼╝na z niej korzysta─ç bez instalowania jakichkolwiek wtyczek.

Kt├│re przegl─ůdarki j─ů obs┼éuguj─ů? Magazyn HTML5 obs┼éuguj─ů najnowsze wersje praktycznie wszystkich przegl─ůdarek… Nawet Internet Explorera!

Obsługa Magazynu HTML5
IEFirefoxSafariChromeOperaiPhoneAndroid
8.0+3.5+4.0+4.0+10.5+2.0+2.0+

Z poziomu JavaScriptu dost─Öp do Magazynu HTML5 mo┼╝na uzyska─ç poprzez obiekt localStorage globalnego obiektu window. Zanim si─Ö go u┼╝yje, nale┼╝y sprawdzi─ç czy jest obs┼éugiwany przez przegl─ůdark─Ö.

Sprawdzanie obsługi Magazynu HTML5

function supports_html5_storage() {
  try {
    return 'localStorage' in window && window['localStorage'] !== null;
  } catch (e) {
    return false;
  }
}

Zamiast pisać tę funkcję własnoręcznie, do sprawdzania obsługi Magazynu HTML5 można użyć biblioteki Modernizr.

if (Modernizr.localstorage) {
  // w┼éasno┼Ť─ç window.localStorage jest dost─Öpna!
} else {
  // brak standardowej obsługi Magazynu HTML5 :(
  // mo┼╝esz spr├│bowa─ç u┼╝y─ç dojox.storage albo innego zewn─Ötrznego rozwi─ůzania
}

U┼╝ywanie Magazynu HTML5

Obs┼éuga Magazynu HTML5 polega na zapisywaniu i odczytywaniu nazwanych par klucz-warto┼Ť─ç. Dane zapisuje si─Ö pod kluczem o okre┼Ťlonej nazwie, a potem mo┼╝na je z powrotem pobra─ç przy u┼╝yciu tego klucza. Nazwa klucza jest ┼éa┼äcuchem. Dane mog─ů by─ç dowolnego typu JavaScript, czyli mog─ů to by─ç ┼éa┼äcuchy, warto┼Ťci logiczne, liczby ca┼ékowite i liczby zmiennoprzecinkowe. Chocia┼╝ w istocie i tak informacje s─ů zapisywane jako ┼éa┼äcuchy. Je┼Ťli chcesz zapisa─ç co┼Ť innego ni┼╝ ┼éa┼äcuch znak├│w, musisz u┼╝y─ç funkcji typu parseInt() lub parseFloat(), aby zamieni─ç pobrane dane na odpowiedni typ danych JavaScript.

interface Storage {
  getter any getItem(in DOMString key);
  setter creator void setItem(in DOMString key, in any data);
};

Wywo┼éanie funkcji setItem() z ju┼╝ istniej─ůc─ů nazw─ů klucza spowoduje nadpisanie jego aktualnej warto┼Ťci bez ostrze┼╝enia. Wywo┼éanie funkcji getItem() z nieistniej─ůcym kluczem spowoduje zwr├│cenie null, a nie zg┼éoszenie wyj─ůtku.

Obiekt localStorage, jak wszystkie obiekty w JavaScript, mo┼╝na traktowa─ç jako tablic─Ö asocjacyjn─ů. Dlatego zamiast metod getItem() i setItem() mo┼╝na te┼╝ u┼╝ywa─ç kwadratowych nawias├│w. Przyk┼éadowo poni┼╝szy kod

var foo = localStorage.getItem("bar");
// ...
localStorage.setItem("bar", foo);

…mo┼╝na nast─Öpuj─ůco zapisa─ç przy u┼╝yciu nawias├│w kwadratowych:

var foo = localStorage["bar"];
// ...
localStorage["bar"] = foo;

Istniej─ů te┼╝ metody do usuwania warto┼Ťci wybranych kluczy i kasowania zawarto┼Ťci ca┼éego magazynu (tzn. usuwania wszystkich kluczy i warto┼Ťci na raz).

interface Storage {
  deleter void removeItem(in DOMString key);
  void clear();
};

Wywo┼éanie metody removeItem() z nieistniej─ůcym kluczem nie wywo┼éa ┼╝adnego efektu.

Istnieje te┼╝ w┼éasno┼Ť─ç pozwalaj─ůca sprawdzi─ç liczb─Ö warto┼Ťci w magazynie oraz iteracyjne przejrzenie wszystkich kluczy wg indeks├│w (w celu pobrania ich nazw).

interface Storage {
  readonly attribute unsigned long length;
  getter DOMString key(in unsigned long index);
};

Je┼Ťli wywo┼éa si─Ö metod─Ö key() z indeksem o warto┼Ťci nie nale┼╝─ůcej do przedzia┼éu 0–(length-1), to zwr├│ci ona null.

Śledzenie zmian w Magazynie HTML5

Zmiany w magazynie mo┼╝na ┼Ťledzi─ç wykorzystuj─ůc zdarzenie storage. Zdarzenie to jest wyzwalane na obiekcie window, gdy wywo┼éana zostaje metoda setItem(), removeItem() lub clear() i efektem tego wywo┼éania jest jaka┼Ť zmiana. Przyk┼éadowo, je┼Ťli ustawimy element na tak─ů sam─ů warto┼Ť─ç, jak─ů mia┼é albo wywo┼éamy metod─Ö clear(), gdy nie ma ┼╝adnych kluczy, zdarzenie storage nie zostanie wyzwolone, poniewa┼╝ nic si─Ö nie zmieni w magazynie.

Zdarzenie storage jest obs┼éugiwane wsz─Ödzie tam, gdzie jest obs┼éugiwany obiekt localStorage, a wi─Öc tak┼╝e w przegl─ůdarce Internet Explorer 8. IE 8 nie obs┼éuguje standardu W3C addEventListener (chocia┼╝ w IE 9 to naprawiono). W zwi─ůzku z tym, aby skorzysta─ç ze zdarzenia storage, trzeba sprawdzi─ç kt├│ry mechanizm zdarzeniowy jest obs┼éugiwany przez przegl─ůdark─Ö. (Je┼Ťli robi┼ée┼Ť to ju┼╝ z innymi zdarzeniami, to mo┼╝esz pomin─ů─ç dalsz─ů cz─Ö┼Ť─ç tego podrozdzia┼éu.) Zdarzenie storage przechwytuje si─Ö tak samo, jak ka┼╝de inne. Je┼Ťli do rejestracji zdarze┼ä wolisz u┼╝ywa─ç jQuery albo jakiej┼Ť innej biblioteki JavaScript, to nie ma problemu.)

if (window.addEventListener) {
  window.addEventListener("storage", handle_storage, false);
} else {
  window.attachEvent("onstorage", handle_storage);
};

Funkcja zwrotna handle_storage zostanie wywo┼éana z obiektem StorageEvent we wszystkich przegl─ůdarkach opr├│cz Internet Explorera, w kt├│rym obiekt zdarzenia jest przechowywany w window.event.

function handle_storage(e) {
  if (!e) { e = window.event; }
}

Teraz zmienna e b─Ödzie reprezentowa┼éa obiekt StorageEvent maj─ůcy opisane poni┼╝ej w┼éasno┼Ťci.

Obiekt StorageEvent
W┼éasno┼Ť─çTypOpis
keystringnazwany klucz, który został dodany, usunięty lub zmieniony
oldValuedowolnypoprzednia warto┼Ť─ç (teraz nadpisana) lub null, je┼Ťli zosta┼é dodany nowy element
newValuedowolnynowa warto┼Ť─ç albo null, je┼Ťli element zosta┼é usuni─Öty
url*stringstrona, która wywołała metodę, która z kolei spowodowała tę zmianę
* Uwaga: w┼éasno┼Ť─ç url pocz─ůtkowo nazywa┼éa si─Ö uri. Niekt├│re przegl─ůdarki obs┼éugiwa┼éy j─ů jeszcze przed t─ů zmian─ů. Dlatego powinno si─Ö sprawdza─ç czy istnieje w┼éasno┼Ť─ç url, a je┼Ťli nie, to r├│wnie┼╝ czy istnieje w┼éasno┼Ť─ç uri.

Zdarzenia storage nie mo┼╝na anulowa─ç. W funkcji zwrotnej handle_storage nie da si─Ö zapobiec zmianie. Zdarzenie to jest po prostu informacj─ů od przegl─ůdarki, ┼╝e co┼Ť si─Ö wydarzy┼éo. Nie mo┼╝na temu zapobiec, bo to ju┼╝ si─Ö dokona┼éo.

Ograniczenia aktualnych przegl─ůdarek

W cz─Ö┼Ťci po┼Ťwi─Öconej historii lokalnych magazyn├│w opartych na zewn─Ötrznych wtyczkach wspomnia┼éem, ┼╝e ka┼╝da z tych technologii ma jakie┼Ť ograniczenia, np. dotycz─ůce ilo┼Ťci dost─Öpnego miejsca. W┼éa┼Ťnie sobie przypomnia┼éem, ┼╝e nie napisa┼éem jeszcze nic o ograniczeniach standardowego ju┼╝ Magazynu HTML5. Najpierw udziel─Ö odpowiedzi na pytania, a potem podam szczeg├│┼éowe wyja┼Ťnienia. Odpowiedzi w kolejno┼Ťci od najwa┼╝niejszej brzmi─ů „5 megabajt├│w”, „QUOTA_EXCEEDED_ERR” oraz „nie”.

„5 megabajt├│w” to ilo┼Ť─ç miejsca, jak─ů domy┼Ťlnie otrzymuje ka┼╝de ┼║r├│d┼éo. Mimo ┼╝e w specyfikacji HTML5 warto┼Ť─ç ta jest tylko zaleceniem, r├│┼╝ne przegl─ůdarki zaskakuj─ůco ┼Ťci┼Ťle si─Ö go trzymaj─ů. Nale┼╝y pami─Öta─ç, ┼╝e dane s─ů przechowywane w formacie ┼éa┼äcuchowym,a nie swoim oryginalnym. Je┼Ťli zapisuje si─Ö du┼╝o liczb, r├│┼╝nica w sposobie reprezentacji mo┼╝e mie─ç znaczenie. Ka┼╝da cyfra liczby zmiennoprzecinkowej jest osobnym znakiem, co jest r├│┼╝ne od standardowej reprezentacji liczb zmiennoprzecinkowych.

QUOTA_EXCEEDED_ERR” to wyj─ůtek zg┼éaszany po przekroczeniu limitu 5 megabajt├│w. „Nie” to odpowied┼║ na automatycznie nasuwaj─ůce si─Ö pytanie: „Czy mo┼╝na poprosi─ç u┼╝ytkownika o udost─Öpnienie wi─Ökszej ilo┼Ťci miejsca?” W czasie pisania tego tekstu (luty 2011 r.) ┼╝adna przegl─ůdarka nie pozwala na poproszenie o dodatkow─ů przestrze┼ä. W niekt├│rych przegl─ůdarkach (np. Operze) u┼╝ytkownik mo┼╝e ustawia─ç ilo┼Ť─ç pami─Öci dost─Öpnej dla ka┼╝dej witryny, ale inicjatywa musi wyj┼Ť─ç od u┼╝ytkownika i nie da si─Ö w ┼╝aden spos├│b po┼é─ůczy─ç tego z aplikacj─ů sieciow─ů.

Magazyn HTML5 w akcji

Zobaczmy, jak mo┼╝na wykorzysta─ç Magazyn HTML5. Przypomnij sobie gr─Ö Halma z rozdzia┼éu o elemencie canvas. Ma ona jedn─ů wad─Ö: je┼Ťli zamkniemy przegl─ůdark─Ö w trakcie gry, wszystko stracimy. Dzi─Öki Magazynowi HTML5 mo┼╝na zapisa─ç potrzebne informacje w przegl─ůdarce, aby po jej ponownym uruchomieniu odtworzy─ç poprzedni stan gry. Tutaj znajduje si─Ö dzia┼éaj─ůca demonstracja. Wykonaj kilka ruch├│w, zamknij kart─Ö, a potem otw├│rz j─ů z powrotem. Je┼Ťli twoja przegl─ůdarka obs┼éuguje Magazyn HTML5, na stronie powinien zosta─ç zapami─Ötany i odtworzony stan gry, w┼é─ůcznie z liczb─ů wykonanych ruch├│w, pozycjami pionk├│w oraz tym, kt├│ry z nich jest aktualnie zaznaczony.

Jak to dzia┼éa? Za ka┼╝dym razem, gdy w grze co┼Ť si─Ö zmienia, wywo┼éywana jest poni┼╝sza funkcja:

function saveGameState() {
    if (!supportsLocalStorage()) { return false; }
    localStorage["halma.game.in.progress"] = gGameInProgress;
    for (var i = 0; i < kNumPieces; i++) {
	localStorage["halma.piece." + i + ".row"] = gPieces[i].row;
	localStorage["halma.piece." + i + ".column"] = gPieces[i].column;
    }
    localStorage["halma.selectedpiece"] = gSelectedPieceIndex;
    localStorage["halma.selectedpiecehasmoved"] = gSelectedPieceHasMoved;
    localStorage["halma.movecount"] = gMoveCount;
    return true;
}

Jak wida─ç u┼╝yty jest w niej obiekt localStorage do zapisywania czy trwa w┼éa┼Ťnie jaka┼Ť rozgrywka (warto┼Ť─ç logiczna gGameInProgress). Je┼Ťli tak, nast─Öpuje iteracja przez pionki (gPieces — tablica JavaScript) i zapisanie numeru wiersza i kolumny ka┼╝dego pionka. Nast─Öpnie zapisywane s─ů dodatkowe informacje o stanie gry, np. to kt├│ry pionek jest wybrany (gSelectedPieceIndex — liczba ca┼ékowita), czy pionek ten znajduje si─Ö w ┼Ťrodku potencjalnie d┼éugiej serii skok├│w (gSelectedPieceHasMoved — warto┼Ť─ç logiczna) oraz jak─ů liczb─Ö ruch├│w wykonano do tej pory (gMoveCount — liczba ca┼ékowita).

Podczas ┼éadowania strony zamiast funkcji newGame() ustawiaj─ůcej zmienne na domy┼Ťlne warto┼Ťci wywo┼éujemy funkcj─Ö resumeGame(). Funkcja resumeGame() sprawdza czy stan gry jest przechowywany lokalnie. Je┼Ťli tak, przywraca zapisane warto┼Ťci przy u┼╝yciu obiektu localStorage.

function resumeGame() {
    if (!supportsLocalStorage()) { return false; }
    gGameInProgress = (localStorage["halma.game.in.progress"] == "true");
    if (!gGameInProgress) { return false; }
    gPieces = new Array(kNumPieces);
    for (var i = 0; i < kNumPieces; i++) {
	var row = parseInt(localStorage["halma.piece." + i + ".row"]);
	var column = parseInt(localStorage["halma.piece." + i + ".column"]);
	gPieces[i] = new Cell(row, column);
    }
    gNumPieces = kNumPieces;
    gSelectedPieceIndex = parseInt(localStorage["halma.selectedpiece"]);
    gSelectedPieceHasMoved = localStorage["halma.selectedpiecehasmoved"] == "true";
    gMoveCount = parseInt(localStorage["halma.movecount"]);
    drawBoard();
    return true;
}

Najwa┼╝niejsz─ů cz─Ö┼Ťci─ů tej funkcji jest pu┼éapka, o kt├│rej wspomina┼éem wcze┼Ťniej, a kt├│r─ů powtarzam jeszcze raz: Dane s─ů przechowywane jako ┼éa┼äcuchy. Je┼Ťli zapiszesz co┼Ť innego ni┼╝ ┼éa┼äcuch, musisz to przekonwertowa─ç na w┼éa┼Ťciwy typ. Przyk┼éadowo znacznik okre┼Ťlaj─ůcy czy trwa w┼éa┼Ťnie jaka┼Ť rozgrywka (gGameInProgress) jest typu logicznego. W funkcji saveGameState() po prostu go zapisali┼Ťmy i w og├│le nie przejmowali┼Ťmy si─Ö typem:

localStorage["halma.game.in.progress"] = gGameInProgress;

Ale w funkcji resumeGame() musimy pobran─ů warto┼Ť─ç traktowa─ç jako zwyk┼éy ┼éa┼äcuch, kt├│ry trzeba przekszta┼éci─ç w logiczny typ danych:

gGameInProgress = (localStorage["halma.game.in.progress"] == "true");

Analogicznie liczba ruch├│w jest zapisywana w gMoveCount jako liczba ca┼ékowita. W funkcji saveGameState() zapisali┼Ťmy j─ů tak:

localStorage["halma.movecount"] = gMoveCount;

Ale w funkcji resumeGame() musimy pobran─ů warto┼Ť─ç przekonwertowa─ç na typ ca┼ékowitoliczbowy za pomoc─ů funkcji JavaScript parseInt():

gMoveCount = parseInt(localStorage["halma.movecount"]);

┼Üwiat poza parami klucz-warto┼Ť─ç — konkurencyjne koncepcje

Podczas gdy w przesz┼éo┼Ťci powstawa┼éo wiele niezbyt udanych sztuczek i hack├│w, obecny stan Magazynu HTML5 jest zaskakuj─ůco dobry. Nowe API zosta┼éo ustandaryzowane i zaimplementowane we wszystkich najwa┼╝niejszych przegl─ůdarkach, platformach i urz─ůdzeniach. B─Öd─ůc programist─ů sieciowym niecz─Östo jeste┼Ť ┼Ťwiadkiem takich wydarze┼ä, prawda? Ale na „5 megabajtach par klucz-warto┼Ť─ç” ┼Ťwiat si─Ö nie ko┼äczy i w przysz┼éo┼Ťci… jak by to powiedzie─ç… ju┼╝ powstaj─ů konkurencyjne koncepcje.

Jedna z nich nosi nazw─Ö, kt├│r─ů na pewno znasz: SQL. W 2007 r. Google uruchomi┼éo Gears, otwart─ů wtyczk─Ö rozszerzaj─ůc─ů mo┼╝liwo┼Ťci r├│┼╝nych przegl─ůdarek internetowych zawieraj─ůc─ů m.in. baz─Ö danych SQLite. Ten wczesny prototyp mia┼é wp┼éyw na kszta┼ét specyfikacji Web SQL Database. Web SQL Database (wcze┼Ťniej zwana WebDB) to cienkie opakowanie dla bazy danych SQL umo┼╝liwiaj─ůce wykonywanie z poziomu JavaScriptu takich dzia┼éa┼ä, jak to:

Kod dzia┼éaj─ůcy w czterech przegl─ůdarkach

openDatabase('documents', '1.0', 'Local document storage', 5*1024*1024, function (db) {
  db.changeVersion('', '1.0', function (t) {
    t.executeSql('CREATE TABLE docids (id, name)');
  }, error);
});

Jak wida─ç, wi─Ökszo┼Ť─ç dzia┼éa┼ä odbywa si─Ö w ┼éa┼äcuchu przekazywanym do metody executeSql. Ła┼äcuch mo┼╝e zawiera─ç dowoln─ů obs┼éugiwan─ů instrukcj─Ö SQL, wliczaj─ůc SELECT, UPDATE, INSERT oraz DELETE. Wygl─ůda to jak zwyk┼ée pos┼éugiwanie sie baz─ů danych, tylko ┼╝e z poziomu JavaScriptu! A niech mnie!

Specyfikacja Web SQL Database jest zaimplementowana w czterech przegl─ůdarkach i platformach.

Obsługa Web SQL Database
IEFirefoxSafariChromeOperaiPhoneAndroid
··4.0+4.0+10.5+3.0+2.0+

Oczywi┼Ťcie ka┼╝dy kto u┼╝ywa┼é wi─Öcej ni┼╝ jednej bazy danych wie, ┼╝e SQL to bardziej marketingowe okre┼Ťlenie ni┼╝ konkretny standard. (Niekt├│rzy to samo m├│wi─ů o „HTML5”, ale niewa┼╝ne.) Oczywi┼Ťcie istnieje specyfikacja SQL (nosi nazw─Ö SQL-92), ale nie ma na ┼Ťwiecie serwera baz danych, kt├│ry by by┼é w pe┼éni zgodny z ni─ů i tylko z ni─ů. Istniej─ů zatem SQL Oracle, SQL Microsoftu, SQL MySQL, SQL PostgreSQL i SQL SQLite. W ka┼╝dym z tych produkt├│w ci─ůgle wprowadzane s─ů nowe udoskonalenia SQL, przez co nawet stwierdzenie „SQL SQLite” nie jest ca┼ékiem precyzyjne. Dla pewno┼Ťci najlepiej jest m├│wi─ç „wersja SQL dost─Öpna w SQLite w wersji X.Y.Z”.

To wszystko doprowadzi┼éo, ┼╝e na pocz─ůtku specyfikacji Web SQL Database umieszczono nast─Öpuj─ůc─ů informacj─Ö:

Niniejsza specyfikacja znalaz┼éa si─Ö w impasie: wszyscy zainteresowani implementatorzy u┼╝ywaj─ů tego samego silnika SQL (Sqlite), ale do kontynuacji procesu standaryzacji potrzebnych jest wi─Öcej niezale┼╝nych implementacji. ZDANIE DO USUNI─śCIA

Na tle tych wydarze┼ä zarysowuje si─Ö kolejna wizja utworzenia zaawansowanych trwa┼éych lokalnych magazyn├│w dla aplikacji sieciowych: API Indexed Database, wcze┼Ťniej zwane „WebSimpleDB”, a teraz wyst─Öpuj─ůce pod nazw─ů „IndexedDB”.

API Indexed Database udost─Öpnia tzw. magazyn obiektowy. Magazyn ten jest podobny pod wieloma wzgl─Ödami do bazy danych SQL. Wyst─Öpuj─ů w nim „bazy danych” z „rekordami”, z kt├│rych ka┼╝dy ma ustalon─ů liczb─Ö „p├│l”. Ka┼╝de pole ma okre┼Ťlony typ danych definiowany podczas tworzenia bazy danych. Mo┼╝na wybra─ç zbi├│r rekord├│w i przejrze─ç je przy u┼╝yciu „kursora”. Zmiany w magazynie obiektowym s─ů obs┼éugiwane przy u┼╝yciu „transakcji”.

Dla ka┼╝dego, kto u┼╝ywa┼é baz danych SQL wymienione poj─Öcia brzmi─ů znajomo. Najwa┼╝niejsza r├│┼╝nica w stosunku do baz SQL polega na tym, ┼╝e magazyn obiektowy nie ma strukturalnego j─Özyka. Nie tworzy si─Ö instrukcji typu "SELECT * from USERS where ACTIVE = 'Y'". Zamiast tego u┼╝ywa si─Ö udost─Öpnianych przez magazyn metod do otwierania na bazie danych kursora o nazwie USERS, przegl─ůda si─Ö rekordy, odfiltrowuje si─Ö rekordy nieaktywnych u┼╝ytkownik├│w oraz pobiera si─Ö warto┼Ťci z pozosta┼éych p├│l za pomoc─ů metod dost─Öpowych. Dobrym wprowadzaniem do IndexDB jest artyku┼é An early walk-through of IndexedDB. Znajdziesz w nim por├│wnanie IndexedDB Web SQL Database.

W czasie pisania tego tekstu IndexedDB obs┼éugiwa┼éa tylko wersja beta Firefoksa 4. (Dla kontrastu Mozilla zapowiedzia┼éa, ┼╝e nigdy nie zaimplementuje Web SQL Database.) Google twierdzi, ┼╝e rozwa┼╝a implementacj─Ö obs┼éugi IndexedDB w Chromium i Google Chrome. Nawet Microsoft twierdzi, ┼╝e IndexedDB „jest znakomitym rozwi─ůzaniem dla sieci”.

A co ty, programista sieciowy, mo┼╝esz zrobi─ç z IndexedDB? Chwilowo nic opr├│cz opracowania demonstracji zastosowania tej technologii. Za rok? Mo┼╝e co┼Ť si─Ö zmieni. Poni┼╝ej znajdziesz list─Ö odno┼Ťnik├│w do ciekawych artyku┼é├│w na ten temat.

Lektura uzupe┼éniaj─ůca

Magazyn HTML5:

Wczesne prace Brada Neuberga i in. (przed powstaniem HTML5):

Web SQL Database:

IndexedDB:

Autor: Mark Pilgrim

Źródło: http://diveintohtml5.info/

Tłumaczenie: Łukasz Piwko

Tre┼Ť─ç tej strony jest dost─Öpna na zasadach licencji CC BY 3.0