未加星标

“Instant Loading” with IndexedDB (Building a PWA, Part 2)

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二05 | 时间 2016 | 作者 红领巾 ] 0人收藏点击收藏

Last week's article was Part 1 (of 3) in my series on how I built a Progressive Web Application for this blog.


“Instant Loading” with IndexedDB (Building a PWA, Part 2)

Get the Application |View Source

As I mentioned, there are two parts to creating an "offline-first" application -

Cache the app shell so pages and assets are accessible offline Load locally-saved data first, then fetch updated data later if the network permits

Last week, I showed how I achieved the first using Service Worker to cache the app shell and serve it to the user even when they are offline. In this article, I will show how I used IndexedDB to save the fetched data for offline use. In the final part of this series next week, I will demonstrate how I implemented push notifications.

What is IndexedDB?

IndexedDB is a "low-level API for client-side storage of significant amounts of structured data" ( Mozilla ). It is a javascript-based, object-oriented, database that allows us to easily store and retrieve data that has been indexed with a key. IndexedDB can take some getting used to as it very low-level and works quite differently to other databases.

For the bitsofcode web app, I used IndexedDB to store the articles fetched from the blog RSS feed. This way, articles could be loaded instantly from the database first, before checking over the network for the more up-to-date data.

Creating a Database and Store

To start using IndexedDB, we need to create our database and the stores (which are like tables) within the database. To do that, we need to open up IndexedDB with the database name and version.

idb.open('DATABASE_NAME', VERSION, function(upgradeDb) { // Create stores });

To create stores in our database, we use the createObjectStore method of upgradeDb . When creating a store, we pass two arguments -

The name of the store An object with some optional settings. For example, the keyPath property, which allows us to specify what key in the object we want to be the primary key.

Here it is all together -

idb.open('DATABASE_NAME', VERSION, function(upgradeDb) { const store = upgradeDb.createObjectStore('STORE_NAME', { keyPath: 'PRIMARY_KEY_NAME' }); });

We can also define indexes for the stores. Indexes provide us alternative ways of accessing the data. For example, in a store we may have data that has, amongst others, a timestamp and name key. If we ever want to retrieve the data by timestamp or name, we may want to have indexes for each of these keys.

idb.open('DATABASE_NAME', VERSION, function(upgradeDb) { const store = upgradeDb.createObjectStore('STORE_NAME', { keyPath: 'PRIMARY_KEY_NAME' }); store.createIndex('INDEX_NAME', 'KEY_PATH'); });

For the bitsofcode application, I created 3 stores - an ArticlesStore , a BookmarksStore and a SettingsStore . Here is how I created the first.

idb.open('bitsofcode', 1, function(upgradeDb) { const ArticlesStore = upgradeDb.createObjectStore('Articles', { keyPath: 'guid' }); ArticlesStore.createIndex('guid', 'guid') // Other stores.. });

This process of opening up IndexedDB and creating/updating the database itself returns the database, to which we can create/read/update/delete data. Because of this, it is best to wrap the creation of the database and stores in a function that can be reused.

const OpenIDB = function() { return idb.open('bitsofcode', 1, function(upgradeDb) { const ArticlesStore = upgradeDb.createObjectStore('Articles', { keyPath: 'guid' }); // etc etc }); }; Data CRUD

Once the database and stores have been created, we can move on the the CRUD actions.

For my own sanity (and because I lovePromises), I use Jake Archibald’s IndexedDB Promised library which offers a Promise-ified version of the IndexedDB methods. All my examples will be using this library.

Create / Update

IndexedDB is a transactional database system, which is a system where operations on the database can be rolled back if not completed properly. This means that whenever we want to interact with any of our data, we need to first open up a new transaction, perform our actions, then either commit or rollback the transaction depending on if things succeed. This may seem like a lot of tedious work to do, but this is critical for maintaining the integrity of data.

Therefore, to create (or update) data in our IndexedDB, we need to do the following things -

Open up the database Open a new read/write transaction with the store within the database Add the data to the store Complete the transaction

These steps looks like this -

// 1. Open up the database OpenIDB().then((db) => { const dbStore = 'ArticlesStore'; // 2. Open a new read/write transaction with the store within the database const transaction = db.transaction(dbStore, 'readwrite'); const store = transaction.objectStore(dbStore); // 3. Add the data to the store store.put(data); // 4. Complete the transaction return transaction.complete; })

We are using the store.put() method here, which is also used for updating data in the database.

Read

To retrieve data from IndexedDB, we need to do the following things -

Open up the database Open a new read-only transaction with the store within the database Return the data

These steps looks like this -

// 1. Open up the database OpenIDB().then((db) => { const dbStore = 'ArticlesStore'; // 2. Open a new read-only transaction with the store within the database const transaction = db.transaction(dbStore); const store = transaction.objectStore(dbStore); // 3. Return the data return store.getAll(); })

We can also get data by a specific index -

// 1. Open up the database OpenIDB().then((db) => { const dbStore = 'ArticlesStore'; // 2. Open a new read-only transaction with the store within the database const transaction = db.transaction(dbStore); const store = transaction.objectStore(dbStore); const index = store.index('INDEX_NAME'); // 3. Return the data return index.getAll(); })

We can even get data by a value that correlates to the index -

// 1. Open up the database OpenIDB().then((db) => { const dbStore = 'ArticlesStore'; // 2. Open a new read-only transaction with the store within the database const transaction = db.transaction(dbStore); const store = transaction.objectStore(dbStore); const index = store.index('INDEX_NAME'); // 3. Return the data return index.getAll('VALUE'); }) Delete

Finally, to delete data from IndexedDB, we need to do the following things -

Open up the database Open a new read/write transaction with the store within the database Delete the data corresponding to the passed key Complete the transaction

These steps looks like this -

// 1. Open up the database OpenIDB().then((db) => { const dbStore = 'ArticlesStore'; // 2. Open a new read/write transaction with the store within the database const transaction = db.transaction(dbStore, 'readwrite'); const store = transaction.objectStore(dbStore); // 3. Delete the data corresponding to the passed key store.delete('DATA_KEY'); // 4. Complete the transaction return transaction.complete; }) Instant Loading Data with IndexedDB

By saving the articles locally in IndexedDB, I was able to first serve those locally saved articles to users first, before going to the network to get the most up-to-date data. This is what the sequence of functions look like for the Latest Articles page.

Database.retrieve('Articles') .then((articlesFromDatabase) => { if (articlesFromDatabase.length == 0) return fetchArticles(true) didFetchArticlesFromDatabase = true; return Promise.resolve(articlesFromDatabase); }) .then((articles) => { Articles = sortedArticles(articles); displayArticles(Articles); return Promise.resolve(); }) .then(() => { if (didFetchArticlesFromDatabase) updateArticlesInBackground() });

This creates an "Instant Loading" experience for users, with page load happening in 144ms !


“Instant Loading” with IndexedDB (Building a PWA, Part 2)
Support for IndexedDB

Can I Use indexeddb? Data on support for the indexeddb feature across the major browsers from caniuse.com.

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

主题: JavaScriptJavaRIMRY
分页:12
转载请注明
本文标题:“Instant Loading” with IndexedDB (Building a PWA, Part 2)
本站链接:http://www.codesec.net/view/484712.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(26)