Note: this presentation was written for Gistdeck. Add the bookmarklet, come back to this gist, click the bookmarklet, then use the arrow keys to navigate.
Note2: See https://github.com/meagar/taking-javascript-offline for code examples; any time a string like 2-basic-caching
appears, that's a branch which supports that slide
Taking JavaScript Offline
Who am I?
@meagar
Matthew Eagar / Tech Lead / 500px
Case Study
Data Capture
- Survey consumers on the street
- Take photos, capture signatures
- On Windows, IOS, Android devices
- Sync to central data store (Rails)
- With no wifi or cell network
Solution?
Native App
Three Native Apps
PhoneGap
HTML5!
HTML5
- Appcache
- IndexedDB
- Location
- WebSockets
- FileReader
HTML5
Store site structure in appcache
RESTful API for data
Temporary storage in IndexedDB
It worked!
CODE BREAK
- Sample Backbone app
- Basic RESTful API
- JS + Coffee + EJS + Jade?
1-no-cache
Appcache
A mechanism for storing/accessing static assets offline
-
Driven by manifests
-
Define paths to cache
- JS/CSS/images/HTML
-
Full of gotchas
-
5mb limit
Appcache isn't...
A douche bag :(
Basic caching
manifests...
- Contain sections, paths, comments
- included via
<html manifest="...">
- Served with Content-type: text/appcache *
- Have a .appcache extension *
- start with CACHE MANIFEST
Basic caching
CACHE MANIFEST
CACHE:
# Static assets
/js/jquery.js
/js/app.js
/css/bootstrap.css
/css/styles.css
# Single-page backbone apps
/posts
/users
Basic caching
<!doctype html>
<html manifest="/my_cache.appcache">
<head>
<script src="/js/jquery.js"></script>
<!-- ... -->
More code!
Add a basic manifest
Hooray for Chrome!
2-basic-caching
Gotcha #1
Once appcache'd, content only comes from appcache
Gotcha #2
On cached pages, all requests go through Appcache
Gotcha #2
On cached pages, all requests go through Appcache
Gotcha #3
Appcache only updates if the manifest changes
Gotcha #4
Request that triggers an update still comes from appcache
Changes require two page reloads to become apparent
So...
On cached pages, all requests go through Appcache
So...
On cached pages, all requests go through Appcache
NETWORK sections
Appcache allows NETWORK sections to define content that shouldn't be cached
NETWORK sections
CACHE MANIFEST
CACHE:
# Single-page backbone apps
/posts
/users
NETWORK:
/api/posts
2-basic-cache
Tiny Gotcha #...5?
Implicit Caching
You can't NETWORK a page which has a manifest; it's cached implicitly
Going offline
Some content can't/shouldn't be cached
FALLBACK lets us provide defaults for use while offline
FALLBACK:
# Don't show user profiles while offline
/users/* /offline
Aside: Going offline
Our SPA can detect the presence of a network connection and render accordingly
navigator.onLine // true/false
window.addEventListener('online', fn);
window.addEventListener('offline', fn);
3-going-offline
Dealing with realistic network conditions
Networks are slow
The appcache doesn't like slow networks
4-waiting-on-appcache
Gotcha #6
Manifest downloads are brittle
- Entire manifest is redownloaded
- Upgrade is interruptable
- Users unaware that updates are happening
- Errors cancel entire update
Appcache events
Appache emits several useful events
Can tie into them to provide a GUI for updates
Appcache events
First load:
- checking
- downloading
- progress(, progress, ...)
- cached
Appcache events
Subsequent loads
- checking
- noupdate
- downloading
- progress(, progress, ...)
- cached
Building a dedicated updater!
More code!
- Detect cache downloading
- Show progress
4-waiting-on-appcache
Appcache Obsoletion
Allows programatic expiration of the entire cache
Still subject to the double-reload requirement
Forces browser into uncached state
5-obsoletion
Gotcha #lots
Obsolete caches!
Why obsoletion instead of updating?
Why obsoletion?
-
/login - online only, set is_authorized
-
/index - offline
- check is_authorized
- bounce to index
- check is_authorized
-
/login - change to auth_token
What should I appcache?
Static sites for mobile!
Web apps that server structure first, data later
Anything!
That's the appcache!
IndexedDB
IndexedDB
Very quickly...
- A key/value store
- larger than localStorage
- Size varies from browser-to-browser
- Prompts the user for more storage
IndexedDB
Very quickly...
- Doesn't warn you when it's full *
- Supports transactions, migrations, indices
- Faster than you might expect, slower than you might hope
IndexedDB
Behavies like REST:
Backbone.sync
IndexedDB
backbone-indexeddb.js
6-indexeddb