[Updated on 11/27/2017]
This article is part of a serie that aims to build a basic but complete Progressive Web Application with VueJs, Webpack & Material Design, step-by-step and from scratch. If you have not read them yet, you can find previous parts below:
Code source is available on this GitHub repository.
For those who have already followed first parts: I have updated Cropchat sources to use the brand new
vue init pwa command. If you already cloned this repository, I strongly recommend that you pull this repository again.
Here is where we stopped:
This third part aims to give our CropChat application a new dimension: offline mode.
In a few words, we are going to learn:
What a Service Worker is and how it can offer offline experience ;
How to cache your application App Shell (core js and css) ;
How to cache your HTTP requests (external assets, cat pictures, …) ;
How to cache Firebase stream.
Being independant of network connectivity is one of the PWA key features. We want to provide our users with an app-like user experience and keep them engaged: we don’t want them to be disconnected or frozen each time they have slow or nonexistent connectivity.
A Progressive Web App is basically a web application that runs in a webview. However, it benefits from recent browsers & OS improvements that make offline mode possible: Service Workers.
A Service Worker is a JS script that user’s browser runs in the background, separately from your web application. A Service Worker executes code even when your application is closed or inactive, and opens the way to app-like features such as caching (using Cache Storage API and Fetch API), push notifications (using Push API) or background sync.
If you want to learn more about Service Workers, please have a look at Google documentation.
Remark: Progressive applications do not necessarily implement an offline mode on every device: iOS do not yet support Service Workers. This is the reason why we call them progressive: they will run on every device and propose a better/advanced user experience if the device can handle it.
You can see a Service Worker as a proxy. Each HTTP request that our application makes triggers a
fetch event. This event is catched by our Service Worker who choose to retrieve from cache or fetch from the network.
All you have to do is to populate your cache when your application is online. How does your Service Worker knows when to cache and when to fetch from network? It’s up to you: you have precise control on caching strategies: cache first (useful to cache App shell files), network first (useful to cache remote assets or data)…
For example, here is a “classic” caching strategy called Network first:
There are many other caching strategies but I won’t cover them here: for further information, refer to Google Offline Coobook.
While writing this article, I tried several caching methods. Here are your choices:
writing your Service Worker manually following Google code proof of concept;
use the new workbox plugin developed by Google.
Here is my advice: use sw-precache plugin. Writing your own Service Worker is slow and repetitive: it is not a viable solution. Workbox plugin is promising but is not yet ready [June 2017]: it does not yet support runtime caching. [UPDATE: it seems that they released it 3 days ago. Have to look at it!]
This way, no matter what network connectivity you have, your core assets will be loaded and your Front Application will be up and running. This is named pre-caching.
We are going to use sw-precache-webpack-plugin. To install it:
npm install sw-precache-webpack-plugin --save
Actually, new VueJS cli template already includes
sw-precache. If you are using it, you won’t even need to install
Have a look to your
dist/webpack.prod.conf.js file and update your production webpack configuration:
Add following line to
<body> section in your
index.html so Webpack will load
<%= htmlWebpackPlugin.options.serviceWorkerLoader %>
Build Cropchat in production mode:
npm run build
That’s all you need to cache Cropchat App Shell.
During CropChat’s build,
sw-precach-webpack-plugin generates a
service-worker.js file for us. This Service Worker will be located in
dist/service-worker.js and will be loaded and executed in your browser on first application run. Have a look at it:
You probably have noticed that I am not taking care of
webpack.dev.conf.js file. There is a good reason why. Service Workers is a production feature: you don’t want
*.css files to be cached while developing your application.
To test it, build Cropchat in production mode:
npm run build
In order to emulate an HTTP server and serve Cropchat built files, you can use brilliant serve tool: (requires node ≥ 6.9.0)
sudo npm install -g serve
Then, let the magic happen:
And browse: http://localhost:3000
Press F12 to open Chrome Developer Tools and open Application Tab:
Under Service Workers menu, you will see that CropChat now has a Service Worker installed and running. Chrome Developer Tools is a very powerful debugging software. If you want to learn more about Service Workers debugging, do not hesitate to have a look at this Progressive Web App section.
Now, let’s test offline mode:
Go back to Service Workers menu item, activate Offline Mode ;
under Network Tab, see that Offline Mode is activated here too ;
under Network Tab, disable cache (browser cache <> Service Worker cache) ;
Application is still running thanks to our Service Worker that runs in background and serves assets rom cache.
Have a look to Cache Storage: (You may need to close and re-open dev tools):
Static assets are cached!
However, we are far from a proper offline mode! Here is why:
we are not caching external Material Design Lite fonts and stylesheets, fetched from https://code.getmdl.io/1.2.1/material.blue-red.min.css ;
we are not caching cats pictures ;
We are going to address these points in the next section.
Basically, we would like to cache all HTTP requests. Service Workers can do that for us too. It’s named runtime caching.
Again, we won’t write our Service Worker manually. There is great plugin for that called
sw-toolbox and it is already included in
sw-precache-webpack-plugin. Nothing more to install: isn’t VueJS + webpack combo great?
Build and serve again. You may need to Unregister your service worker, close your Chrome Browser… (Service Workers lifecycle seems a bit buggy right now)
We are close to achieving our goal. All we need to do is to cache CropChat cat images. Remember, cat pictures urls are fetched from the Cat API. Here is what they look like:
Re-build, serve your application again (unregister your Service Worker or/and close Chrome if needed), activate offline mode and test it again:
Take a look at your browser’s Cache Storage again:
I lied to you. Our application is not yet functional online. Try to turn on your network connection (instead of using Chrome Offline Mode): you won’t see CropChat content anymore.
We need to handle Firebase case manually. Here is my network-first caching strategy:
if user has access to the Internet (using navigator.onLine check): cache Firebase Websocket content in browser’s local storage, then hook on Firebase WebSocket ;
else: serve cached content from browser’s local storage.
To get it done, update your
HomeView.vue component with the following code:
And update <template> section to use
Build your app again, serve it, load it (with internet connection back) and try it offline !
I hope you have a better understanding of how to implement offline mode with VueJS, Webpack and a few plugins. We learned:
how to use
sw-precache to cache App Shell files ;
how to user
runtimeCaching to cache external assets ;
how to handle Firebase websockets caching using browser’s local storage.
Let’s have a look to our Progressive Web App checklist:
Two of our requirements are not yet met. We will handle them in next parts. Part 4 will show you how to “benefit from native features” using HTML 5 powerful APIs such as MediaDevice and MediaStream APIs.
For those who are in Paris, I am co-organizing a Progressive Web App MeetUp. Do not hesitate to say ‘Hi!’.
Get Started with PySpark and Jupyter Notebook in 3 Minutes
Spark is a fast and powerful framework.
A progressive Web application with Vue JS, Webpack & Material Design [Part 1]
This tutorial aims to create a basic but complete progressive web application with VueJS and Webpack, from scratch.
How to Build a Serverless REST API in 15 Minutes on AWS
Use AWS Lambda to build a Serverless REST API, storing data in S3 and querying it with Athena.