© Carlos Rojas 2020
C. RojasBuilding Progressive Web Applications with Vue.js https://doi.org/10.1007/978-1-4842-5334-2_4

4. Caching Strategies

Carlos Rojas1 
(1)
Medellin, Colombia
 
Nowadays, most web apps run without offline capabilities. They depend almost totally on a server. We can observe this when, from our browser in a web app, we get disconnected and the dinosaur game appears onscreen. This is how Google Chrome tells us we are offline (Figure 4-1).
../images/483082_1_En_4_Chapter/483082_1_En_4_Fig1_HTML.jpg
Figure 4-1

Chrome dino game

This is not the only scenario we want to avoid. There is also a common scenario in which LiFi shows neither our app nor the dinosaur, but leaves users waiting with a white browser window.

Some solutions work well to handle this nonconnection status, such as PouchDB or Firebase. However, with the use of web technologies such as service workers, IndexedDB, and the cache API, we can plan our offline strategies for our PWAs.

When to Store Information

If you remember, a service worker has stages in its life cycle (Figure 4-2), beginning with when it is installed until it is terminated in a web app. These moments are perfect to start mechanisms that save, update, return, or delete data from cache storage. Let’s look at some well-known strategies that can be applied to our PWAs.
../images/483082_1_En_4_Chapter/483082_1_En_4_Fig2_HTML.png
Figure 4-2

The service worker life cycle

The install event is the best moment to store files for the first time. If you remember the service worker life cycle, this stage is reached after it is detected in a web app. To reach it, we do something like this:

sw.js
const currentCache = 'cache-v1.0.0';
const files = [
    "favicon.ico",
    "icons/icon-128x128.png",
    "icons/icon-144x144.png",
    "icons/icon-152x152.png",
    "icons/icon-192x192.png",
    "icons/icon-384x384.png",
    "icons/icon-512x512.png",
    "icons/icon-72x72.png",
    "icons/icon-96x96.png",
    "index.html",
    "manifest.json",
  ];
  self.addEventListener('install', event => {
    event.waitUntil(
      caches.open(currentCache).then(cache => {
        return cache.addAll(files);
      })
    );
  });
