Real-World off-line datalagring

I många projekt kommer det en tid när du måste lagra vissa data offline. Det kan vara ett krav eller bara en förbättring för dina användare, men du måste bestämma vilka av de tillgängliga lagringsalternativ du ska använda i din ansökan. Den här artikeln hjälper dig att välja det bästa för din app.


Introduktion

HTML5 introducerade några offline lagringsalternativ. AppCache, localStorage, sessionStorage och IndexedDB. Var och en av dem är lämplig för en specifik användning. AppCache kan till exempel öka din applikation eller låta vissa delar av det fungera utan en Internetanslutning. Nedan kommer jag att beskriva alla dessa alternativ och visa några kodsedlar med exempelanvändning.


AppCache

Om en del av din ansökan (eller hela appen) kan användas utan åtkomst till servern kan du använda AppCache för att göra det möjligt för dina användare att göra några saker offline. Allt du behöver göra är att skapa en manifestfil där du skulle ange vad som ska cachas och vad som inte borde vara. Du kan också ange ersättningar för de filer som kräver online-åtkomst.

Ett AppCache-manifest är bara en textfil med a .AppCache (rekommenderad) förlängning. Det börjar med CACHE MANIFEST och är uppdelad i tre delar:

  • GÖMSTÄLLE - filer du anger här kommer att hämtas och cachas första gången användaren når din webbplats
  • NÄTVERK - Här listar du de filer som kräver en Internetanslutning för att fungera korrekt, de kommer aldrig att cachas
  • RETIRERA - Dessa filer kommer att användas när en online-resurs öppnas utan en anslutning

Exempel

Först måste du definiera manifestfilen på din sida:

  ... 

Du måste komma ihåg att manifestfilen måste serveras med en text / cache-manifest MIME-typ, annars kommer det inte att analyseras av webbläsaren. Därefter måste du skapa filen du definierade tidigare. För det här exemplet, låt oss föreställa oss att ni har en informationswebbplats med möjlighet att kontakta dig och skriva kommentarer. Du kan låta användare komma åt de statiska delarna av webbplatsen och ersätta kontaktformuläret och kommentarer med annan information så att formuläret och kommentarerna är otillgängliga medan offline.

Låt oss först definiera lite statiskt innehåll:

 CACHE MANIFEST CACHE: /om.html /portfolio.html /portfolio_gallery/image_1.jpg /portfolio_gallery/image_2.jpg /info.html /style.css /main.js /jquery.min.js

Sidanotag: En dålig sak om manifestet är att du inte kan använda ett jokertecken för att ange att en hel mapp ska cachelagras. Du kan bara använda ett jokertecken under NÄTVERKS-sektionen för att indikera att alla resurser inte listade i manifestet ska inte cachas.

Du behöver inte cache den sida där manifestet är definierat, det kommer att cachas automatiskt. Nu kommer vi att definiera fallbacks för kontakt och kommentarer sektioner:

 FALLBACK: /contact.html /offline.html /comments.html /offline.html

Slutligen kan vi använda en * för att stoppa alla andra resurser från att cachas:

 Nätverk: *

Slutresultatet ska se ut så här:

 CACHE MANIFEST CACHE: /about.html /portfolio.html /portfolio_gallery/image_1.jpg /portfolio_gallery/image_2.jpg /info.html /style.css /main.js/jquery.min.js FALLBACK: /contact.html / offline .html /comments.html /offline.html NÄTVERK: *

En viktig sak att komma ihåg är att dina resurser endast kommer att cachas en gång. De kommer inte att bli cachade när du uppdaterar dem, bara när du ändrar manifestet. En bra metod är att skriva in en kommentar med ett versionsnummer och öka det varje gång du uppdaterar filen:

 CACHE MANIFEST # version 1 CACHE: ... 

LocalStorage & SessionStorage

Dessa två lagringsalternativ kommer att vara användbara om du vill behålla något i din JavaScript-kod. Den första låter dig spara ett värde utan ett utgångsdatum. Detta värde kommer att vara tillgängligt för alla sidor med samma domän och protokoll. Du kanske till exempel vill spara användarens programinställningar på sin dator så att han eller hon kan anpassa dem till den dator de använder för närvarande. Den andra kommer att hålla värdena tills användaren stänger webbläsarfönstret (eller fliken). Dessutom delas data inte mellan windows, även om användaren öppnar några sidor av din ansökan.

