Progressive Web Apps are web applications that utilize the best features of the web to provide app-like experiences. As users visit your web app, the apps progressively become more like native apps with offline support, push notifications, and the ability to add your web app to the home screen of their device.
Last week we presented the Building Progressive Web Apps webinar. Check out the webinar page here.
As a companion, this blog post will show you how to set up a simple progressive web app with Vaadin Elements and Polymer.
Your completed app will look like this:
Getting started
First, install the Polymer CLI and the Vaadin Elements generator. This will build out a basic app that you will modify to add progressive web app features like service worker and offline support.
$ npm install -g polymer-cli generator-polymer-init-vaadin-elements-app $ polymer init vaadin-elements-app
Serve the app with the Polymer CLI to see this basic app.
$ polymer serve --open
This runs the app, however if you disconnect from the internet or set your browser to offline, then you’ll see that your app stops working.
Tools for working with Progressive Web Apps
Chrome Developer Tools has an applications tab, which is perfect for working with Progressive Web Apps. You can toggle the network, you can inspect the service worker, and you can view and clear the cache.See Chrome DevTools at the Google Developers site
Service Workers and offline support
To add offline support to the app, you’ll use a service worker to cache resources and display the cached app when there is no network connectivity.
Service Workers
A service worker is a programmable proxy that runs in a separate thread from your code. It cannot access the DOM and it can run even after you close your app. The service worker works in conjunction with your app to provide fast startup, background sync, and offline support. For this example, you’ll use a service worker to help run your app offline.
Learn more about Service Workers at the Google Developers site
The App Shell Model
The first thing you’ll do is make sure that when users visit your app they are presented with something, anything that’s not an offline dinosaur. One way to do this is to serve up the basic shell of your app before there is even data to put into it. This is called the app shell model, and it enables you app to still look like an app even without network.
Learn more about the App Shell Model at the Google Developers site
Trying it offline
Your app has already been set up to work as an app shell. You just need to make sure the service worker serves the correct data.
The basic app is already set up to load a service worker when you first visit it, but if you inspect the service-worker.js
file in your project directory, it will read:
console.info('Service workers will be generated at build time.');
So to get service worker support you will need to use Polymer to build your application. Run:
$ polymer build
This will create two build folders: an unbundled folder, which is optimized for HTTP/2, and a bundled folder, which bundles all the assets into as few files as possible. You’ll serve the the bundled build for now.
If you check the service-worker.js
in the build/unbundled
folder you’ll see that there is now quite a bit of data there that was generated in the build step. This built service worker will serve just the shell of your application.
Next serve the application with:
$ polymer serve build/bundled
When you refresh your browser then the app will be there as expected, but when you then go offline and refresh your page, you’ll see that the basic shell of the app still loads. The service worker takes the basic skeleton of your site and serves it to the visitor.
Despite the shell loading, no data is served up. This is better than displaying the offline dinosaur, but it is still a bad experience. Next you will implement caching with service worker.
Toward real offline functionality
We can assume that the employee data used in this app doesn’t change very often, so it’s reasonable to serve up the latest cached version of that data so that the user can see it even when offline. Then, when the user comes back online, the cache is replaced with the latest data from the network.
sw-precache
To implement caching, you can use sw-precache a tool that makes it easy to set up service worker caching. sw-precache will look through your project and cache the resources you’ve specified in a config file.
Open up sw-precache-config.js
and change it to read:
module.exports = { staticFileGlobs: [ '/index.html', '/manifest.json', '/bower_components/webcomponentsjs/webcomponents-lite.min.js' ], runtimeCaching: [{ urlPattern: /^http:\/\/localhost.*json/, handler: 'networkFirst' }], navigateFallback: '/index.html' };
The staticFileGlobs
section is where the application shell is cached. Those three files are static files that the service worker knows explicitly to cache.
The runtimeCaching
block, on the other hand, tells the service worker which files loaded by the application should be cached. These are files that will be cached when they are first loaded into the app. The urlPattern
is a regular expression matching json files in our app and handler
is a the caching strategy your app uses for these files.
As mentioned previously, the service worker is a programmable proxy that lives between your application and the cache or network. Requests will go through the service worker and then the service worker will decided how to handle those requests using handlers.
networkFirst
is a caching handler for the service worker that says that when a json file is requested, first try to grab the latest version of the file from the network, and if that request fails, then serve the copy of the json file from the cache. networkFirst
is just one of many handlers provided by sw-precache.
https://github.com/GoogleChrome/sw-precache
Read documentation for sw-precache and caching handlers on Github.
Putting it all together
Because sw-precache is a build time tool, you’ll need to rebuild the app to implement this runtime caching.
Clear the build directory and once again run:
$ polymer build $ polymer serve build/bundled
Now make sure you’re online and refresh your app twice. The first refresh is to enable the new service worker to see the app and the second one is so that the service worker grabs the latest version of the json file and puts it into cache.
Now when you go offline and refresh you’ll see that all the employee information remained in the application.
Conclusion
This is just a brief example of what you can accomplish with progressive web apps. You can find out more about progressive web apps in a number of places:
- Guides, tutorials, codelabs, and more resources from Google.
- Progressive web app APIs and more resources from Mozilla.
Vaadin is very focused on progressive web apps so keep following this blog for more tutorials about PWAs.