-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.html
529 lines (477 loc) · 17.8 KB
/
index.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
<!DOCTYPE html>
<html lang="en">
<head>
<title>Offline Storage for Progressive Web Applications</title>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="node_modules/shower-ribbon/styles/screen-16x10.css">
</head>
<body class="shower full">
<header class="caption">
<h1>Offline Storage for Progressive Web Applications</h1>
<p>Majid Hajian</p>
</header>
<section class="slide" id="cover">
<h2>Offline Storage for <br/>Progressive Web Applications</h2>
<p class="small">Dynamic Data</p>
<figure>
<img class="cover" src="pictures/pwa-sl1.jpg" alt="User working beside Ocean">
<figcaption class="white">
<a href="http://www.twitter.com/mhadaily">© Majid Hajian</a>
</figcaption>
</figure>
<style>
#cover h2 {
margin:30px 0 0;
color:#FFF;
text-align:center;
font-size:70px;
}
#cover p {
margin:10px 0 0;
text-align:center;
color:#FFF;
font-style:italic;
font-size:20px;
}
#cover p a {
color:#FFF;
}
#cover .cover{
width: 100%;
}
.center {
text-align: center
}
.center h2{
text-align: center
}
</style>
</section>
<section class="slide grid">
<h2>Majid Hajian</h2>
<div class="double">
<ol>
<li>Software Developer</li>
<li>Passionate about Front-end</li>
<li>Love building good application</li>
<li>Open Source Enthusiast </li>
<li>Working Glimmer HN PWA 💯, <br>Contibutor to Angular, Vue.js and Ember.js HN PWA</li>
</ol>
<img style="max-width:100%" src="pictures/mine.jpg">
</div>
<p class="center" style="margin-top:-5px; color:brown">
Find me on Gmail / Twitter / Github / Stackoverflow / Linkedin / Medium
</p>
<h2 class="center" style="margin-top:-30px">@mhadaily</h3>
</section>
<section class="slide center">
<h2 class="center">Why Offline Storage?</h2>
<div class="next center">
<img src="pictures/magic.gif" width="70%">
</div>
</section>
<section class="slide center">
<h2>Keep Organized!</h2>
<img src="pictures/organized.gif" width="60%">
<h2>...instant retrieval of structured data</h2>
</section>
<section class="slide center">
<h2 class="next">Performance!</h2>
<video width="420" height="340" autoplay loop>
<source src="pictures/loading.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
</section>
<section class="slide center">
<div class="double">
<h2 class="next">Reduce network <br/>requests
<br><br>
<span style="font-size:50%;"> unneeded data download</span>
</h2>
<img src="pictures/traffic.jpg" width="100%">
</div>
</section>
<section class="slide center">
<h2>...mobile data is not cheap!</h2>
<img src="pictures/cuba.jpg" width="70%">
</section>
<section class="slide center">
<h2>Server Outage</h2>
<img src="pictures/so.jpg" width="70%">
</section>
<section class="slide">
<h2>Ok, What are the options?</h2>
<ol>
<li>Static Assets
<ul>
<li>Application Cache (Part of Service Workers) "cache-manifest"</li>
</ul>
</li>
<li>Dynamic Content <span class="next" style="color:red">✔</span>
<ul>
<li>Web Storage APIs</li>
<li>Web SQL Database</li>
<li>IndexedDB</li>
<li>File System APIs</li>
<li>File APIs</li>
<li>Wrappers & Polyfills!</li>
</ul>
</li>
</ol>
</section>
<section class="slide center">
<h2>Goal</h2>
<img src="pictures/offline-arch.jpg" width="100%">
</section>
<section class="slide center">
<br><br><br><br>
<h2 class="shout shrink">‘Offlining’ <br>Dynamic Data</h2>
</section>
<section class="slide">
<h2>Web Storage / localStorage & sessionStorage</h2>
<ul class="double">
<li>Pros
<ul>
<li>Key / value(strings) pairs</li>
<li>Persistent on page reloads</li>
<li>Avoids HTTP overhead of cookies</li>
<li>Great for storing user preferences</li>
</ul>
</li>
<li class="next">Cons
<ul>
<li>5 MB (but really only 2.5 MB)</li>
<li>Performance (Synchronous)</li>
<li>No Support for Web Worker</li>
</ul>
</li>
</ul>
</section>
<section class="slide">
<h2>Web Storage Compatibility</h2>
<img src="pictures/webstorage.jpg" width="100%">
</section>
<section class="slide">
<h2>Web Storage Sample Code</h2>
<pre>
<code><mark><script></mark></code>
<code>localStorage.<mark class="important">setItem</mark>('User',JSON.stringify({fn:'MJ',id:10}));</code>
<code>JSON.parse(localStorage.<mark class="important">getItem</mark>('User')); <span class="comment">// Object</span></code>
<code>localStorage.removeItem('User');</code>
<code>localStorage.length <span class="comment">// num of items stored</span></code>
<code>localStorage.key(0); <span class="comment">// 'User'</span></code>
<code>localStorage.clear();</code>
<code><mark><script></mark></code>
</pre>
</section>
<section class="slide">
<h2>WebSQL</h2>
<ul class="double">
<li>Pros
<ul>
<li>Basically wrapper around SQLite</li>
<li>asynchronous(Callback)</li>
<li>Transaction-oriented</li>
<li>Put processing (sorting, filtering, etc.) on client</li>
</ul>
</li>
<li class="next">Cons
<ul>
<li>~50MB limit of data storage</li>
<li>No support for Web Worker</li>
<li>Depreciated!!
<img src="pictures/websql.jpg" width="100%"/>
</li>
</ul>
</li>
</ul>
</section>
<section class="slide">
<style>
pre.small code{
font-size:70%
}
</style>
<pre class="small">
<code><mark><script></mark> </code>
<code>var db = openDatabase('myDB', '1.0', 'Web SQL DB', 2 * 1024 * 1024, function(db){});</code>
<code> db.transaction(function (tx) {</code>
<code> tx.executeSql("CREATE TABLE IF NOT EXISTS..." <span class="comment">// SQL statement.</span></code>
<code> [], <span class="comment">// Parameter array.</span></code>
<code> function onTXSuccess(transaction, results) { ... }, <span class="comment">//opt_successCallback</span></code>
<code> function onTXError(transaction, error) { ... } <span class="comment">//opt_errorCallback</span></code>
<code> );</code>
<code> });</code>
<code class="mark"> var taskText = 'buy groceries';</code>
<code class="mark"> tx.executeSql(</code>
<code class="mark"> <mark class="important">'INSERT INTO todo(task, added_on) VALUES (?,?)', [taskText, new Date()], </mark></code>
<code class="mark"> renderFunc, onError);</code>
<code><mark></script></mark></code>
</pre>
</section>
<section class="slide">
<h2>Web SQL Compatibility</h2>
<img src="pictures/websql-cover.jpg" width="100%">
</section>
<section class="slide">
<h2>IndexedDB</h2>
<ul class="double">
<li>Pros (NoSQLDB)
<ul>
<li>Object based data store</li>
<li>“No limit”, can ask user for more space</li>
<li>For the browser and for Web Workers</li>
<li>Asynchronous API</li>
<li>Transaction-oriented</li>
</ul>
</li>
<li class="next">Cons
<ul>
<li>Relatively Complex API</li>
<li>Buggy Implementation in Safari < v10.1!</li>
</ul>
</li>
</ul>
<p class="note">Indexed Database API 2.0 is almost there https://hacks.mozilla.org/2016/10/whats-new-in-indexeddb-2-0/</p>
</section>
<section class="slide">
<h2>IndexedDB Compatibility</h2>
<img src="pictures/indexedb-cov.jpg" width="100%">
</section>
<section class="slide">
<style>
pre.smaller code{
font-size:50%
}
</style>
<pre class="smaller">
<code><mark><script></mark></code>
<code>var open = indexedDB.open("MyDatabase", 1);</code>
<code>open.onupgradeneeded = function() { <span class="comment">// Create the schema</span></code>
<code> var db = open.result;</code>
<code> var store = db.createObjectStore("MyObjectStore", {keyPath: "id"});</code>
<code> var index = store.createIndex("NameIndex", ["name.last", "name.first"]);</code>
<code>};</code>
<code>open.onsuccess = function() {</code>
<code> var db = open.result; <span class="comment">// Start a new transaction</span></code>
<code> var tx = db.transaction("MyObjectStore", "readwrite");</code>
<code> var store = tx.objectStore("MyObjectStore");</code>
<code> var index = store.index("NameIndex");</code>
<code> <mark>store.put({id: 1, name: {first: "Majid", last: "Hajian"}, age: 30}); </mark><span class="comment">// Add some data</span></code>
<code class="mark"> var getMajid = store.get(1); <span class="comment">// Query the data</span></code>
<code class="mark"> getMajid.onsuccess = function() { console.log(getMajid.result.name.first);}; <span class="comment">// => "Majid"</span></code>
<code> tx.oncomplete = function() { <span class="comment">// Close the db when the transaction is done</span></code>
<code> db.close();</code>
<code> };</code>
<code>}</code>
<code><mark></script></mark></code>
</pre>
</section>
<section class="slide">
<h2>File System API (Only Chrome) / File API </h2>
<style>
.smLi{font-size:70%}
</style>
<ul class="double">
<li>Focus:
<ul>
<li>Asynchronous (callback-based)</li>
<li>For the browser and for Web Workers</li>
<li>Large files</li>
<li>Binary content</li>
</ul>
</li>
<li>Source
<ul>
<li class="smLi">https://w3c.github.io/FileAPI/</li>
<li class="smLi">https://www.w3.org/TR/file-system-api/</li>
<li class="smLi">https://github.com/WICG/writable-files</li>
<li class="smLi">https://wicg.github.io/entries-api/</li>
<li class="smLi">https://github.com/eligrey/FileSaver.js</li>
</ul>
</li>
</ul>
<p class="note">Just keep an eye on them but they are not going to be in place soon!</p>
</section>
<section class="slide center">
<h2>Goal</h2>
<img src="pictures/offline-arch.jpg" width="100%">
</section>
<section class="slide">
<h2>There is a problem?</h2>
<div class="next center">
<img src="pictures/browsers.jpg" width="100%">
</div>
</section>
<section class="slide">
<h2>Wrappers, Polyfills and New Options</h2>
<ul>
<li class="next"><a href="https://github.com/localForage/localForage">localForage</a> (~8KB, promises,good legacy browser support)</li>
<li class="next"><a href="https://www.npmjs.com/package/idb-keyval">IDB-keyval</a> (500 byte alternative to localForage, for modern browsers, promise-based)</li>
<li class="next"><a href="https://www.npmjs.com/package/idb">IDB-promised</a> (~2k, same IndexedDB API, but with promises)</li>
<li class="next"><a href="http://dexie.org/">Dexie</a> (~16KB, promises, complex queries, secondary indices, FP style)</li>
<li class="next"><a href="https://pouchdb.com/">PouchDB</a> (~45KB (supports <a href="https://pouchdb.com/2016/06/06/introducing-pouchdb-custom-builds.html">custombuilds</a>),synchronization)</li>
<li class="next"><a href="https://github.com/google/lovefield">Lovefield From Google</a> (SQL-like APIs, modern browsers support)</li>
<li class="next"><a href="http://lokijs.org/#/">LokiJS</a> (in-memory, Monogodb-like api or FP style,)</li>
<li class="next"i><a href="https://github.com/yathit/ydn-db">ydn-db</a> (dexie-like, Good Supported browsers, supporting version migration, advanced query, SQL and transaction.)</li>
</ul>
</section>
<section class="slide">
<h2>Data Synchronization</h2>
<ol>
<li><a href="https://github.com/localForage/localForage">localForage</a> (~8KB, promises,good legacy browser support)</li>
<li><a href="https://pouchdb.com/">PouchDB</a> (~45KB, synchronization, couchDB)</li>
<li><a href="http://lokijs.org/#/">LokiJS</a> (in-memory)</li>
</ol>
</section>
<section class="slide">
<h2>LocalForage</h2>
<ul class="double">
<li>Lightweight ~8KB gzip</li>
<li>localStorage-like API</li>
<li>Callback and Promise</li>
<li>Legacy browsers</li>
<li>Native JS objects,serialized to JSON, ArrayBuffers, Blobs, and TypedArrays.</li>
<li>TypeScript</li>
<li>Front-end Frameworks</li>
</ul>
<div class="double">
<img src='pictures/localforage.jpg' width="100%">
</div>
</section>
<section class="slide">
<h2>LocalForage Sample Code</h2>
<pre>
<code>npm install localforage</code>
<code>localforage.<mark class="important">setItem</mark>('key', 'value', function (err) {</code>
<code><span class="comment">// if err is non-null, we got an error</span></code>
<code>localforage.<mark class="important">getItem</mark>('key', function (err, value) {</code>
<code><span class="comment">// if err is non-null, we got an error. otherwise, value is the value</span></code>
<code>});</code>
<code>});</code>
</pre>
</section>
<section class="slide">
<h2>PouchDB</h2>
<ul class="double">
<li>~46KB (gzipped)</li>
<li>Basically a CoudDb in Browser</li>
<li>Easy to sync</li>
<li>Well-supported browsers</li>
<li>underhood IndexedDB and fallback to websql</li>
<li>Cordova/PhoneGap, NW.js, Electron, and Chrome apps</li>
<li>Angular, React, Ember, Backbone, Vue.js and ...</li>
<li>Fully support ES6 and ES7 (Async/Await) and es5-shim for legacy environment</li>
<li>LocalStorage and in-memory adapters</li>
<li>Node.js support (LevelDB)</li>
</ul>
</section>
<section class="slide">
<style>
.evensmaller{
font-size:30%
}
</style>
<pre class="evensmaller">
<code>const db = new PouchDB('customers');</code>
<code>db.changes({ continuous: true, onChange: updateDOM });</code>
<code>function updateDOM() {</code>
<code class="mark"> db.allDocs({ include_docs: true, descending: false}, </code>
<code class="mark"> (err, doc) => doc.rows.map(row => customersList.push(row.doc));</code>
<code>}</code>
<code>const CustomerModel = () => {</code>
<code> this.addCustomer = function () {</code>
<code> const customer = {</code>
<code> _id: new Date().toISOString(),</code>
<code> firstname: "", lastname: "", email: "",</code>
<code> };</code>
<code> db.put(customer, callback(err, result) => {</code>
<code> if (err) return console.log(err);</code>
<code> console.log('Customer added.');</code>
<code> });</code>
<code> };</code>
<code> this.updateCustomer = (customer) => {</code>
<code> db.put(customer, callback(err, result) => {</code>
<code> if (err) return console.log(err);</code>
<code> console.log('Customer added.');</code>
<code> });</code>
<code> };</code>
<code> this.removeCustomer = (customer) => {</code>
<code> db.remove(customer);</code>
<code> };</code>
<code> this.syncDB = function () {</code>
<code> <mark class="important"> remoteCouchDB = 'http://yourcouchdbserver.com/customers';</mark></code>
<code class="mark"> const options = { complete: syncDBCompleted };</code>
<code class="mark"> db.replicate.to(remoteCouchDB, options);</code>
<code class="mark"> db.replicate.from(remoteCouchDB, options);</code>
<code> }};</code>
<code>function syncDBCompleted() { console.log('Synchronization completed!'); }</code>
<code>updateDOM();</code>
</pre>
</section>
<section class="slide">
<h2>Lokijs</h2>
<ul class="double">
<li>Fully written in Javascript</li>
<li>Node.js and Browser</li>
<li>Document-oriented, nosql-style</li>
<li>JSON, dump to everywhere!</li>
<li>in-memory, fast</li>
<li>~15kb minified gizp</li>
<li>http://obeliskos.com/LokiSandbox/</li>
<img width="100%" src="pictures/lokijs.jpg">
</ul>
</section>
<selection class="slide">
<pre class="smaller">
<code>var lfAdapter = new LokiLocalForageAdapter("lokidb");</code>
<code>var db = new loki('databaseName',{ <span class="comment">// autoload: true, // autoloadCallback : lokidb_loadHandler,</code>
<code> //autosaveInterval: 10000,</span> autosave: true,</code>
<code> adapter: lfAdapter<span class="comment"> // <- required</span>});</code>
<code>var users = db.addCollection('users'); <span class="comment">// Add a collection to the database</span></code>
<code><span class="comment">// Add some documents to the collection</span></code>
<code class="mark">users.insert([{ name : 'user1', owner: 'fuser1', age: '30' },</code>
<code class="mark"> { name : 'user2', owner: 'fuser2', age: '40' },</code>
<code class="mark"> { name : 'user3', owner: 'fuser3', age: '50' },</code>
<code class="mark"> { name : 'user4', owner: 'fuser4', age: '60' }]);</code>
<code class="mark"><span class="comment">// Find and update an existing document</span></code>
<code>var user3 = users.findOne({'name': 'user3'});</code>
<code>user3.owner = 'Majid'; <mark class="important">users.update(user3);</mark></code>
<code>var results = users.where(obj => obj.age >= 35}); <span class="comment">// Query</span></code>
<code>var resultsFPstyle = users.chain()</code>
<code> .find({ age: {'$gte': 35} })</code>
<code> .simplesort('name')</code>
<code> .data();</code>
<code>db.loadDatabase({}, function() {</code>
<code>lokidb_loadHandler(); <span class="comment">// <- load any collections in here</span></code>
<code>});</code>
</pre>
</selection>
<section class="slide grid center">
<h2>There are still more options!</h2>
<video width="420" height="340" autoplay loop>
<source src="pictures/noo.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
</section>
<section class="slide center">
<br>
<h3>Slides and Sample codes are availabe on Github</h3>
<h3>https://goo.gl/T0HhIB</h3>
<h3>https://github.com/mhadaily/pwa-offline-storage</h3>
<br>
<h3><img src="pictures/t.svg" width="30px"> @mhadaily</h3>
<h2>Thank you</h2>
</section>
<footer class="badge">
<a href="https://github.com/mhadaily/pwa-offline-storage">Fork me on GitHub</a>
</footer>
<div class="progress"></div>
<script src="node_modules/shower-core/shower.min.js"></script>
<script src="js/localforage.min.js"></script>
<script src="lokijs/lokijs.min.js"></script>
<script src="js/loki-localforage-adapter.js"></script>
</body>
</html>