{"id":269,"date":"2012-05-23T09:07:15","date_gmt":"2012-05-23T07:07:15","guid":{"rendered":"https:\/\/alistapart.com\/it\/article\/application-cache-idiota\/"},"modified":"2012-05-23T09:07:15","modified_gmt":"2012-05-23T07:07:15","slug":"application-cache-idiota","status":"publish","type":"article","link":"https:\/\/alistapart.com\/it\/article\/application-cache-idiota\/","title":{"rendered":"Application Cache \u00e8 un idiota"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/alistapart.com\/it\/wp-content\/uploads\/sites\/2\/2012\/05\/n51cacheweb.png\" border=\"0\" width=\"233\" height=\"194\" align=\"left\" \/>Buongiorno! Al \u201ccastello Lanyrd\u201d abbiamo appena lanciato <a href=\"http:\/\/m.lanyrd.com\">la versione mobile del nostro sito<\/a>, che memorizza i dati degli eventi a cui assistete per vederli offline. Ho ridotto i bit offline a <a href=\"http:\/\/appcache-demo.s3-website-us-east-1.amazonaws.com\/localstorage-cache\/\">una semplice demo<\/a> e ho pubblicato <a href=\"https:\/\/github.com\/jakearchibald\/appcache-demo\">tutto il codice su Github<\/a>. Ma prima di immergerci nel codice, lasciate che vi racconti una storia vera, assolutamente vera.<\/p>\n<p>Mi trovavo ad un party, uno di quelli in cui gli ospiti si conoscono a malapena. Facevo parte di un piccolo gruppo che stava cercando, non senza disagi, di fare le presentazioni. Una donna piuttosto bella si rivolse ad uno dei membri pi\u00f9 timidi del gruppo, presentandosi come &#8220;Dev&#8221; e chiese: &#8220;E tu che lavoro fai?&#8221;<\/p>\n<p>&#8220;Oh, sono il LocalStorage.&#8221;, rispose, chiaramente a disagio. &#8220;Fornisco un&#8217;interfaccia di scripting per memorizzare del testo che viene mantenuto tra le pagine e le sessioni del browser.&#8221;<\/p>\n<p>&#8220;S\u00ec, praticamente \u00e8 uno scaffale!&#8221;, interruppe un altro. Il gruppo ridacchi\u00f2 alle spalle di LocalStorage. Io rimasi in silenzio, perch\u00e9 conoscevo abbastanza bene quel ragazzo.<\/p>\n<p>Un altro membro del gruppo si fece avanti. &#8220;Ciao, sono ApplicationCache&#8221; disse, spostandosi per stringere la mano di Dev, &#8220;Faccio diventare la tua esperienza offline da pessima a fantastica: solo un file in pi\u00f9 e voil\u00e0! Funziona. Nessuna preoccupazione, nessun bisogno di fare &#8216;scripting&#82167;&#8221;. S\u00ec, fece le virgolette con le dita mentre diceva &#8216;scripting&#8217;. A quel punto stavo stringendo i denti, perch\u00e9 sapevo che aveva ingrandito enormemente le sue capacit\u00e0 senza che gli altri lo vedessero. Tuttavia, se avessi gridato &#8220;cavolate&#8221; <em>io<\/em> avrei fatto la figura dell&#8217;idiota.<\/p>\n<p>Mi sono sentito male per non fatto nulla in quella serata completamente reale. \u00c8 doloroso vedere articoli che lodano la facilit\u00e0 d&#8217;uso di ApplicationCache, scritti da persone che l&#8217;hanno chiaramente visto superficialmente. Devo mettere subito le cose in chiaro: sono qui per dirvi che ApplicationCache \u00e8 una cretinata.<\/p>\n<p>Ora, non intendo dire che sia completamente inutile o che dovrebbe essere evitato, dovete solo essere molto cauti quando ci lavorate e nel modo in cui lo usate. Se lo usate nel modo sbagliato, la stupidit\u00e0 si diffonde tutto intorno, fino all&#8217;utente finale. Leggendo le mie esperienze traumatiche con AppCache, saprete cosa aspettarvi da AppCache e come gestirlo.<\/p>\n<div class=\"paragrafo\">\n<h2>Quando \u00e8 utile l&#8217;accesso offline?<\/h2>\n<p>Siamo meglio connessi di quanto non lo siamo mai stati, ma non siamo <em>sempre<\/em> connessi. Ad esempio, sto scrivendo questo articolo su un treno che sfreccia attraverso le pianure povere di dati del West Sussex. In alternativa, potreste scegliere di essere offline. Quando uso la connessione dati all&#8217;estero mi pare di sentire i tappi di champagne che saltano negli uffici del mio network provider. So di avere un&#8217;emorragia di soldi quando uso il roaming dati, ma internet ha dei dati di cui ho bisogno e non mi fa accedere alla maggior parte di questi senza una connessione funzionante.<\/p>\n<p>I siti utili offline si suddividono generalmente in due categorie: quelli che ti permettono di fare cose e quelli che ti permettono di cercare cose.<\/p>\n<p>I siti che permettono di &#8220;cercare cose&#8221; includono <a href=\"http:\/\/www.wikipedia.com\">Wikipedia<\/a>, <a href=\"http:\/\/youtube.com\">YouTube<\/a> e <a href=\"http:\/\/twitter.com\">Twitter<\/a>. Il grosso del carico tende ad essere sul server e c&#8217;\u00e8 una gran quantit\u00e0 di dati a disposizione degli utenti, che per\u00f2 ne usano solo una parte.<\/p>\n<p>I siti che permettono di &#8220;Fare cose&#8221; includono <a href=\"http:\/\/www.cuttherope.ie\/\">Cut the Rope<\/a>, <a href=\"http:\/\/csslint.net\/\">CSS Lint<\/a> e <a href=\"http:\/\/docs.google.com\">Google Docs<\/a>. Per questa tipologia di siti, il grosso del carico \u00e8 a lato client. Offrono una quantit\u00e0 limitata di dati, ma potete usare quei dati in molti modi o creare i vostri dati. Questo \u00e8 il caso per cui \u00e8 stato realizzato Application Cache, quindi cominceremo da qui per una semplice introduzione.<\/p>\n<\/div>\n<div class=\"paragrafo\">\n<h2>Mettere offline un sito per &#8220;fare cose&#8221;<\/h2>\n<p><a href=\"http:\/\/www.spritecow.com\">Sprite Cow<\/a> ricade nella categoria &#8220;fare cose&#8221;. Le CSS sprites sono <a href=\"http:\/\/css-tricks.com\/css-sprites\/\">ottime per le prestazioni<\/a>, ma trovare la dimensione e la posizione di un oggetto in una sprite \u00e8 piuttosto macchinoso. Sprite Cow carica lo sprite sheet e ritorna il CSS per mostrarne una particolare porzione. \u00c8 un file html, alcuni elementi e tutto il processing viene fatto a lato client: il server non fa nulla se non mandare i files.<\/p>\n<p>Sarebbe bello poter usare Sprite Cow in treno, cos\u00ec come facciamo con le app native. Per fare ci\u00f2, creiamo un manifest file che elenchi tutte gli elementi di cui ha bisogno il sito:<\/p>\n<pre><code>\nCACHE MANIFEST\n\nassets\/6\/script\/mainmin.js\nassets\/6\/style\/mainmin.css\nassets\/6\/style\/fonts\/pro.ttf\nassets\/6\/style\/imgs\/sprites1.png\n<\/code><\/pre>\n<p>&#8230; Poi colleghiamo quel manifest alla pagina html mediante un attributo:<\/p>\n<pre><code>&lt;html manifest=\"offline.appcache\"&gt;<\/code><\/pre>\n<p>La pagina HTML stessa non \u00e8 elencata nel manifest. Le pagine che si associano ad un manifest ne diventano parte.<\/p>\n<p>In pratica, se visitate <a href=\"http:\/\/www.spritecow.com\">Sprite Cow<\/a> con una connessione dati, sarete in grado di visitarlo in seguito senza una connessione.<\/p>\n<p>Il tab &#8220;resource&#8221; nel Web Inspector di Chrome vi mostrer\u00e0 i files caricati dal manifest e quali pagine vi puntano. Se avete bisogno di svuotare quelle cache, andate su <a href=\"chrome:\/\/appcache-internals\/\">chrome:\/\/appcache-internals\/<\/a>.<\/p>\n<p>Sulle prime, questo pu\u00f2 sembrare una soluzione magica al problema. Sfortunatamente, stavo mentendo quando ho detto che questa sarebbe stata una semplice introduzione. La specifica di ApplicationCache \u00e8 come una cipolla: ha molti strati e man mano che li togliete, cominciate a lacrimare.<\/p>\n<h3>Problema n.1: i files provengono sempre da ApplicationCache, anche se siete online.<\/h3>\n<p>Quando visitate Sprite Cow, ottenete subito la versione che avete in cache. Una volta che la pagina ha finito di essere caricata, il browser cercher\u00e0 degli aggiornamenti al manifest e ai file in cache.<\/p>\n<p>Tutto ci\u00f2 suona come un modo bizzarro per fare cose ma vuol dire che il browser non deve attendere che la connessione scada prima di decidere che siete offline.<\/p>\n<p>ApplicationCache attiva un evento <code>updateready<\/code> per farci sapere che c&#8217;\u00e8 del contenuto aggiornato, ma non possiamo semplicemente fare il refresh della pagina a questo punto, perch\u00e9 l&#8217;utente potrebbe aver gi\u00e0 interagito con la versione che ha gi\u00e0.<\/p>\n<p>Non si tratta di un grosso problema, dal momento che la versione vecchia probabilmente \u00e8 sufficientemente buona. Se dovesse essere necessario, possiamo mostrare un piccolo messaggio &#8220;\u00c8 disponibile un aggiornamento. Fai refresh per aggiornare&#8221;. Potreste averlo visto in alcune app di Google come Reader e Gmail.<\/p>\n<p>Oh, vi ricordate quattro paragrafi fa quando dissi che ApplicationCache cerca il contenuto aggiornato dopo aver caricato la pagina? Mentivo.<\/p>\n<h3>Problema n.2: ApplicationCache si aggiorna solo se il contenuto stesso del manifest \u00e8 cambiato.<\/h3>\n<p>HTTP ha gi\u00e0 un modello di caching. Ogni file pu\u00f2 definire come dovrebbe essere messo in cache. Anche ad un livello base, i file singoli possono dire &#8220;Non mettermi mai in cache&#8221; oppure &#8220;controlla con il server, ti dir\u00f2 se c&#8217;\u00e8 un aggiornamento&#8221; oppure &#8220;assumi che io vada bene fino al 1 Aprile 2022&#8221;.<\/p>\n<p>Tuttavia, immaginatevi di avere 50 pagine html nel vostro manifest. Ogni volta che ne visitate una di queste online, il browser dovrebbe fare 50 http request per vedere se avete bisogno di un update.<\/p>\n<p>Come workaround leggermente insolito, il browser cercher\u00e0 gli aggiornamenti solo dei file elencati nel manifest se il file del manifest stesso \u00e8 cambiato dall&#8217;ultima volta che il browser ha controllato. Va bene qualsiasi cambiamento che renda diverso il manifest anche di un solo byte.<\/p>\n<p>Questo funziona in maniera piuttosto trasparente per gli elementi statici che vengono idealmente caricati attraverso un content delivery network e che non cambiano mai. Quando il CSS\/JavaScript\/etc. cambiano, viene caricato sotto ad un differente url, il che vuol dire che il contenuto del manifest cambia. Se non avete familiarit\u00e0 con &#8220;far-future caching&#8221; e le CDN, leggete <a href=\"http:\/\/developer.yahoo.com\/performance\/rules.html#expires\">la guida con le best practices per le performance di Yahoo!<\/a>.<\/p>\n<p>Alcune risorse semplicemente non possono cambiare il proprio url in questo modo, come ad esempio le nostre pagine HTML. ApplicationCache non cercher\u00e0 aggiornamenti a questi files senza un piccolo incoraggiamento. Il modo pi\u00f9 semplice per farlo consiste nell&#8217;aggiungere un commento al proprio manifest e cambiarlo quando serve.<\/p>\n<pre><code>\nCACHE MANIFEST\n# v1\n\nwhatever.html\n<\/code><\/pre>\n<p>I commenti nel manifest cominciano con un <code>#<\/code>. Se aggiorno <code>whatever.html<\/code> dovrei cambiare il mio commento a <code># v2<\/code>, facendo cos\u00ec scattare un aggiornamento. Potrei automatizzarlo con un build script che mi mandi fuori qualcosa di simile a ETAG come un commento per ciascun file nel manifest, cos\u00ec che ciascun cambiamento nel file \u00e8 certo che cambier\u00e0 il contenuto del manifest.<\/p>\n<p>Comunque, aggiornare il testo nel manifesto non garantisce che le risorse all&#8217;interno saranno aggiornate dal server.<\/p>\n<h3>Problema #3: ApplicationCache \u00e8 una cache aggiuntiva, non alternativa.<\/h3>\n<p>Quando il browser aggiorna ApplicationCache, richiede gli url come farebbe di solito. Obbedisce alle normali istruzioni di caching: se un header di un oggetto dice &#8220;assumi che io sia valido fino al 1 Aprile 2022&#8221; il browser supporr\u00e0 che la risorsa sia davvero valida fino al 1 Aprile 2022 e non dar\u00e0 fastidio al server per un aggiornamento.<\/p>\n<p>Questa \u00e8 una buona cosa perch\u00e9 potete usarla per tagliare il numero di richieste che il browser deve fare quando cambia il manifest.<\/p>\n<p>Tutto ci\u00f2 pu\u00f2 cogliere le persone di sorpresa mentre stanno facendo esperimenti volti a capire se i propri server non mandino gli header della cache. Senza specifiche, il browser tirer\u00e0 ad indovinare sulla cache. Potete aggiornare <code>whatever.html<\/code> e il manifest, ma il browser non aggiorner\u00e0 il file perch\u00e9 &#8220;supporr\u00e0&#8221; che non abbia bisogno di aggiornamenti.<\/p>\n<p>Tutti i file che inviate dovrebbero avere degli header della cache \u00e8 questo risulta specialmente importante per tutto quello che c&#8217;\u00e8 nel vostro manifest e per il manifest stesso. Se \u00e8 molto probabile che un file venga aggiornato, dovrebbe essere inviato con <code>no-cache<\/code>. Se il file cambia poco frequentemente, \u00e8 meglio optare per <code>must-revalidate<\/code>. Ad esempio, <code>must-revalidate<\/code> \u00e8 una buona scelta per il file manifest stesso. Oh, gi\u00e0 che stiamo parlando di questo&#8230;<\/p>\n<h3>Problema n.4: mai mai fare &#8220;far-future cache&#8221; nel manifest.<\/h3>\n<p>Potreste pensare di poter trattare il vostro manifest come un file statico, come in &#8220;assumi che io sia valido fino al 1 Aprile 2022&#8221; poi cambiare l&#8217;url al manifest quando dovete fare un aggiornamento.<\/p>\n<p>No! Non fatelo! <em>*sberla*<\/em><\/p>\n<p>Ricordatevi il Problema n.1: quando l&#8217;utente visita una pagina una seconda volta, otterr\u00e0 la versione di ApplicationCache. Se avete cambiato l&#8217;url al manifest, sfortuna, la versione memorizzata dall&#8217;utente punta ancora al vecchio manifest, che ovviamente \u00e8 identico byte per byte, quindi non si aggiorna alcunch\u00e9. Mai.<\/p>\n<h3>Problema n.5: le risorse non in cache non verranno caricate in una pagina in cache.<\/h3>\n<p>Se mettete in cache <code>index.html<\/code> ma non <code>cat.jpg<\/code>, quell&#8217;immagine non verr\u00e0 mostrata su <code>index.html<\/code> anche se siete online. No, davvero, quello \u00e8 il comportamento atteso, <a href=\"http:\/\/appcache-demo.s3-website-us-east-1.amazonaws.com\/without-network\/\">controllate voi stessi<\/a>.<\/p>\n<p>Per disabilitare questo comportamento, usate la sezione <code>NETWORK<\/code> del manifest.<\/p>\n<pre><code>\nCACHE MANIFEST\n# v1\n\nindex.html\n\nNETWORK:\n*\n<\/code><\/pre>\n<p>L&#8217;<code>*<\/code> indica che il browser dovrebbe permettere tutte le connessioni alle risorse non memorizzate da una pagina in cache. Potete qui <a href=\"http:\/\/appcache-demo.s3-website-us-east-1.amazonaws.com\/with-network\/\">vederlo applicato all&#8217;esempio precedente<\/a>. Ovviamente queste connessioni falliranno mentre siete offline.<\/p>\n<p>Ben fatto: siete arrivati alla fine dell&#8217;esempio di ApplicationCache. S\u00ec, davvero, abbiamo visto il caso semplice. Mi spiace. Cerchiamo qualcosa di pi\u00f9 difficile.<\/p>\n<\/div>\n<div class=\"paragrafo\">\n<h2>Mettere offline un sito che &#8220;fa cercare cose&#8221;<\/h2>\n<p>Come ho detto all&#8217;inizio di questo articolo (ricordate quant&#8217;ero felice?), Lanyrd ha recentemente lanciato <a href=\"http:\/\/m.lanyrd.com\">un sito mobile<\/a> che permette alla gente di guardare i programmi, i luoghi, chi partecipa alle conferenze e altro ancora. L&#8217;accesso offline a questi dati \u00e8 importante mentre si \u00e8 in viaggio e si devono affrontare dei costi di roaming.<\/p>\n<p>C&#8217;\u00e8 troppo contenuto per poter mettere tutto offline, ma a ciascun utente di solito interessano solo gli eventi a cui partecipa.<\/p>\n<p>Il vecchio <a href=\"http:\/\/diveintohtml5.info\/\">Dive into HTML5<\/a> ci mostra <a href=\"http:\/\/diveintohtml5.info\/offline.html#fallback\">un esempio in cui si mette offline<\/a>, un altro sito che permette di cercare cose. Funziona usando un manifest quasi vuoto a cui sono collegate tutte le pagine, cos\u00ec man mano che gli utenti navigano nel sito, quelle pagine diventano implicitamente parte della loro cache. Quando saranno offline, saranno in grado di visitare ciascuna delle pagine che hanno precedentemente visitato.<\/p>\n<p>Questa soluzione \u00e8 incredibilmente semplice, ma grazie ad alcuni &#8220;goffi pezzi&#8221; della specifica, si rivela essere un completo disastro. Tanto per cominciare, all&#8217;utente non viene data nessuna indicazione di quale sia il contenuto disponibile mentre si \u00e8 offline e non c&#8217;\u00e8 una API JavaScript che possiamo usare per arrivare a tale informazione. Potremmo scaricare e analizzare il manifest con JavaScript, ma tutte queste pagine Wikipedia sono memorizzate in cache implicitamente e pertanto non sono elencate.<\/p>\n<p>Inoltre, ricordatevi del Problema n.1: viene visualizzata la versione in cache piuttosto che la versione proveniente dal server. La pagina viene congelata per l&#8217;utente quando questo la guarda per la prima volta, ma come abbiamo visto nel Problema n.2 possiamo fare in modo che il browser cerchi degli aggiornamenti cambiando il testo nel file manifest. Tuttavia, quando cambiamo il file manifest? Ogni volta che viene aggiornata una voce di Wikipedia? In questo modo, gli aggiornamenti sarebbero troppo frequenti, infatti se un manifest cambia tra l&#8217;inizio e la fine di un aggiornamento, il browser <a href=\"http:\/\/www.whatwg.org\/specs\/web-apps\/current-work\/multipage\/offline.html#downloading-or-updating-an-application-cache\">considerer\u00e0 fallito questo aggiornamento (step 24)<\/a>.<\/p>\n<p>La frequenza di questi aggiornamenti \u00e8 un problema, ma il vero killer \u00e8 il peso di questi aggiornamenti. Prendete ad esempio il numero di pagine di Wikipedia che avete visitato: un centinaio? Migliaia? Un aggiornamento AppCache implica lo scaricamento di <strong>ogni singola<\/strong> pagina. AppCache non ci d\u00e0 un modo per rimuovere implicitamente gli oggetti memorizzati, cos\u00ec quel numero continuer\u00e0 a crescere e crescere finch\u00e9 non raggiunger\u00e0 un qualche tipo di limite della cache del browser ed il mondo esploder\u00e0. Non va bene.<\/p>\n<\/div>\n<div class=\"paragrafo\">\n<h2>Cosa vogliamo da un sito di riferimento che possiamo mettere offline?<\/h2>\n<p>I requisiti che vorrei per un sito di riferimento con capacit\u00e0 offline sono le seguenti:<\/p>\n<ul>\n<li>deve mostrare dati aggiornati mentre sono collegato, come farebbe se non ci fosse ApplicationCache,<\/li>\n<li>deve permettere a noi sviluppatori di controllare quale contenuto \u00e8 presente nella cache, quando viene messo in cache e in che modo,<\/li>\n<li>permetterci di rinviare alcune parti di quel controllo all&#8217;utente, magari sotto forma di un pulsante &#8220;Save offline&#8221; o &#8220;Read later&#8221;<\/li>\n<li>fare in modo che una sola visita una qualsiasi pagina dia al browser ci\u00f2 di cui ha bisogno per mostrare il contenuto offline.<\/li>\n<\/ul>\n<p>ApplicationCache, per quanto si vanti, non rende semplice tutto ci\u00f2. Se una pagina ha un manifest, viene messa in cache. Non c&#8217;\u00e8 modo che la pagina racconti al browser delle sue capacit\u00e0 offline senza che finisca in cache essa stessa.<\/p>\n<\/div>\n<div class=\"paragrafo\">\n<h2>Limitare la portata di ApplicationCache<\/h2>\n<p>Il modo pi\u00f9 semplice per far s\u00ec che una pagina si comporti come se non ci fosse ApplicationCache \u00e8 quello di dare la pagina senza un manifest. Possiamo dire al browser che ci sono cose offline immettendo un iframe nascosto che punti ad una pagina che <em>abbia effettivamente<\/em> un manifest.<\/p>\n<p><a href=\"http:\/\/appcache-demo.s3-website-us-east-1.amazonaws.com\/offline-iframe\/\">Visitate questa pagina<\/a> mentre siete online. Una volta che l&#8217;avete fatto, sarete in grado di visitare <a href=\"http:\/\/appcache-demo.s3-website-us-east-1.amazonaws.com\/offline-iframe\/offline.html\">questa pagina<\/a> mentre siete offline. La prima pagina non \u00e8 in cache, quindi l&#8217;utente avr\u00e0 sempre le informazioni pi\u00f9 aggiornate da quella.<\/p>\n<p>Tuttavia questa cosa non \u00e8 molto impressionante. Se visitate la prima pagina quando siete offline, non otterrete nulla.<\/p>\n<\/div>\n<div class=\"paragrafo\">\n<h2>Fallback<\/h2>\n<p>Il manifest di ApplicationCache ci permette di specificare una risorsa di fallback da usare quando una certa richiesta fallisce.<\/p>\n<pre><code>\nCACHE MANIFEST\n\nFALLBACK:\n\/ fallback.html\n\/assets\/imgs\/avatars\/ assets\/imgs\/avatars\/default-v1.png\n<\/code><\/pre>\n<p>Questo dice ad ApplicationCache di mostrare <code>fallback.html<\/code> ogni volta che una richiesta fallisce, a meno che la richiesta fallisca all&#8217;interno di <code>\/assets\/imgs\/avatars\/<\/code>, nel qual caso verr\u00e0 usata un&#8217;immagine di fallback.<\/p>\n<p>Per vedere come funziona in pratica, <a href=\"http:\/\/appcache-demo.s3-website-us-east-1.amazonaws.com\/simple-fallback\/\">visitate questa pagina<\/a>. Andateci di nuovo senza una connessione di rete e al suo posto vi verr\u00e0 mostrata una pagina di fallback. Vedete che non \u00e8 un &#8220;hard redirect&#8221;? Rimane l&#8217;url della pagina originale e questo ci sar\u00e0 utile.<\/p>\n<p>Incidentalmente, il fatto di avere un fallback <a href=\"http:\/\/appcache-demo.s3-website-us-east-1.amazonaws.com\/with-fallback\/\">fa rilassare le regole di blocco della rete che abbiamo incontrato al Problema n.5<\/a>. Adesso sono permesse le connessioni all&#8217;interno dello stesso dominio ma abbiamo ancora bisogno di usare la network wildcard per le connessioni ad altri domini.<\/p>\n<p>Adesso stiamo andando da qualche parte: mostriamo la pagina in cache solo se non ha successo la pagina normale.<\/p>\n<\/div>\n<div class=\"paragrafo\">\n<h2>Usare ApplicationCache solo per il contenuto statico<\/h2>\n<p>Con &#8220;contenuto statico&#8221; intendo il contenuto che non cambia mai: immagini, script, stili e la nostra pagina di fallback.<\/p>\n<pre><code>\nCACHE MANIFEST\n\njs\/script-v1.js\ncss\/style-v1.css\nimg\/logo-v1.png\n\nFALLBACK:\n\/ fallback\/v1.html\n\/imgs\/avatars\/ imgs\/avatars\/default-v1.png\n<\/code><\/pre>\n<p>Se avessimo bisogno di fare un cambiamento al JavaScript, dovremmo caricare un nuovo file su un nuovo url, ad esempio, <code>script-v2.js<\/code><\/p>\n<p>Questo aggira i Problemi n.1 e n.2: all&#8217;utente non verr\u00e0 mai mandato uno script o uno stile non aggiornati perch\u00e9 ci sar\u00e0 un nuovo url quando cambier\u00e0. Non dobbiamo gestire nel manifest dei numeri di versione basati sui commenti perch\u00e9 il cambiamento del testo nell&#8217;url \u00e8 sufficiente per azionare un aggiornamento. Tutte le risorse avranno una cache orientata al futuro, quindi solo i file modificati avranno bisogno di una richiesta http per aggiornarsi.<\/p>\n<h3>Problema n.6: addio download condizionali!<\/h3>\n<p>Dai, non avrete davvero pensato che questo sarebbe stato un articolo senza nemmeno un riferimento al responsive design!<\/p>\n<p>Avete due insiemi di immagini di design? Uno di questi \u00e8 molto pi\u00f9 piccolo e pi\u00f9 leggero, pensato apposta per le persone che vedono il sito un dispositivo mobile? Usate le media query per decidere quale di queste mostrare? Bene, ApplicationCache odia voi e la vostra famiglia.<\/p>\n<p>Tutte le immagini necessarie per visualizzare il vostro sito vanno nel manifest e il browser le scarica tutte. Nel caso delle immagini responsive, l&#8217;utente finisce con lo scaricare entrambe le versioni della stessa risorsa: viene cos\u00ec vanificato qualsiasi sforzo. Usate semplicemente le immagini a risoluzione desktop e ridimensionatele sul client usando <a href=\"https:\/\/developer.mozilla.org\/en\/CSS\/background-size\">CSS background size<\/a>.<\/p>\n<p>Se la versione mobile ha un design completamente differente, almeno mettetele in un&#8217;immagine sprite insieme alla grafica ad alta risoluzione, cos\u00ec che possano beneficiare insieme dalla compressione png basata sulla palette.<\/p>\n<p>La stessa regola vale per i font. Ho visto degli idioti raccomandare di usare <a href=\"http:\/\/speakerdeck.com\/u\/jaffathecake\/p\/in-your-font-face?slide=38\">molti formati di font<\/a>, il che va molto bene per i siti normali, ma non si possono tenere tutti nel manifest. Per l&#8217;uso offline, usate solo i True Type Fonts (TTF). &#8220;Hey, ma non era Web Open Font Format (WOFF) il futuro?&#8221; S\u00ec, probabilmente, ma solo per ragioni legali. Non c&#8217;\u00e8 nessun beneficio tecnico derivante dall&#8217;uso di WOFF piuttosto che di TTF. Ok, WOFF ha la compressione built-in, ma non \u00e8 migliore della versione &#8220;gzipped&#8221; di un TTF. Inoltre, <a href=\"http:\/\/caniuse.com\/woff\">WOFF non \u00e8 supportato dalle versioni pi\u00f9 vecchie di molti browser<\/a>, mentre il supporto per TTF si estende ben oltre.<\/p>\n<p>Comunque, torniamo ad ApplicationCache.<\/p>\n<\/div>\n<div class=\"paragrafo\">\n<h2>Usare LocalStorage per mettere offline dinamicamente<\/h2>\n<p>Non possiamo mettere offline tutto il nostro contenuto, perch\u00e9 ce n&#8217;\u00e8 troppo. Vogliamo che l&#8217;utente scelga a cosa vuole accedere offline. Possiamo usare LocalStorage per memorizzare i dati.<\/p>\n<p>S\u00ec, LocalStorage \u00e8 solo uno scaffale, ma \u00e8 uno scaffale estremamente utile e molto semplice da usare. Potete metterci qualsiasi dato testuale che volete e prenderlo poi dopo, da qualunque pagina dello stesso dominio.<\/p>\n<p>LocalStorage \u00e8 memorizzato sul disco, quindi usarlo \u00e8 economico <a href=\"http:\/\/calendar.perfplanet.com\/2011\/localstorage-read-performance\/\">ma non gratis<\/a>. A causa di ci\u00f2, dovremmo tenere il numero di letture e scritture basso e non vogliamo leggere e scrivere di pi\u00f9 di quello che dovremmo per ogni read\/write.<\/p>\n<p>Useremo una entry per ogni pagina che memorizziamo e una entry aggiuntiva per tener traccia di quello che abbiamo salvato offline, insieme ai rispettivi titoli di pagina. Questo significa che possiamo elencare tutte le pagine che abbiamo memorizzato con una sola lettura e mostrare una pagina particolare con due letture.<\/p>\n<p>Cos\u00ec, per salvare la pagina <code>articles\/1.html<\/code> per l&#8217;utilizzo offline, facciamo cos\u00ec: <\/p>\n<pre><code>\n\/\/ Get the page content\nvar pageHtml = document.body.innerHTML;\nvar urlPath = location.pathName;\n\/\/ Save it in local storage\nlocalStorage.setItem( urlPath, pageHtml );\n\/\/ Get our index\nvar index = JSON.parse( localStorage.getItem( index' ) );\n\/\/ Set the title and save it\nindex[ urlPath ] = document.title;\nlocalStorage.setItem( 'index', JSON.stringify( index ) );\n<\/code><\/pre>\n<p>Poi, se l&#8217;utente visita <code>articles\/1.html<\/code> senza una connessione, otterr\u00e0 fallback.html, che fa quanto segue:<\/p>\n<pre><code>\nvar pageHtml = localStorage.getItem( urlPath );\nif ( !pageHtml ) {\n\tdocument.body.innerHTML = '&lt;h1&gt;Page not available&lt;\/h1&gt;';\n}\nelse {\n\tdocument.body.innerHTML = localStorage.getItem( urlPath );\n\tdocument.title = localStorage.getItem( 'index' )[ urlPath ];\n}\n<\/code><\/pre>\n<p>Possiamo iterare <code>localStorage.getItem( 'index' )<\/code> per ottenere i dettagli su tutte le pagine che l&#8217;utente ha messo in cache.<\/p>\n<\/div>\n<div class=\"paragrafo\">\n<h2>Mettiamo tutto insieme<\/h2>\n<p><a href=\"http:\/\/appcache-demo.s3-website-us-east-1.amazonaws.com\/localstorage-cache\/\">Ecco una demo in azione di quanto illustrato sopra<\/a>. Le pagine dell&#8217;articolo possono essere messe in cache mediante un pulsante nel lato in alto a destra della pagina e la pagina index indicher\u00e0 quali pagine sono disponibili offline.<\/p>\n<p>Tutto il codice \u00e8 <a href=\"https:\/\/github.com\/jakearchibald\/appcache-demo\/tree\/master\/www\/localstorage-cache\">disponibile su GitHub<\/a>. Qualunque pagina pu\u00f2 essere messa in cache con una chiamata a <a href=\"https:\/\/github.com\/jakearchibald\/appcache-demo\/blob\/master\/www\/localstorage-cache\/js\/offliner-v1.js#L62\"><code>offliner.cacheCurrentPage()<\/code><\/a>. Questa viene richiamata a ciascuna visita alla pagina index e durante ogni visita a una pagina che l&#8217;utente desidera mettere in cache.<\/p>\n<p>Se l&#8217;utente finisce sulla pagina di fallback, viene richiamata <a href=\"https:\/\/github.com\/jakearchibald\/appcache-demo\/blob\/master\/www\/localstorage-cache\/js\/offliner-v1.js#L74\"><code>offliner.renderCurrentPage()<\/code><\/a>, che rende la pagina desiderata. Se non abbiamo una pagina da mostrare, viene visualizzato un messaggio. Oh, questo mi ricorda il&#8230;<\/p>\n<h3>Problema n.7: non sappiamo come siamo arrivati alla pagina di fallback.<\/h3>\n<p>Quando non possiamo visualizzare una particolare pagina, l&#8217;errore che mostriamo \u00e8 <a href=\"https:\/\/github.com\/jakearchibald\/appcache-demo\/blob\/master\/www\/localstorage-cache\/js\/offliner-v1.js#L84\">piuttosto vago<\/a>. Stando alla specifica, la pagina di fallback viene visualizzata se la richiesta originale risulta in &#8220;un redirect a una risorsa con un&#8217;altra origine (indicativa di un portale catturato), o un codice di stato 4xx o 5xx o equivalente, oppure se ci sono stati errori di rete (ma non se l&#8217;utente ha cancellato il download).&#8221;<\/p>\n<p>Per certi aspetti si tratta di una buona notizia: se l&#8217;utente \u00e8 online ma il nostro sito va gi\u00f9, il loro browser gli mostrer\u00e0 semplicemente i dati messi in cache e l&#8217;utente potrebbe non accorgersene nemmeno! Sfortunatamente, non abbiamo accesso alla ragione del fallback. Potrebbe essere causato dal fatto che l&#8217;utente non ha connessione, potrebbe essere che abbiano seguito un url rotto o averlo scritto male, oppure potrebbe essere colpa del server. Semplicemente, non lo sappiamo.<\/p>\n<p>Oh, avete notato l&#8217;accenno ai redirect?<\/p>\n<h3>Problema n.8: i redirect ad altri domini sono trattati come un errore.<\/h3>\n<p>S\u00ec, corretto, un altro problema. Sono sorpreso che stiate ancora leggendo. Se volete andare a chiudervi nel cubicolo di una toilette e rifiutarvi di uscirne finch\u00e9 non sar\u00e0 sparito internet, vi capirei perfettamente.<\/p>\n<p>Se uno dei vostri url decide di aver bisogno di fare redirect a Twitter o Facebook per fare un&#8217;autenticazione, il nostro amichevole Application Cache decider\u00e0 che <strong>NON \u00c8 POSSIBILE<\/strong> e ci mostrer\u00e0 al suo posto la nostra pagina di fallback.<\/p>\n<p>Questa regola ha dei buoni presupposti: se l&#8217;utente cerca di visitare il vostro sito e il wi-fi che stanno usando li ridirige a http:\/\/rubbish-network\/pay-for-wifi-access, mostrando al suo posto la nostra pagina di fallback, allora \u00e8 grandioso.<\/p>\n<p>Nel caso dei redirect intenzionali e spontanei per delle autorizzazioni, non funziona elencarli nella sezione <code>NETWORK<\/code>. Al contrario, dovete usare un JavaScript o un meta-redirect. Acc!<\/p>\n<\/div>\n<div class=\"paragrafo\">\n<h2>Gli svantaggi dell&#8217;approccio LocalStorage<\/h2>\n<p>&#8220;Ah, siamo passati agli svantaggi adesso? E cos&#8217;era esattamente il resto dell&#8217;articolo?&#8221; S\u00ec, lo so, non picchiatemi! Ci sono degli svantaggi rispetto alla semplice soluzione di ApplicationCache.<\/p>\n<p>Occorre JavaScript, mentre invece l&#8217;uso di ApplicationCache per Sprite Cow non \u00e8 dipendente da JavaScript (sebbene il resto del sito lo sia). Mi esporr\u00f2 alle critiche e vi dir\u00f2 che ci sono molti pochi utenti che supportano ApplicationCache e che non supportano JavaScript. Questo non vuol dire che l&#8217;assenza di supporto per JavaScript non sia importante: il <a href=\"http:\/\/m.lanyrd.com\">sito mobile di Lanyrd<\/a> funziona senza JavaScript. Infatti, evitiamo il parsing di JavaScript sui dispositivi pi\u00f9 vecchi per far s\u00ec che le cose rimangano semplici e rapide. <\/p>\n<p>L&#8217;esperienza su una cattiva connessione non \u00e8 fantastica: il problema con <code>FALLBACK<\/code> \u00e8 che la connessione originale deve cadere prima che possa avvenire un qualsiasi fallback, cosa che richiede del tempo se la connessione va e viene. In questo caso, il Problema n.1 )i file arrivano sempre da ApplicationCache) \u00e8 piuttosto utile.<\/p>\n<p>Non funziona su Opera. Opera non supporta correttamente le sezioni <code>FALLBACK<\/code> nei manifest. Speriamo che lo sistemino presto questo problema.<\/p>\n<\/div>\n<div class=\"paragrafo\">\n<h2>Cosa fa di diverso m.lanyrd.com?<\/h2>\n<p><a href=\"http:\/\/appcache-demo.s3-website-us-east-1.amazonaws.com\/localstorage-cache\/\">La demo che ho mostrato in precedenza<\/a> \u00e8 una semplificazione di quello che facciamo a Lanyrd. Invece di memorizzare l&#8217;HTML della pagina per l&#8217;utilizzo offline, memorizziamo i dati JSON in LocalStorage e i template per quei dati in ApplicationCache. Questo significa che possiamo aggiornare i template in maniera indipendente dai dati ed usare un insieme di dati in pi\u00f9 template.<\/p>\n<p>I nostri dati JSON sono versionati per conferenza. Questi numeri di versione vengono controllati quando navigate nel sito. Se non corrispondo a quelli che avete memorizzato, viene scaricato un aggiornamento. Ci\u00f2 significa che fate solo una richiesta per i dati aggiornati quando c&#8217;\u00e8 un aggiornamento.<\/p>\n<p>Piuttosto che dare un pulsante che permetta all&#8217;utente di memorizzare offline una particolare pagina, mettiamo in cache i dati degli eventi che l&#8217;utente segue o a cui partecipa. Come risultato, il server sa quello che l&#8217;utente vuole offline, quindi se cambiano dispositivo o se perdono in qualche modo la propria cache, possiamo rapidamente ripopolarla.<\/p>\n<p>Lo scambio delle pagine \u00e8 fatto con XMLHttpRequest e pushState: \u00e8 molto pi\u00f9 rapido su device mobile perch\u00e9 non deve rianalizzare il JavaScript ad ogni caricamento di pagina e la fa sembrare pi\u00f9 una app che un sito.<\/p>\n<p>Oh, per l&#8217;amor dei tempi andati, vai avanti!<\/p>\n<h3>Problema n.9: un balzo in pi\u00f9 per saltare attraverso XHR.<\/h3>\n<p>Potete fare delle richieste XHR per le risorse mentre siete offline. Sfortunatamente, le versioni pi\u00f9 vecchie di WebKit terminano la richiesta con uno <code>statusCode<\/code> pari a 0, che le librerie pi\u00f9 popolari interpretano come un errore.<\/p>\n<pre><code>\n\/\/ In jQuery...\n$.ajax( url ).done( function(response) {\n\t\/\/ Hooray, it worked!\n}).fail( function(response) {\n\t\/\/ Unfortunately, some requests\n\t\/\/ that worked end up here\n});\n<\/code><\/pre>\n<p>Allo stato brado, potete vedere questo su Blackberry Playbook e sui dispositivi che usano iOS3 e Android 3\/4. Il browser di Android 2 non ha questo baco. Strandamente, sembra che utilizzi una versione pi\u00f9 nuova di WebKit. Supporta anche <code>history.pushState<\/code> mentre al contrario i browser sulle versioni successive di Android non lo fanno. GRAZIE ANDROID. Ecco come aggirare questo problema:<\/p>\n<pre><code>\n$.ajax( url ).always( function(response) {\n\t\/\/ Exit if this request was deliberately aborted\n\tif (response.statusText === 'abort') { return; }\n\n\t\/\/ Does this smell like an error?\n\tif (response.responseText !== undefined) {\n\t\tif (response.responseText &amp;&amp; response.status < 400) {\n\t\t\t\/\/ Not a real error, recover the content\n\t\t\tresponse = response.responseText;\n\t\t}\n\t\telse {\n\t\t\t\/\/ This is a proper error, deal with it\n\t\t\treturn;\n\t\t}\n\t}\n\n\t\/\/ do something with 'response'\n});\n<\/code><\/pre>\n<p>Ed <a href=\"http:\/\/appcache-demo.s3-website-us-east-1.amazonaws.com\/localstorage-cache\/\">ecco una demo<\/a> su cui potete fare un po' di test.<\/p>\n<\/div>\n<div class=\"paragrafo\">\n<h2>ApplicationCache: il vostro amico idiota<\/h2>\n<p>Non sto dicendo che ApplicationCache debba essere evitato: \u00e8 estremamente utile. Conosciamo tutti qualcuno che si loda un po' o che ha bisogno di pi\u00f9 &#8220;attenzioni&#8221; degli altri in caso facciano qualcosa di molto stupido. ApplicationCache \u00e8 una di quelle persone.<\/p>\n<p>ApplicationCache pu\u00f2, dopo attente istruzioni, fare cose che altri non riescono a fare. Ma quando dice &#8220;Non dovete chiamare un idraulico, vi sistemo io il vostro bagno! Ho fatto tutti i bagni a Buckingham Palace, sai?&#8221;, lasciatelo perdere.<\/p>\n<p>Se state creando una qualsiasi cosa pi\u00f9 complicata di un'&#8220;app&#8221; indipendente a lato client, passerete dei bei momenti usandola al suo minimo assoluto e facendo fare tutto il resto a LocalStorage.<\/p>\n<p>Illustrazioni: {carlok}<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Siamo meglio connessi di quanto non lo siamo mai stati ma non siamo sempre connessi. ApplicationCache permette agli utenti di interagire con i propri dati anche quando si \u00e8 scollegati, ma insieme a grandi poteri arrivano grandi problemi. Ad esempio, i files provengono sempre da ApplicationCache, anche se l&#8217;utente \u00e8 online, Oh e in certe circostanze, un browser nono pu\u00f2 sapere che quel contenuto online \u00e8 cambiato, il che implica che l&#8217;utente ottiene continuamente contenuto vecchio. E&#8230; S\u00ec, a seconda di come mettete in cache le vostre risorse, le risorse non messe in cache potrebbero non caricarsi, anche quando l&#8217;utente \u00e8 online. Jake Archibald di Lanyrd fa luce sui rischi di ApplicationCache e condivide con noi strategie, tecniche e code workaround per massimizzare il piacere e minimizzare il dolore sia per l&#8217;utente sia per lo sviluppatore. Tutto questo insieme a una demo. Tuffiamoci!<\/p>\n","protected":false},"author":818,"featured_media":7000658,"comment_status":"open","ping_status":"open","template":"","categories":[269,247,271,66],"tags":[],"coauthors":[364],"class_list":["post-269","article","type-article","status-publish","has-post-thumbnail","hentry","category-application-development","category-html","category-javascript","category-numero-51-22-maggio-2012"],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/article\/269","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/article"}],"about":[{"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/types\/article"}],"author":[{"embeddable":true,"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/users\/818"}],"replies":[{"embeddable":true,"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/comments?post=269"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/media\/7000658"}],"wp:attachment":[{"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/media?parent=269"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/categories?post=269"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/tags?post=269"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/coauthors?post=269"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}