I’m making an offline first WordPress PWA, part 6

Single Page App

This series consists of the following parts:

In the previous part, I actually achieved my goal, at least in part. The front page of the blog is working offline and loads almost instantly thanks to the App Shell Model and caching with the service worker.

However, the pages for the individual blog posts, single.php in other words, needs to be worked on. They work just like any regular page at the moment, which isn’t good if the user is offline or on an unstable lie-fi connection.

My first though was to do in the same way as I did on the front page. The server side rendered template would only consist of placeholders which is replaced with real content on the client side.

But what I later realized was that there’s no gain in that method. Let’s say that there’s two blog posts with the following URL’s:

/blog/hello-world/

/blog/blog-post-1/

In order to save these in the cache, just like the front page, I could have added this to the service worker:

workbox.routing.registerRoute(/\/blog\/.+/, workbox.strategies.staleWhileRevalidate());

The problem is that the service worker would have treated these blog posts as two different objects in the cache. It’s certainly true that they are different, but both should share the same template according to the App Shell Model. I should end up with three objects in the cache:

The template JSON data for the first blog post JSON data for the second blog post

But the result is the following with client side rendering:

HTML (placeholders) for the first blog posts HTML (placeholders) for the second blog posts JSON data for the first blog post JSON data for the second blog post

This is how it looks in developer tools:

The front page, two blog posts, one header image and also the JSON data for all posts and two individual.

I have these two alternatives since this is meaningless:

Follow my principles and switch to regular server side rendering for the individual blog posts. Go against my principles and bet entirely on client side rendering and make an SPA (Single Page Application) with Vue components.

Violated against my principles have I already done so I can (and should?) continue doing that. After all, this is just an experiment 🤷🏼‍♂️

SPA PWA

I love how smart those acronyms makes me feel!

Anyway, I could fairly easily convert the blog to an SPA with the help of Vue Router.

I won’t go into detail of how it works, but I can describe how it’s made in short terms.

index.php

This file contains only this, more or less:

<div id="app">

<router-view></router-view>

</div>

<router-view> will be replaced with the Vue component that the current route should display.

app.js

This is where all the action happens. The most important things is happening between line 8–17.

The index component should be displayed on the front page and singlePostContainer is being used when a single post is visited.

The index component is, just like before, loading all blog posts with AJAX from the JSON API. singlePostContainer does exactly the same thing, but for a single post of course.

serviceworker.js

This new solution means that the service worker needs to store the following in the cache:

The template of the front page (the “shell” according to the App Shell Model) CSS, JavaScript and images. (The JS files takes care of most of the rendering) JSON data for the blog posts.

serviceworker.js has been updated in the following way:

// The latest blog posts

workbox.routing.registerRoute('/wp-json/wp/v2/posts', workbox.strategies.networkFirst()); // A single blog post

workbox.routing.registerRoute(/\/wp-json\/wp\/v2\/posts\/.+/, workbox.strategies.staleWhileRevalidate());

The networkFirst() strategy is used because I want the user to always get a fresh list of all the latest blog posts. They will be served from the cache as fallback if the user is offline.

Single blog posts will probably not be updated very much after they has been published, so that’s why I’m using the staleWhileRevalidate() strategy for them. It means that the service worker will fetch the JSON data from the cache, but also make an network request and update the cache in the background. In other words, the blog post will load instantly from the second visit.

I have also activated the WordPress plugin Autoptimize for optimization of CSS/JS files. I’ve added the following to the service worker so these files are cached to:

workbox.routing.registerRoute(/wp-content\/cache\/autoptimize\/.+/, workbox.strategies.cacheFirst());

I’m using the cacheFirst() strategy here since these files never will be updated. Autoptimize will give the optimized file a completely new and unique name (see screenshot below) if I make a change to any of my files.

This is how the cache looks like after all the changes:

As you can see, it contains a HTML file, a few CSS/JS files, a header image and JSON data. Removing old Autoptimize files as soon as a new one has been stored would of course be nice, but that’s an adventure for another time.

The complete service worker looks like this:

Was this the final part of this series? My goal is completely achieved now: the blog is an PWA that works offline!

But there’s still a few adjustments that could be done. The performance audit in Developer tools gives the following score, which isn’t very flattering:

If you’re like me and usually tweet with hashtags like #perfmatters, the project could not end like this. So I guess I’ll see you in part 7!