forked from zenorocha/diveintohtml5
-
Notifications
You must be signed in to change notification settings - Fork 6
/
storage.html
930 lines (893 loc) · 39.9 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
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
---
title: Local Storage - Dive Into HTML5
---
<!DOCTYPE html>
<html
lang="pt-br"
class=" js flexbox canvas canvastext webgl no-touch geolocation postmessage websqldatabase no-indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients cssreflections csstransforms csstransforms3d csstransitions fontface video audio localstorage sessionstorage webworkers applicationcache svg inlinesvg smil svgclippaths"
>
<head>
<meta charset="utf-8" />
<title>{{ page.title }}</title>
<!--[if lt IE 9]><script src=j/html5.js></script><![endif]-->
<link
rel="alternate"
type="application/atom+xml"
href="https://github.com/webfatorial/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" />
</head>
<body cz-shortcut-listen="true">
{% include fork.html %}
<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>
</p>
<h1>
<br />O Passado, Presente & Futuro de Armazenamento Local para
Aplicações Web
</h1>
<p id="toc"><a href="javascript:showTOC()">exibir índice analítico</a></p>
<p class="a">❧</p>
<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>
<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]:
</p>
<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>
<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-->); </abbr>
</li>
<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;
</li>
</ul>
<p>O que nós realmente queremos é</p>
<ul>
<li>bastante espaço de armazenamento</li>
<li>no cliente</li>
<li>que persista depois da atualização da página</li>
<li>e não seja transmitido para o servidor</li>
</ul>
<p>
Antes da <abbr>HTML5</abbr>, todas as tentativas de obtenção foram
fundamentalmente insatisfatórias de diferentes maneiras.
</p>
<p class="a">❧</p>
<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>
<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>
<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>
<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>
<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>
<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>
<p class="a">❧</p>
<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>
<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>
<p>
Quais navegadores? Bom, as últimas versões de quase todos os navegadores
suportam <abbr>HTML5</abbr> Storage… até o Internet Explorer!
</p>
<table class="bc">
<caption>
Suporte ao
<abbr>HTML5</abbr>
Storage
</caption>
<thead>
<tr>
<th title="Internet Explorer">IE</th>
<th title="Mozilla Firefox">Firefox</th>
<th title="Apple Safari">Safari</th>
<th title="Google Chrome">Chrome</th>
<th>Opera</th>
<th>iPhone</th>
<th>Android</th>
</tr>
</thead>
<tbody>
<tr>
<td>8.0+</td>
<td>3.5+</td>
<td>4.0+</td>
<td>4.0+</td>
<td>10.5+</td>
<td>2.0+</td>
<td>2.0+</td>
</tr>
</tbody>
</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>
<p class="legend top" style="padding-left:6em">
<span class="arrow">↶</span> Verificando suporte
<abbr>HTML5</abbr> Storage
</p>
<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.
</p>
<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">❧</p>
<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.
</p>
<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>
<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:
</p>
<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:</p>
<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).
</p>
<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>
<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).
</p>
<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>.
</p>
<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>
<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.)
</p>
<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>.
</p>
<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:
</p>
<table class="st">
<caption>
Objeto StorageEvent
</caption>
<thead>
<tr class="ho">
<th>Propriedade</th>
<th>Tipo</th>
<th>Descrição</th>
</tr>
</thead>
<tbody>
<tr class="zebra">
<th><code>key</code></th>
<td>string</td>
<td>o nome da chave que foi adicionada, removida ou alterada</td>
</tr>
<tr>
<th><code>oldValue</code></th>
<td>qualquer</td>
<td>
o valor antigo (agora atualizado), ou <code>null</code> se for um
novo item adicionado
</td>
</tr>
<tr class="zebra">
<th><code>newValue</code></th>
<td>qualquer</td>
<td>o novo valor, ou <code>null</code> se um item foi removido</td>
</tr>
<tr>
<th><code>url</code><sup>*</sup></th>
<td>string</td>
<td>a página que chamou o método que realizou a mudança</td>
</tr>
</tbody>
<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.
</td>
</tr>
</tfoot>
</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.”
</p>
<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>
<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>
<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>
<p class="a">❧</p>
<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>
<p>
Como isto funciona? Cada momento que uma mudança acontece dentro do jogo,
chamamos esta função:
</p>
<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>
<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>.
</p>
<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:
</p>
<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:
</p>
<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:
</p>
<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>:
</p>
<pre><code>gMoveCount = <mark>parseInt</mark>(localStorage["halma.movecount"]);</code></pre>
<p class="a">❧</p>
<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>
<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>
<p class="legend top" style="padding-left:3em">
<span class="arrow">↶</span>código atual funcionando em 4 navegadores
</p>
<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>
<p>
A especificação da Base de Dados SQL vem sendo implementada por quatro
navegadores e plataformas.
</p>
<table class="bc">
<caption>
Suporte a Base de Dados SQL Web
</caption>
<thead>
<tr>
<th title="Internet Explorer">IE</th>
<th title="Mozilla Firefox">Firefox</th>
<th title="Apple Safari">Safari</th>
<th title="Google Chrome">Chrome</th>
<th>Opera</th>
<th>iPhone</th>
<th>Android</th>
</tr>
</thead>
<tbody>
<tr>
<td>·</td>
<td>·</td>
<td>4.0+</td>
<td>4.0+</td>
<td>10.5+</td>
<td>3.0+</td>
<td>2.0+</td>
</tr>
</tbody>
</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>
<p>
Tudo o que nos leva ao seguinte aviso, atualmente residindo no topo da
especificação da Base de Dados SQL Web:
</p>
<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.
</p>
</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>
<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>
<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>
<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>
<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>
<p class="a">❧</p>
<h2 id="further-reading">Leitura complementar</h2>
<p><abbr>HTML5</abbr> storage:</p>
<ul>
<li>
<a href="http://dev.w3.org/html5/webstorage/"
>Especificação de Armazenamento <abbr>HTML5</abbr></a
>
</li>
<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>
<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>
<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>
<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
</li>
</ul>
<p>
Trabalho prévio de Brad Neuberg <i>et. al.</i> (pré-<abbr>HTML5</abbr>):
</p>
<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>
<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>
<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>
<li>
<a href="http://svn.dojotoolkit.org/src/dojox/trunk/storage/"
>dojox.storage</a
>
repositório Subversion
</li>
</ul>
<p>Web SQL Database:</p>
<ul>
<li>
<a href="http://dev.w3.org/html5/webdatabase/"
>Especificação Web SQL Database</a
>
</li>
<li>
<a href="http://html5doctor.com/introducing-web-sql-databases/"
>Introdução à Web <abbr>SQL</abbr> Databases</a
>
</li>
<li>
<a href="http://html5demos.com/database"
>Demonstração de Web Database</a
>
</li>
<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
</li>
</ul>
<p>IndexedDB:</p>
<ul>
<li>
<a href="http://dev.w3.org/2006/webapi/IndexedDB/"
>Especificação do Indexed Database <abbr>API</abbr></a
>
</li>
<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>
<li>
<a
href="http://hacks.mozilla.org/2010/06/comparing-indexeddb-and-webdatabase/"
>Firefox 4: Uma prévia caminhada por IndexedDB</a
>
</li>
</ul>
<p class="a">❧</p>
<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.
</p>
<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>
{% include copyright.html %}
{% include google-search.html %}
<script src="j/jquery.js"></script>
<script src="j/modernizr.js"></script>
<script src="j/dih5.js"></script>
{% include google-analytics.html %}
</body>
</html>