Download as KEY, PPTX
IndexedDB Mike West @mikewest, mkwst@google.com SV GTUG, 2010年12月14日
A IndexedDB T ] [ B E Mike West @mikewest, mkwst@google.com
Offline
Storage Options
Cookies http://www.flickr.com/photos/ilmungo/65345233/in/photostream/
• Simple, key/value pairs, Cookies "shared" between server and client. • Excellent for maintaining state, poor for anything else, as they are unstructured, and incur a significant overhead for each HTTP request.
Local Storage http://www.flickr.com/photos/photohome_uk/1494590209/
Local Storage • The simplicity of cookies, tuned for higher-capacity, client-side-only storage. • Dead simple API: localStorage.setItem( ‘key’, ‘value’ ); localStorage.getItem( ‘key’ ); // ‘value’ • Values are unstructured strings: • Filtering and search are O(n), unless you layer some indexing on top. • Structure requires JSON.stringify & JSON.parse
WebSQL http://www.flickr.com/photos/nickperez/2569423078/
WebSQL • A real, relational database implementation on the client (SQLite) • Data can be highly structured, and JOIN enables quick, ad-hoc access • Big conceptual overhead (SQL), no finely grained locking • Not very JavaScripty, browser support is poor, and the spec has been more or less abandoned.
File API http://www.flickr.com/photos/daddo83/3406962115/
File API ?
IndexedDB http://www.flickr.com/photos/31408547@N06/4671916278/
IndexedDB • Sits somewhere between full-on SQL and unstructured key-value pairs in localStorage. • Values are stored as structured JavaScript objects, and an indexing system facilitates filtering and lookup. • Asynchronous, with moderately granular locking • Joining normalized data is a completely manual process.
IndexedDB Concepts
Practically everything is asynchronous. Callbacks are your friends.
Databases are named, and contain one or more named Object Stores
contacts
Object Stores define a property which every stored object must contain, explicitly or implicitly.
contacts 1 2 3 4 5 6 7 8 9
Values in an Object Store are structured, but don’t have a rigidly defined schema.
contacts 1 2 3 4 5 6 7 8 9
Object Stores can contain one or more Indexes that make filtering and lookup possible via arbitrary properties.
contacts 1 2 3 4 5 6 7 8 9
IndexedDB API
T A ] [ B E IndexedDB API
Vendor prefixes: webkitIndexedDB & moz_indexedDB
// Deal with vendor prefixes if ( "webkitIndexedDB" in window ) { window.indexedDB = window.webkitIndexedDB; window.IDBTransaction = window.webkitIDBTransaction; window.IDBKeyRange = window.webkitIDBKeyRange; // ... } else if ( "moz_indexedDB" in window ) { window.indexedDB = window.moz_indexedDB; } if ( !window.indexedDB ) { // Browser doesn’t support indexedDB, do something // clever, and then exit early. }
Database creation is asynchronous
var dbRequest = window.indexedDB.open( "AddressBook", // Database ID "All my friends ever" // Database Description ); // The result of `open` is _not_ the database. // It’s a reference to the request to open // the database. Listen for its `success` and // `error` events, and respond appropriately. dbRequest.onsuccess = function ( e ) { ... }; dbRequest.onerror = function ( e ) { ... };
Databases are versioned...
// The `result` attribute of the `success` event // holds the communication channel to the database dbRequest.onsuccess = function ( e ) { var db = e.result; // Bootstrapping: if the user is hitting the page // for the first time, she won’t have a database. // We can detect this by inspecting the database’s // `version` attribute: if ( db.version === "" ) { // Empty string means the database hasn’t been versioned. // Set up the database by creating any necessary // Object Stores, and populating them with data // for the first run experience. } else if ( db.version === "1.0" ) { // 1.0 is old! Let’s make changes! } else if ( db.version === "1.1" ) { // We’re good to go! } // ... };
... and versioning is asynchronous.
dbRequest.onsuccess = function ( e ) { var db = e.result; if ( db.version === "" ) { // We’re dealing with an unversioned DB. Versioning is, of // course, asynchronous: var versionRequest = db.setVersion( "1.0" ); versionRequest.onsuccess = function ( e ) { // Here’s where we’ll set up the Object Stores // and Indexes. }; } // ... };
Creating Object Stores and Indexes
dbRequest.onsuccess = function ( e ) { var db = e.result; if ( db.version === "" ) { var versionRequest = db.setVersion( "1.0" ); // Setting a version creates an implicit Transaction, meaning // that either _everything_ in the callback succeeds, or // _everything_ in the callback fails. versionRequest.onsuccess = function ( e ) { // Object Store creation is atomic, but can only take // place inside version-changing transaction. var store = db.createObjectStore( "contacts", // The Object Store’s name "id", // The name of the property to use as a key true // Is the key auto-incrementing? ); // ... }; } // ... };
dbRequest.onsuccess = function ( e ) { var db = e.result; if ( db.version === "" ) { var versionRequest = db.setVersion( "1.0" ); versionRequest.onsuccess = function ( e ) { var store = db.createObjectStore( "contacts", "id", true ); store.createIndex( "CellPhone", // The index’s name "cell", // The property to be indexed false // Is this index a unique constraint? ); }; } // ... };
Writing Data (is asynchronous)
// Assuming that `db` has been set somewhere in the current // scope, we use it to create a transaction: var writeTransaction = db.transaction( [ "contacts" ], // The Object Stores to lock IDBTransation.READ_WRITE // Lock type (READ_ONLY, READ_WRITE) ); // Open a contact store... var store = writeTransaction.objectStore( "contacts" ); // ... and generate a write request: var writeRequest = store.add( { "name": "Mike West", "email": "mkwst@google.com" } ); writeRequest.onerror = function ( e ) { writeTransaction.abort(); }; // Transactions are "complete" (not "committed"?) either when // they fall out of scope, or when all activities in the // transaction have finished (whichever happens last) writeTransaction.oncomplete = function ( e ) { ... };
Reading Data (is asynchronous)
// Assuming that `db` has been set somewhere in the current // scope, we use it to create a transaction: var readTransaction = db.transaction( [ "contacts" ], // The Object Stores to lock IDBTransation.READ_ONLY // Lock type (READ_ONLY, READ_WRITE) ); // Open the `contact` store... var store = readTransaction.objectStore( "contacts" ); // ... and generate a cursor to walk the complete list: var readCursor = store.openCursor(); // Setup a handler for the cursor’s `success` event: readCursor.onsuccess = function ( e ) { if ( e.result ) { // You now have access to the key via `e.result.key`, and // the stored object via `e.result.value`. For example: console.log( e.result.value.email ); // mkwst@google.com } else { // If the `success` event’s `result` is null, you’ve reached // the end of the cursor’s list. } };
Querying (is asynchronous)
var t = db.transaction( [ "contacts" ], IDBTransation.READ_ONLY ); var s = t.objectStore( "contacts" ); // ... and generate a cursor to walk a bounded list, for example // only those names between M and P (inclusive) var bounds = new IDBKeyRange.bound( "M", // Lower bound "Q", // Upper bound true, // Include lower bound? false // Include upper bound? ); var readCursor = store.openCursor( bounds ); // Setup a handler for the cursor’s `success` event: readCursor.onsuccess = function ( e ) { // process `e.result` };
Further Reading The IndexedDB Spec: http://www.w3.org/TR/IndexedDB/ "Firefox 4: An Early Walkthrough of IndexedDB": http://hacks.mozilla.org/2010/06/comparing- indexeddb-and-webdatabase/ Mozilla Developer Docs: https://developer.mozilla.org/en/IndexedDB
Questions? Mike West @mikewest mkwst@google.com http://mikewest.org/