Något som är värt att komma ihåg är att du bara kan lagra grundläggande typer i lokalt utrymme/sessionStorage. Så bara strängar och siffror kommer att fungera. Allt annat kommer att lagras med hjälp av det att stränga() metod. Om du behöver spara ett objekt borde du göra det med JSON.stringify (om det här objektet är en klass kan du bara åsidosätta standardvärdet att stränga() metod för att göra det för dig automatiskt).

Exempel

Låt oss överväga föregående exempel. I kommentarerna och kontaktdelarna på webbplatsen kan vi spara vad användaren skrev in, så om han / hon stänger fönstret av misstag kommer värdena att finnas kvar för honom / henne att fortsätta senare. Detta kommer att vara en riktigt enkel kod med jQuery (eftersom vi kommer att använda ett fältets ID för att identifiera det senare måste var och en av formulärfälten ha ett id-attribut)

 $ ('# comments-input, .contact-field'). på ('keyup', funktion () // låt oss kontrollera om localStorage stöds om (window.localStorage) localStorage.setItem ($ (this) ('id'), $ (detta) .val ()););

När kommentaren / kontaktformuläret skickas måste vi rensa värdet. Låt oss göra detta genom att hantera en inlämnings händelse (här är det mest grundläggande exemplet):

 $ ('# comments-form, # contact-form'). på ("skicka", funktion () // få alla de fält vi sparade $ ('# comments-input, .contact-field'). (funktion () // få fältets id och ta bort det från lokal lagring localStorage.removeItem ($ (this) .attr ('id'));););

Och till sist, på sidan laddas, kommer vi att återställa värdena:

 // få alla fält vi sparade $ ('# comments-input, .contact-field'). varje (funktion () // få fältets id och få det värde från lokal lagring var val = localStorage.getItem ($ (detta) .attr ('id')); // om värdet existerar, sätt det om (val) $ (detta) .val (val););

IndexedDB

Detta är det mest intressanta lagringsalternativet enligt min mening. Det låter dig lagra ganska stora mängder indexerad data i användarens webbläsare. På det här sättet kan du spara komplexa objekt, stora dokument etc. och få din användare åtkomst till dem utan en Internetanslutning. Den här funktionen är användbar för alla typer av applikationer. Om du skapar en e-postklient kan du spara användarens e-postmeddelanden så att han eller hon kan komma åt dem senare, ett fotoalbum kan spara foton för off-line användning eller GPS-navigering kan spara en viss väg och listan fortsätter.

IndexedDB är en objektorienterad databas. Det betyder att det inte finns några tabeller och ingen SQL. Du lagrar nyckelvärdespar av data, där tangenter är strängar, siffror, datum eller arrays och värden kan vara komplexa objekt. Databasen själv består av butiker. En butik liknar ett bord i en relationsdatabas. Varje värde måste ha sin egen nyckel. En nyckel kan genereras automatiskt, du kan ange den när du lägger till värdet, eller det kan vara något fält i värdet (vilket också kan genereras automatiskt). Om du väljer att använda ett fält som en nyckel, kommer du bara att kunna lägga till JavaScript-objekt i affären (eftersom enkla nummer eller strängar inte kan ha några egenskaper som objekt kan).

Exempel

För det här exemplet, låt oss föreställa oss att vi har ett musikalbum. Nu ska jag inte täcka uppbyggnaden av hela musikalbum-appen här. Jag kommer bara att täcka IndexedDB-delen av appen, men själva appen för musikalbum ingår i den här artikeln för att du ska ladda ner, så du kan titta på hela källkoden där. Först måste vi öppna databasen och skapa butiken:

 // kontrollera om indexedDB stöds om (! window.indexedDB) throw 'IndexedDB stöds inte!'; // naturligtvis ersätta det med någon användarvänlig meddelande // variabel som håller databasanslutningen var db; // öppna databasen // första argumentet är databasens namn, det andra är det version (jag kommer att prata om versioner på ett tag) var request = indexedDB.open ('album', 1); request.onerror = function (e) console.log (e); ; // detta kommer att eldas när versionen av databasen ändras request.onupgradeneeded = function (e) // e.target.result håller anslutningen till databasen db = e.target.result; // skapa en butik för att hålla data // första argumentet är butikens namn, andra är för alternativ // här anger vi fältet som ska fungera som nyckeln och möjliggör också automatisk generering av nycklar med autoIncrement var objectStore = db. createObjectStore ("cd", keyPath: 'id', autoIncrement: true); // skapa ett index för att söka cd-skivor efter titel // första argumentet är indexets namn, andra är fältet i värdet // i det sista argumentet vi anger andra alternativ, här anges bara att indexet är unikt, eftersom det kan Var bara ett album med specifikt titelobjektStore.createIndex ("title", "title", unique: true); // skapa ett index för att söka cd-skivor med band // den här är inte unik, eftersom ett band kan ha flera album objectStore.createIndex ("band", "band", unique: false); ;

Ovanstående kod är ganska enkel. Du har noga märkt versionen och onupgradeneeded händelse. Denna händelse avfyras när databasen öppnas med en ny version. Eftersom databasen inte existerade, brinner händelsen och vi kan skapa den butik vi behöver. Senare lägger vi till två index, en för att söka efter titel och en för att söka efter band. Nu får vi se processen att lägga till och ta bort album:

 // (lägg till $ ('# add-album'). På ('klicka', funktion () // skapa transaktionen // första argumentet är en lista över butiker som ska användas, andra specificerar flaggan // eftersom vi vill lägga till något vi behöver skrivåtkomst, så vi använder readwrite flagga transaction = db.transaction (['cds'], 'readwrite'); transaction.onerror = function (e) console.log (e);; var värde = ...; // läs från DOM // lägg albumet till butiken var request = transaction.objectStore ('cd'). lägg till (värde); request.onsuccess = function (e) // lägg till album till användargränssnittet, e.target.result är en nyckel till objektet som läggs till;); // (borttagning av $ ('remove-album'). På ('klick', funktion () var transaktion = db.transaction (['cds'], 'readwrite'); var request = transaction.objectStore ') .delete (/ * någon id har fått från DOM, konverterad till heltal * /); request.onsuccess = function () // ta bort albumet från användargränssnitt);

Ganska okomplicerat. Du måste komma ihåg att alla operationer i databasen är baserade på transaktioner för att bevara datakonsistensen. Nu är det enda som finns kvar att visa albumen:

 request.onsuccess = funktion (e) if (! db) db = e.target.result; var transaktion = db.transaction (['cd'er']); // ingen flagga eftersom vi bara läser var butik = transaction.objectStore ('cds'); // öppna en markör, som kommer att få alla objekt från databasbutiken.openCursor (). onsuccess = funktion (e) var markör = e.target.result; om (markör) var value = cursor.value; $ ('# albums-list tbody'). Lägg till (''+ value.title +''+ värde.band +''+ value.genre +''+ värde.år +''); // flytta till nästa objekt i markören cursor.continue (); ; 

Detta är inte heller väldigt komplicerat. Som du kan se, använder IndexedDB du enkelt att lagra komplexa värden. Du kan också söka efter värden enligt index, så här:

 funktion getAlbumByBand (band) var transaktion = db.transaction (['cd'er']); var butik = transaction.objectStore ('cds'); var index = store.index ("band"); // öppna en markör för att bara få album med specificerat band // märka det argument som passerat till openCursor () index.openCursor (IDBKeyRange.only (band)). onsuccess = function (e) var cursor = e.target.result; om (markör) // gör albumet // flytta till nästa objekt i markören cursor.continue (); ); 

Du kan använda markören med indexet precis som hur vi gjorde med affären. Eftersom det kan finnas några poster med samma indexvärde (om det inte är unikt) måste vi använda IDBKeyRange. Detta kommer att filtrera resultaten beroende på vilken funktion du använder. Här vill vi bara få varor av det medföljande bandet, så vi använde endast() metod. Du kan också använda nedre_gräns (), övre gräns() och bunden. Metodnamnen är ganska självförklarande.


Slutsats

Därför är det inte så komplicerat att det är möjligt att använda offline-åtkomst för dina användare. Jag hoppas att efter att ha läst den här artikeln kommer du att göra dina applikationer mer användarvänliga genom att låta dem komma åt vissa delar (eller kanske till och med alla) utan internetanslutning. Du kan hämta provprogrammet och experimentera med det, lägga till fler alternativ eller inkludera vissa delar av det på din webbplats.