-
Notifications
You must be signed in to change notification settings - Fork 85
/
Copy pathstorage.html
365 lines (260 loc) · 34.3 KB
/
storage.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
<!DOCTYPE html>
<html lang=pt-br>
<meta charset=utf-8>
<title>Local Storage - Dive Into HTML5</title>
<!--[if lt IE 9]><script src=j/html5.js></script><![endif]-->
<link rel=alternate type=application/atom+xml href=https://github.com/zenorocha/diveintohtml5/commits/gh-pages.atom>
<link rel=alternate href=http://diveintohtml5.info/storage.html hreflang=en>
<link rel=stylesheet href=screen.css>
<style>
body{counter-reset:h1 7}
</style>
<link rel=stylesheet media='only screen and (max-device-width: 480px)' href=mobile.css>
<link rel=prefetch href=detect.html>
<a href="https://github.com/zenorocha/diveintohtml5"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>
<p>Você está aqui: <a href=index.html>Home</a> <span class=u>‣</span> <a href=table-of-contents.html#storage>Dive Into <abbr>HTML5</abbr></a> <span class=u>‣</span>
<h1><br>O Passado, Presente & Futuro de Armazenamento Local para Aplicações Web</h1>
<p id=toc>
<p class=a>❧
<h2 id=divingin>Mergulhando</h2>
<p class=f><img src=i/aoc-a.png alt=A width=110 height=106>Armazenamento local persistente é uma das áreas onde aplicações nativas de cliente têm mantido uma vantagem sobre aplicações web.
Para aplicações nativas, o sistema operacional tipicamente provê uma camada de abstração para armazenar e recuperar
dados específicos do aplicativo como preferências ou estado de tempo de execução.
Estes valores podem ser armazenados no registro, arquivos INI,
arquivos XML ou algum outro lugar de acordo com a convenção da plataforma.
Se o seu aplicativo cliente nativo necessita de armazenamento local diferente dos pares chave/valor,
você pode inserir o seu próprio banco de dados, inventar o seu próprio formato de arquivo ou qualquer número de outras soluções.
<p>Historicamente, aplicações web não tiveram nenhum desses luxos. Cookies foram inventados no início da história da web e, de fato, eles podem ser usados para armazenamento local persistente de pequenas quantidades de dados. Mas eles têm três desvantagens potencialmente [potentially dealbreaking downsides]:
<ul>
<li>Cookies são incluídos em toda requisição <abbr>HTTP</abbr>, atrasando assim sua aplicação web por desnecessariamente sempre transmitir os mesmos dados;
<li>Cookies são incluídos em toda requisição <abbr>HTTP</abbr>, enviando assim dados não criptografados através da internet (a menos que sua aplicação web inteira seja servida sobre <abbr>SSL</ abbr>);
<li>Cookies são limitados a certa de 4KB de dados — suficiente para atrasar sua aplicação (ver acima), mas não o suficiente para ser muito útil;
</ul>
<p>O que nós realmente queremos é
<ul>
<li>bastante espaço de armazenamento
<li>no cliente
<li>que persista depois da atualização da página
<li>e não seja transmitido para o servidor
</ul>
<p>Antes da <abbr>HTML5</abbr>, todas as tentativas de obtenção foram fundamentalmente insatisfatórias de diferentes maneiras.
<p class=a>❧
<h2 id=history>Uma Breve História dos Hacks de armazenamento locais antes HTML5</h2>
<p>No início, existia apenas o Internet Explorer. Ou ao menos, era isso que a Microsoft gostaria que o mundo acreditasse. Para esse fim, como parte da <a href="http://en.wikipedia.org/wiki/Browser_wars#The_first_browser_war">Primeira Grande Guerra de Browsers</a>, a Microsoft inventou uma grande quantidade de coisas e as incluiu no seu "vencedor de todas as batalhas", o Internet Explorer. Uma dessas coisas foi conhecida como <a href="http://msdn.microsoft.com/en-us/library/ms531078(v=VS.85).aspx"><abbr>DHTML</abbr> Comportamentos</a>, e um desses comportamentos foi conhecido como <a href="http://msdn.microsoft.com/en-us/library/ms531424(VS.85).aspx">userData</a>.
<p><code>userData</code> possibilita que páginas web armazenem até 64KB de dados por domínio, em uma estrutura hierárquica baseada em XML. (Domínios confiáveis, tais como sites de intranet, podem armazenar 10 vezes esta quantidade. E, <a href="http://en.wikiquote.org/wiki/Bill_Gates#Misattributed">640 KB deveria ser o suficiente para qualquer um</a>.) O IE não apresenta qualquer forma de diálogo de permissões e não há previsão para o aumento da quantidade de armazenamento disponível.
<p> Em 2002, a Adobe introduziu uma funcionalidade no Flash 6 que ganhou o infeliz e enganoso nome de “Flash cookies.” Dentro do ambiente do Flash, a funcionalidade é propriamente conhecida como <a href="http://kb2.adobe.com/cps/161/tn_16194.html">Local Shared Objects - Objetos de local compartilhado</a>. Resumidamente, isso permitia que objetos Flash armazenassem até 100KB de dados por domínio. Brad Neuberg desenvolveu um pré protótipo de uma ponte Flash-to-JavaScript chamada <a href="http://codinginparadise.org/weblog/2005/10/amass-ajax-massive-storage-system.html">AMASS</a> (AJAX Massive Storage System), porém ficou limitado por algumas peculiaridades de design do Flash. Em 2006, com o advento da <a href="http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/flash/external/ExternalInterface.html">ExternalInterface</a> presente no Flash 8, acessar <abbr>LSO</abbr>s a partir do JavaScript tornou-se uma coisa de magnitude mais fácil e rápida. Brad reescreveu o <abbr>AMASS</abbr> e o integrou dentro do popular <a href=http://www.dojotoolkit.org/>Dojo Toolkit</a> com o nome de <a href="http://api.dojotoolkit.org/jsdoc/HEAD/dojox.storage.manager"><code>dojox.storage</code></a>. Flash garante a cada domínio 100 KB de armazenamento “livre.” Além disso, ele solicita ao usuário para cada ordem de magnitude um aumento no armazenamento de dados (1 Mb, 10 Mb, e assim por diante).
<p>Em 2007, o Google lançou o <a href="http://gears.google.com/">Gears</a>, um plugin de código aberto com o objetivo de providenciar capacidades adicionais nos navegadores. (Nós já tínhamos discutido sobre Gears no contexto de <a href=geolocation.html#ie> providenciar uma <abbr>API</abbr> de geolocalização para o Internet Explorer</a>.) Gears possibilita <a href="http://code.google.com/apis/gears/api_database.html">uma <abbr>API</abbr> para uma base de dados <abbr>SQL</abbr> icorporada</a> baseada em <a href="http://www.sqlite.org/">SQLite</a>. Após obter a permissão de um usuário uma vez, Gears pode armazenar ilimitada quantidade de dados por um domínio em uma tabela de banco de dados <abbr>SQL</abbr>.
<p>Entretanto, Brad Neuberg e outros continuaram a trabalhar em cima do <code>dojox.storage</code> para providenciar uma interface unificada para todos esses diferentes plugins e <abbr>API</abbr>s. Em 2009, <code>dojox.storage</code> podia auto-detectar (e providenciar uma interface unificada em cima de) Adobe Flash, Gears, Adobe AIR, e um pré protótipo da <abbr>HTML5</abbr> storage que era apenas implementado em antigas versões do Firefox.
<p>Quando você analisa essas soluções, um padrão surge: todos eles são específicos de um único navegador, ou dependentes de plugins de terceiros. Apesar do esforço heróico para deixar clara as diferenças (no <code>dojox.storage</code>), eles todos apresentam interfaces totalmente diferentes, têm diferentes limitações de armazenamento, e apresentam diferentes experiências de usabilidade. Portanto, esse é o problema que a <abbr>HTML5</abbr> se propõe a resolver: providenciar uma padronizada <abbr>API</abbr>, implementada de forma nativa e consistente em vários navegadores, sem a necessidade e dependência de plugins de terceiros.
<p class=a>❧
<h2 id=localstorage>Introdução ao HTML5 Storage</h2>
<p>O que me refiro como “<abbr>HTML5</abbr> Storage” é a especificação nomeada de <a href="http://dev.w3.org/html5/webstorage/">Web Storage</a>, que estava na especificação da <abbr>HTML5</abbr> por um tempo, mas era tratado separadamente por desinteresse e razões políticas. Certos navegadores se referenciavam como “Local Storage” ou “<abbr>DOM</abbr> Storage.” A nomeação ficava ainda mais complicada em navegadores relacionados com padrões emergentes, que vou discutir mais adiante neste capítulo.
<p>Então o que é <abbr>HTML5</abbr> Storage? Simplesmente armazenar, é a forma de páginas da web amazenarem pares de chave/valor localmente, dentro do navegador do cliente. Semelhante aos cookies, esses dados persistem mesmo depois de você sair do site, fechar a guia ou o navegador. Diferente dos cookies, esses dados nunca são transmitidos ao servidor (a menos que você queria enviá-los manualmente). Diferente <a href=#history>de todas as tentativas anteriores</a> de fornecimento de armazenamento local persistente, este é implementado de forma nativa nos navegadores, para que esteja disponível mesmo quando os plugins de terceiros não estiverem.
<p>Quais navegadores? Bom, as últimas versões de quase todos os navegadores suportam <abbr>HTML5</abbr> Storage… até o Internet Explorer!
<table class=bc>
<caption>Suporte ao <abbr>HTML5</abbr> Storage</caption>
<thead>
<tr><th title="Internet Explorer">IE<th title="Mozilla Firefox">Firefox<th title="Apple Safari">Safari<th title="Google Chrome">Chrome<th>Opera<th>iPhone<th>Android
<tbody>
<tr><td>8.0+<td>3.5+<td>4.0+<td>4.0+<td>10.5+<td>2.0+<td>2.0+
</table>
<p>Direto do seu código JavaScript, você acessa o <abbr>HTML5</abbr> Storage através do objeto <code>localStorage</code> no objeto global <code>window</code>. Antes de usá-lo, você deve <a href=detect.html#storage>detectar se o navegador tem suporte</a>.
<p class="legend top" style="padding-left:6em"><span class=arrow>↶</span> Verificando suporte <abbr>HTML5</abbr> Storage
<pre><code>function supports_html5_storage() {
try {
return 'localStorage' in window && window['localStorage'] !== null;
} catch (e) {
return false;
}
}</code></pre>
<p>Ao invés de escrever essa função, você pode usar <a href=detect.html#modernizr>Modernizr</a> para detectar o suporte da <abbr>HTML5</abbr> Storage.
<pre><code>if (<mark>Modernizr.localstorage</mark>) {
// window.localStorage é suportado pelo seu navegador!
} else {
// não há suporte nativo ao HTML5 storage no seu navegador! :(
// tente talvez dojox.storage ou alguma solução de terceiros
}</code></pre>
<p class=a>❧
<h2 id=methods>Usando HTML5 Storage</h2>
<p><abbr>HTML5</abbr> Storage é baseado em pares de chave/valor nomeadas. Você pode armazenar dados em uma chave nomeada, e com essa mesma chave, recuperar os dados armazenados. A chave nomeada é uma string. E seu valor, é qualquer um suportado pelo JavaScript, incluindo Strings, Booleanos, Inteiros, ou Flutuantes. Conteúdo, os dados são armazenados como uma sequência de strings. Se você estiver armazenando e recuperando qualquer outro tipo de dado diferente de Strings, você precisa usar funções como <code>parseInt()</code> ou <code>parseFloat()</code> para recuperar os dados em um tipo esperado pelo JavaScript.
<pre><code>interface Storage {
getter any getItem(in DOMString key);
setter creator void setItem(in DOMString key, in any data);
};</code></pre>
<p>Chamando <code>setItem()</code> com o nome de uma chave que já existe, ela sobrescreve o valor anterior. Chamando <code>getItem()</code> com o nome de uma chave que não existe, retornará <code>null</code> ao invés de de uma exceção.
<p>Como outros objetos JavaScript, você pode tratar o objeto <code>localStorage</code> como um array associativo. Ao invés de usar os métodos <code>getItem()</code> e <code>setItem()</code>, você pode usar simplesmentes colchetes. Por exemplo, no trecho seguinte:
<pre><code>var foo = localStorage.<mark>getItem</mark>("bar");
// ...
localStorage.<mark>setItem</mark>("bar", foo);</code></pre>
<p>…pode ser reescrito para usar a sintaxe de colchetes ao invés:
<pre><code>var foo = localStorage<mark>["bar"]</mark>;
// ...
localStorage<mark>["bar"]</mark> = foo;</code></pre>
<p>Existem métodos para remover um valor já dado a uma chave, e limpar o armazenamento local inteiro (isto é, apagar todas as chaves/valor existentes de uma só vez).
<pre><code>interface Storage {
deleter void removeItem(in DOMString key);
void clear();
};</code></pre>
<p>Chamando <code>removeItem()</code> com uma chave inexistente, não retornará nada.
<p>Finalmente, há uma propriedade para obter o número total de chaves/valor na área de armazenamento local, e também, para percorrer todas as chaves pelo seu índice (e retornar o nome de cada uma).
<pre><code>interface Storage {
readonly attribute unsigned long length;
getter DOMString key(in unsigned long index);
};</code></pre>
<p>Se você chamar <code>key()</code> com um índice que não está entre 0–(<code>length</code>-1), a função retornará <code>null</code>.
<h3 id=storage-event>Controlando Alterações na Área do HTML5 Storage</h3>
<p>Se quiser manter o controle das constantes mudanças no armazenamento local, você pode capturar o evento <code>storage</code>. O evento <code>storage</code> é ativado no objeto <code>window</code> sempre que <code>setItem()</code>, <code>removeItem()</code>, ou <code>clear()</code> é chamado <em>e, geralmente, muda alguma coisa</em>. Por exemplo, se você definir um valor a uma chave existente ou chamar <code>clear()</code> em chaves não nomeadas, o evento <code>storage</code> não será chamado, porque realmente, nada mudou na área de armazenamento local.
<p>O evento <code>storage</code> é suportado em todos os lugares em que o objeto <code>localStorage</code> funciona, incluindo Internet Explorer 8. IE 8 não suporta o padrão W3C <code>addEventListener</code> (será adicionado, finalmente, no IE 9). Portanto, para pegar o evento <code>storage</code> , você vai precisar checar qual mecanismo de evento o navegador suporta. (Se você já fez isso antes, para outros eventos, pode pular essa seção. Pegando o evento <code>storage</code> , funciona da mesma forma como todos os outros eventos, você estará sempre preso. Se você preferir usar jQuery ou qualquer outra biblioteca JavaScript para registrar os eventos de manipulação, você pode fazer isso com o evento <code>storage</code> , também.)
<pre><code>if (window.addEventListener) {
window.addEventListener("storage", handle_storage, false);
} else {
window.attachEvent("onstorage", handle_storage);
};</code></pre>
<p>A função de callback <code>handle_storage</code> é chamada junto ao objeto <code>StorageEvent</code>, exceto no Internet Explorer que os objetos de evento são armazenados no <code>window.event</code>.
<pre><code>function handle_storage(e) {
if (!e) { e = window.event; }
}</code></pre>
<p>Nesse ponto, a variável <code>e</code> será o objeto <code>StorageEvent</code>, que tem as seguintes propriedades:
<table class=st>
<caption>Objeto StorageEvent</caption>
<thead>
<tr class=ho><th>Propriedade<th>Tipo<th>Descrição
<tbody>
<tr class=zebra><th><code>key</code><td>string<td>o nome da chave que foi adicionada, removida ou alterada
<tr><th><code>oldValue</code><td>qualquer<td>o valor antigo (agora atualizado), ou <code>null</code> se for um novo item adicionado
<tr class=zebra><th><code>newValue</code><td>qualquer<td>o novo valor, ou <code>null</code> se um item foi removido
<tr><th><code>url</code><sup>*</sup><td>string<td>a página que chamou o método que realizou a mudança
<tfoot>
<tr><td colspan=3>* Nota: a propriedade <code>url</code> é originalmente chamada de <code>uri</code>. Alguns navegadores lançaram essa propriedade antes das mudanças na especificação. Para uma compatibilidade máxima, você deve checar se a propriedade <code>url</code> existe, se não, checar se a propriedade <code>uri</code> existe.
</table>
<p>O evento <code>storage</code> não é cancelável. Dentro da função callback <code>handle_storage</code>, não há maneira de parar essa mundaça. É uma simples forma do navegador dizer, “ei, isso já aconteceu. Não há nada que você possa fazer, eu só queria te avisar sobre isso.”
<h3 id=limitations>Limitações nos navegadores atuais</h3>
<p>Ao falar sobre <a href=#history>a história dos hacks no armazenamento local</a> usando plugins de terceiros, fiz questão de mencionar a limitação de cada técnica, tal como limite de armazenamento. Apenas percebi que não tinha mencionado nada sobre as limitações do agora padronizado armazenamento da <abbr>HTML5</abbr>. Vou dar a vocês as respostas primeiro, em seguida explicarei elas. As respostas, em ordem de importância, são “5 megabytes,” “<code>QUOTA_EXCEEDED_ERR</code>,” e “Não.”
<p>“5 megabytes” é a quantidade em espaço que cada <a href=http://www.whatwg.org/specs/web-apps/current-work/multipage/origin-0.html#origin-0>origem</a> tem por padrão. Isto é surpreendentemente consistente em todos os navegadores, embora seja formulada como não mais do que uma sugestão na especificação do Armazenamento local da <abbr>HTML5</abbr>. Um aspecto a ter em mente é que você está armazenando strings, não as informações no seu formato original. Se você está armazenando muitos inteiros ou floats, a diferença de representação pode realmente aumentar. Cada dígito naquele float está sendo armazenado como um caractere, não na representação usual de um ponto de número flutuante.
<p>“<code>QUOTA_EXCEEDED_ERR</code>” é a exceção que será lançada se você exceder sua cota de armazenamento de 5 megabytes. “Não” é a resposta para a próxima questão óbvia, “Eu posso pedir permissão do usuário para usar mais espaço de armazenamento?” No momento em que escrevo (fevereiro 2011), não há navegador que suporte qualquer mecanismo para desenvolvedores web requisitarem mais espaço de armazenamento. Alguns navegadores (<a href=http://dev.opera.com/articles/view/web-storage/>como Opera</a>) permitem que o usuário controle a cota de armazenamento de cada site, mas isto é uma ação puramente iniciada pelo usuário, não algo que você como desenvolvedor pode construir na sua aplicação web.
<p class=a>❧
<h2 id=halma>Armazenamento HTML5 em Ação</h2>
<p>Vamos ver o Armazenamento da <abbr>HTML5</abbr> em ação. Recorde <a href=canvas.html#halma>o jogo Halma que nós construímos no capítulo do canvas</a>. Existe um pequeno problema com o jogo: se você fechar o navegador no meio do jogo, você perderá seu progresso. Porém, com o armazenamento da <abbr>HTML5</abbr>, podemos salvar o progresso localmente, dentro do próprio navegador. Aqui está <a href=examples/localstorage-halma.html>uma demonstração no ar</a>. Faça alguns movimentos, depois feche a aba do navegador, em seguida reabra ela. Se o seu navegador suporta Armazenamendo da <abbr>HTML5</abbr>, a página de demonstração deve magicamente recordar a sua exata posição dentro do jogo, incluindo o número de movimentos que você fez, a posição de cada uma das peças do tabuleiro, e até mesmo se uma determinada peça está selecionada.
<p>Como isto funciona? Cada momento que uma mudança acontece dentro do jogo, chamamos esta função:
<pre><code>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;
}</code></pre>
<p>Como você pode ver, ela usa o objeto <code>localStorage</code> para salvar se há um jogo em progresso (<code>gGameInProgress</code>, um booleano). Se assim for, percorre as peças (<code>gPieces</code>, um <code>Array</code> JavaScript) e salva o número de linha e coluna de cada uma. Em seguida ele salva alguns estados adicionais do jogo, incluindo a peça que está selecionada (<code>gSelectedPieceIndex</code>, um inteiro), se a peça está no meio de uma série potencialmente longa de saltos (<code>gSelectedPieceHasMoved</code>, um booleano), e o número total de movimentos feito até então (<code>gMoveCount</code>, um inteiro).
<p>No carregamento da página, em vez de chamar automaticamente uma função <code>newGame()</code> que resetaria estas variáveis para valores "hard-coded", chamamos uma função <code>resumeGame()</code>. Usando Armazenamento <abbr>HTML5</abbr>, a função <code>resumeGame()</code> checa se um estado relacionado ao jogo em progresso está armazenado localmente. Se está, ele recupera esses valores usando o objeto do <code>localStorage</code>.
<pre><code>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;
}</code></pre>
<p>A parte mais importante desta função é a ressalva que mencionei no início deste capítulo, que vou repetir aqui: <em>Informações são armazenadas como string. Se você está armazenando algo diferente de string, você vai precisar forçar uma conversão quando você recuperá-la.</em> Por exemplo, a bandeira para verificar se existe um jogo em progresso (<code>gGameInProgress</code>) é um booleano. Na função <code>saveGameState()</code>, nós apenas armazenamos e não nos preocupamos em relação ao datatype:
<pre><code>localStorage["halma.game.in.progress"] = <mark>gGameInProgress</mark>;</code></pre>
<p>Pórem, na função <code>resumeGame()</code>, precisamos tratar o valor que pegamos da área de armazenamento local como uma string e construir manualmente o valor booleano apropriado:
<pre><code>gGameInProgress = (localStorage["halma.game.in.progress"] <mark>== "true"</mark>);</code></pre>
<p>Semelhantemente, o número de movimentos está armazenado no <code>gMoveCount</code> como um inteiro. Na função <code>saveGameState()</code>, apenas armazenamos:
<pre><code>localStorage["halma.movecount"] = <mark>gMoveCount</mark>;</code></pre>
<p>Mas na função <code>resumeGame()</code>, precisamos forçar o valor para um inteiro, usando a função nativa do JavaScript <code>parseInt()</code>:
<pre><code>gMoveCount = <mark>parseInt</mark>(localStorage["halma.movecount"]);</code></pre>
<p class=a>❧
<h2 id=future>Além de Pares de Chaves-Valores Nomeados: Visões conflitantes</h2>
<p>Enquanto <a href=#history>o passado é cheio de truques e soluções alternativas</a>, a condição presente do Armazenamendo da <abbr>HTML5</abbr> é surpreendentemente otimista. Uma nova <abbr>API</abbr> vem sendo padronizada e implementada em todos os principais navegadores, plataformas e dispositivos. Como um desenvolvedor web, isto não é algo que você vê todo dia, não é verdade? Porém, há mais vida além dos “5 megabytes de pares de chaves/valores nomeados” e o futuro do armazenamento local persistente é…como poderei colocá-lo… bom, existem visões conflitantes.
<p>Uma visão é uma sigla que você provavelmente já sabe: <abbr>SQL</abbr>. Em 2007, a Google lançou <a href=http://gears.google.com/>Gears</a>, um plugin de código aberto que incluía um banco de dados incorporado com base de dados no <a href=http://www.sqlite.org/>SQLite</a>. Esse protótipo inicial influenciou a criação da especificação do <a href=http://dev.w3.org/html5/webdatabase/>Web SQL Database</a>. Web SQL Database (formalmente conhecida como “WebDB”) fornece uma fina camada em torno de uma base de dados <abbr>SQL</abbr>, permitindo você fazer coisas como essa pelo JavaScript:
<p class="legend top" style="padding-left:3em"><span class=arrow>↶</span>código atual funcionando em 4 navegadores
<pre><code><mark>openDatabase</mark>('documents', '1.0', 'Local document storage', 5*1024*1024, function (db) {
db.changeVersion('', '1.0', function (t) {
t.<mark>executeSql</mark>('CREATE TABLE docids (id, name)');
}, error);
});</code></pre>
<p>Você pode ver, maior parte da ação reside na sequência que você passa para o método <code>executeSql</code>. Esta string pode ser qualquer comando suportado pelo <abbr>SQL</abbr>, incluindo <code>SELECT</code>, <code>UPDATE</code>, <code>INSERT</code> and <code>DELETE</code>. É como programação de base de dados backend, exceto o fato de você está fazendo isto via JavaScript! Oh alegria!
<p>A especificação da Base de Dados SQL vem sendo implementada por quatro navegadores e plataformas.
<table class=bc>
<caption>Suporte a Base de Dados SQL Web</caption>
<thead>
<tr><th title="Internet Explorer">IE<th title="Mozilla Firefox">Firefox<th title="Apple Safari">Safari<th title="Google Chrome">Chrome<th>Opera<th>iPhone<th>Android
<tbody>
<tr><td>·<td>·<td>4.0+<td>4.0+<td>10.5+<td>3.0+<td>2.0+
</table>
<p>Claro, se você já usou mais de um produto de banco de dados em sua vida, você está ciente que “<abbr>SQL</abbr>” é mais um termo de marketing do que um rígido e rápido padrão. (Alguns podem dizer o mesmo da “HTML5,” mas não se importe.) Claro, existe uma especificação atual do <abbr>SQL</abbr> (ela é chamada de <a href="http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt">SQL-92</a>), mas não há nenhuma base de dados do servidor no mundo que está em conformidade com esta e apenas esta especificação. Existe o <abbr>SQL</abbr> da Oracle, <abbr>SQL</abbr> da Microsoft, <abbr>SQL</abbr> do MySQL, <abbr>SQL</abbr> do PostgreSQL e <abbr>SQL</abbr> do SQLite. De fato, cada um desses produtos adiciona novas funcionalidades ao <abbr>SQL</abbr> no passar do tempo, então mesmo dizendo “<abbr>SQL</abbr> do SQLite” não é suficiente para determinar exatamente o que você está falando. Você precisa dizer a versão do <abbr>SQL</abbr> que acompanha o SQLite versão X.Y.Z.”
<p>Tudo o que nos leva ao seguinte aviso, atualmente residindo no topo da especificação da Base de Dados SQL Web:
<blockquote>
<p>Esta especificação chegou a um impasse: todos os implementadores interessados têm utilizado o mesmo backend do SQL (Sqlite), mas precisamos de múltiplas implementações independentes para prosseguir ao longo de um caminho de normalização. Até outro implementador estar interessado em implementar esta especificação, a descrição do dialeto SQL foi deixada como simplesmente uma referência para Sqlite, o que não é aceitável para um padrão.
</blockquote>
<p>Isto é contra esse cenário que vou apresentá-lo para uma outra visão concorrente avançada, persistente, armazenamento local para aplicações web: <a href=http://dev.w3.org/2006/webapi/IndexedDB/>A <abbr>API</abbr> de "Indexed Database"</a>, formalmente conhecida como “WebSimpleDB,” agora carinhosamente conhecida como “IndexedDB.”
<p>A <abbr>API</abbr> do “Indexed Database” expõe o que é chamado um <em>objeto de armazenamento</em>. Um objeto de armazenamento compartilha vários conceitos com o a base de dados <abbr>SQL</abbr>. Existem “base de dados” com “registros,” e cada registro tem um número definido de “campos.” Cada campo tem um “datatype” específico, que é definido quando a base de dados é criada. Você pode selecionar um subconjunto de registros, depois enumerá-los com um “índice.” Alterações no armazenamento de objetos são tratados dentro de “transações”.
<p>Se você já fez alguma programação de banco de dados <abbr>SQL</abbr>, esses termos provavelmente serão familiares. A principal diferença é que o objeto de armazenamento não tem linguagem de consulta estruturada. Você não constrói uma declaração como <code>"SELECT * from USERS where ACTIVE = 'Y'"</code>. Em vez disso, você usa métodos fornecidos pelo objeto de armazenamento para adicionar um índice na base de dados nomeada “<code>USERS</code>,” enumerar os registros, filtrar os registros de usuários inativos, e usar métodos de acesso para obter os valores de cada campo nos registros restantes. <a href="http://hacks.mozilla.org/2010/06/comparing-indexeddb-and-webdatabase/">Uma visão geral do IndexedDB</a> é um bom tutorial de como IndexedDB funciona, fazendo comparações lado a lado entre o IndexedDB e base dados SQL.
<p>No momento que escrevo, IndexedDB está implementado apenas na <a href=https://developer.mozilla.org/en/Firefox_4_for_developers>versão beta do Firefox 4</a>. (Em contraste, a Mozilla afirmou que <a href="http://hacks.mozilla.org/2010/06/beyond-html5-database-apis-and-the-road-to-indexeddb/">eles nunca irão implementar Base de Dados SQL Web</a>.) Google afirmou que <a href="http://www.chromium.org/developers/design-documents/indexeddb">estão considerando o suporte ao IndexedDB</a> para o Chromium e o Google Chrome. E até mesmo a Microsoft disse que o IndexedDB “<a href="http://blogs.msdn.com/b/ie/archive/2010/03/09/working-with-the-html5-community.aspx">é uma ótima solução para a web</a>.”
<p>Então o que você, como desenvolvedor web, pode fazer com IndexedDB? Neste momento, praticamente nada além de algumas demonstrações técnicas. Um ano a frente? Talvez algo mais. Para iniciar, confira os links na seção “Leitura complementar” com alguns bons tutoriais.
<p class=a>❧
<h2 id=further-reading>Leitura complementar</h2>
<p><abbr>HTML5</abbr> storage:
<ul>
<li><a href="http://dev.w3.org/html5/webstorage/">Especificação de Armazenamento <abbr>HTML5</abbr></a>
<li><a href="http://msdn.microsoft.com/en-us/library/cc197062(VS.85).aspx">Introdução ao DOM Storage</a> no <abbr>MSDN</abbr>
<li><a href="http://dev.opera.com/articles/view/web-storage/">Web Storage: armazenamento de dados no lado do cliente mais fácil e mais poderoso powerful</a> em Opera Developer Community
<li><a href="https://developer.mozilla.org/en/dom/storage">DOM Storage</a> no Mozilla Developer Center. (Nota: a maior parte dessa página é devota ao protótipo da implementação do Firefox’ do objeto <code>globalStorage</code>, um não-padrão precursor do <code>localStorage</code>. Mozilla adicionou suporte ao padrão <code>localStorage</code> no Firefox 3.5.)
<li><a href="http://www.ibm.com/developerworks/xml/library/x-html5mobile2/">Desabilite local storage para aplicações web mobile com HTML5</a>, um tutorial na IBM DeveloperWorks
</ul>
<p>Trabalho prévio de Brad Neuberg <i>et. al.</i> (pré-<abbr>HTML5</abbr>):
<ul>
<li><a href="http://codinginparadise.org/weblog/2005/08/ajax-internet-explorer-has-native.html">Internet Explorer possui suporte nativo para persistência?!?!</a> (sobre o objeto <code>userData</code> do IE)
<li><a href="http://docs.google.com/View?docid=dhkhksk4_8gdp9gr#dojo_storage">Dojo Storage</a>, parte de um tutorial maior sobre a (agora não continuada) biblioteca Dojo Offline
<li><a href="http://api.dojotoolkit.org/jsdoc/HEAD/dojox.storage.manager"><code>dojox.storage.manager</code> referência da <abbr>API</abbr></a>
<li><a href="http://svn.dojotoolkit.org/src/dojox/trunk/storage/">dojox.storage</a> repositório Subversion
</ul>
<p>Web SQL Database:
<ul>
<li><a href="http://dev.w3.org/html5/webdatabase/">Especificação Web SQL Database</a>
<li><a href="http://html5doctor.com/introducing-web-sql-databases/">Introdução à Web <abbr>SQL</abbr> Databases</a>
<li><a href="http://html5demos.com/database">Demonstração de Web Database</a>
<lI><a href="http://zef.me/2774/persistence-js-an-asynchronous-javascript-orm-for-html5gears">persistence.js</a>, um “<abbr>ORM</abbr> assíncrono em JavaScript” construído sob Web <abbr>SQL</abbr> Database e Gears
</ul>
<p>IndexedDB:
<ul>
<li><a href="http://dev.w3.org/2006/webapi/IndexedDB/">Especificação do Indexed Database <abbr>API</abbr></a>
<li><a href="http://hacks.mozilla.org/2010/06/beyond-html5-database-apis-and-the-road-to-indexeddb/">Além de HTML5: Database APIs e o caminho para IndexedDB</a>
<li><a href="http://hacks.mozilla.org/2010/06/comparing-indexeddb-and-webdatabase/">Firefox 4: Uma prévia caminhada por IndexedDB</a>
</ul>
<p class=a>❧
<p>Isso foi “O Passado, Presente & Futuro de Armazenamento Local para Aplicações Web.” Consulte o <a href=table-of-contents.html>Sumário</a>, caso queira continuar com a leitura.
<div class="pf">
<h4>Você sabia?</h4>
<div class="moneybags">
<blockquote><p>Em associação a <span lang="en">Google Press</span>, O’Reilly está distribuindo este livro em variados formatos, incluindo papel, ePub, Mobi, <abbr>DRM</abbr>-free e <abbr>PDF</abbr>. A edição paga é chamada <span lang="en">“HTML5: Up & Running”</span> e está disponível agora. Este capítulo está incluído na versão paga.
</p><p>Se você gostou deste capítulo e quer mostrar sua apreciação, basta <a href="http://www.amazon.com/HTML5-Up-Running-Mark-Pilgrim/dp/0596806027?ie=UTF8&tag=diveintomark-20&creativeASIN=0596806027">comprar o livro “<abbr>HTML5</abbr>: Up & Running” com esse link afiliado</a> ou <a href="http://oreilly.com/catalog/9780596806033">comprar a edição eletrônica diretamente da O’Reilly</a>. Você vai ganhar um livro, e eu vou ganhar um trocado. Atualmente, não aceito doações diretas.
</p></blockquote>
</div>
</div>
<p class=c>Copyright MMIX–MMXI <a href=about.html>Mark Pilgrim</a>
<form action=https://www.google.com/cse><div><input type=hidden name=cx value=014437924039265546826:2nmoshc8y3y><input type=hidden name=ie value=UTF-8><input type=search name=q size=25 placeholder="powered by Google™"> <input type=submit name=sa value=Pesquisar></div></form>
<script src=j/jquery.js></script>
<script src=j/modernizr.js></script>
<script src=j/dih5.js></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-4114546-27', 'auto');
ga('send', 'pageview');
</script>
</html>