You can go there from the repo (https://github.com/carlosrojaso/appress-book-pwa) with
$git checkout v1.0.7
As you can see, we have our files to store in the files array in our app shell. Next, we go in the install event, which brings the app shell elements from the network and stores them in cache storage, which then gives way to the activate event of the service worker (Figure 4-3).
../images/483082_1_En_4_Chapter/483082_1_En_4_Fig3_HTML.jpg
Figure 4-3

Saving data in an install event

When to Update Files in the Cache

Although it is supposed that the files in our app shell do not change frequently, they could do so, for example, with a user experience improvement, a branding change, and so on. We have to prepare for this moment and avoid saddling our users with version 1 of our app forever.

To handle a cache storage update, we use a version number in the name of our cache or the activated event of the service worker life cycle to eliminate older files and replace them with the new ones. Updating our service worker, looks something like this:

sw.js
const currentCache = 'cache-v1.0.0';
const files = [
    "favicon.ico",
    "icons/icon-128x128.png",
    "icons/icon-144x144.png",
    "icons/icon-152x152.png",
    "icons/icon-192x192.png",
    "icons/icon-384x384.png",
    "icons/icon-512x512.png",
    "icons/icon-72x72.png",
    "icons/icon-96x96.png",
    "index.html",
    "manifest.json",
  ];
  self.addEventListener('install', event => {
    event.waitUntil(
      caches.open(currentCache).then(cache => {
        return cache.addAll(files);
      })
    );
  });
  self.addEventListener('activate', event => {
    let version = 'v1.0.0';
    event.waitUntil(
      caches.keys()
        .then(
          cacheNames => {
            Promise.all(
              cacheNames
                .map(c => c.split('-'))
                .filter(c => c[0] === 'cache')
                .filter(c => c[1] !== version)
                .map(c => caches.delete(c.join('-')))
            )
          }
        )
    );
  });
You can go there from the repo (https://github.com/carlosrojaso/appress-book-pwa) with
$git checkout v1.0.8
If you look at the previous code, you’ll see that we added a series of operations when our service worker gets to our activate event. We verify whether the name that it brings in cacheNames has the same version number that we have in version. If it doesn’t, we delete the other cache that is detected (Figure 4-4). This ensures that previous versions, such as cache-v0.0.0 or cache-v0.0.1, get deleted from the client’s cache storage.
../images/483082_1_En_4_Chapter/483082_1_En_4_Fig4_HTML.jpg
Figure 4-4

Update data in activate event

These two moments—when we store files in the cache and when we update cache storage—are usually common in every app.

Responding to Requests

Although there are many strategy mixes to respond to user requests, there are some identified patterns that work great with most apps.

The service worker triggers a fetch event when we make a request. It contains information about the fetch, including the request and how the receiver will treat the response:

sw.js
self.addEventListener("fetch", (event) => {
});

cacheFirst

The cacheFirst pattern responds to requests with files from cache storage. If the response from cache storage fails, it tries to respond with files from the network (Figure 4-5).
../images/483082_1_En_4_Chapter/483082_1_En_4_Fig5_HTML.jpg
Figure 4-5

cacheFirst strategy flow

sw.js
self.addEventListener('fetch', event => {
    event.respondWith(
      caches.match(event.request).then(function(response) {
        return response || fetch(event.request);
      })
    );
 });
  • Use case: When your content changes infrequently, such as a corporate site. The content is always the same. You can save the files in the first use, provide a fast charge in the next uses, and update unfrequented changes in the content if this happens.

You can go there from the repo (https://github.com/carlosrojaso/appress-book-pwa) with
$git checkout v1.0.9

cacheOnly

The cacheOnly pattern responds to all the requests with cache storage files. If it does not find them, then it fails (Figure 4-6).
../images/483082_1_En_4_Chapter/483082_1_En_4_Fig6_HTML.jpg
Figure 4-6

cacheOnly strategy flow

Its implementation is like this:

sw.js
  self.addEventListener('fetch', event => {
    event.respondWith(caches.match(event.request));
  });
  • Use case: When you only want to access static files, such as a book that you can read offline. The content is fundamentally the same all the time.

You can go there from the repo (https://github.com/carlosrojaso/appress-book-pwa) with
$git checkout v1.0.10

networkFirst

The networkFirst pattern responds to all requests with network content. If it fails, it tries to respond with content from cache storage (Figure 4-7).
../images/483082_1_En_4_Chapter/483082_1_En_4_Fig7_HTML.jpg
Figure 4-7

networkFirst strategy flow

Its implementation should look like this:

sw.js
self.addEventListener('fetch', event => {
    event.respondWith(
      fetch(event.request).catch(function() {
        return caches.match(event.request);
      })
    );
  });
  • Use case: When your content is updated frequently, such as a blog that can be read offline. The content changes all the time and users want to read the latest content. However, if the user is offline, you can at least show the last stored content.

You can go there from the repo (https://github.com/carlosrojaso/appress-book-pwa) with
$git checkout v1.0.11

stale-while-revalidate

The stale-while-revalidate pattern responds to all requests with cache content, if available, from cache storage; but, it also fetches an update from the network for next time (Figure 4-8).
../images/483082_1_En_4_Chapter/483082_1_En_4_Fig8_HTML.jpg
Figure 4-8

stale-while-revalidate strategy flow

sw.js
  self.addEventListener('fetch', (event) => {
    event.respondWith(
      caches.open(currentCache)
      .then(function(cache) {
        return cache.match(event.request)
        .then(function(response) {
          var fetchPromise = fetch(event.request)
          .then(function(networkResponse) {
            cache.put(event.request, networkResponse.clone());
            return networkResponse;
          })
          return response || fetchPromise;
        })
      })
    );
  });
  • Use case: When content is frequently updated but the latest version is not critical, such as a news feed app. The content changes often, but you can show updated content in the next visit without providing a lousy experience for users.

You can go there from the repo (https://github.com/carlosrojaso/appress-book-pwa) with
$git checkout v1.0.12

Updating Our App

At this point, we’ve looked at the manifest, service workers, and cache strategies. Now, we can simplify our life with a package that we can install using Vue CLI:
$vue add @vue/pwa
With this package, we add Workbox along with the official plug-in. When you run the command, you’ll see something like what is shown in Figure 4-9.
../images/483082_1_En_4_Chapter/483082_1_En_4_Fig9_HTML.png
Figure 4-9

Installing @vue/cli-plugin-pwa

And the important thing is that, now when we run
$npm run build

we’ll see the new service worker in our dist/ folder. Because of the previous changes, we need to keep our changes in the icons and manifest files.

Now we run our app:
$serve -s build
Figure 4-10 shows the console.
../images/483082_1_En_4_Chapter/483082_1_En_4_Fig10_HTML.jpg
Figure 4-10

Using @vue/cli-plugin-pwa in VueNoteApp

If we take a look in cache storage, our files are there (Figure 4-11).
../images/483082_1_En_4_Chapter/483082_1_En_4_Fig11_HTML.jpg
Figure 4-11

Cache storage using @vue/cli-plugin-pwa in VueNoteApp

You can go there from the repo (https://github.com/carlosrojaso/appress-book-pwa) with
$git checkout v1.0.13

Summary

With service workers, we can save assets and add offline support in our web apps. We looked at some patterns and good practices that help us deliver a better experience to our users. We also studied the events of the service worker life cycle to determine when to use storage capabilities effectively and efficiently.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.97.189