The VitePWA plugin from Anthony Fu is a improbable instrument to your Vite-powered websites. It helps you add a service employee that handles:
offline supportcaching belongings and contentprompting the consumer when new content material is on the market…and different goodies!
We’ll stroll by means of the idea of service staff collectively, then bounce proper into making one with the VitePWA plugin.
New to Vite? Take a look at my prior put up for an introduction.
Desk of Contents
Service staff, launched
Versioning and manifests
Our first service employee
What about offline performance?
How service staff replace
A greater option to replace content material
Runtime caching
Including your personal service employee content material
Wrapping up
Service staff, launched
Earlier than stepping into the VitePWA plugin, let’s briefly speak concerning the Service Employee itself.
A service employee is a background course of that runs on a separate thread in your internet software. Service staff have the flexibility to intercept community requests and do… something. The chances are surprisingly extensive. For instance, you possibly can intercept requests for TypeScript information and compile them on the fly. Or you possibly can intercept requests for video information and carry out a sophisticated transcoding that the browser doesn’t at present help. Extra generally although, a service employee is used to cache belongings, each to enhance a web site’s efficiency and allow it to do one thing when it’s offline.
When somebody first lands in your web site, the service employee the VitePWA plugin creates installs, and caches your entire HTML, CSS, and JavaScript information by leveraging the Cache Storage API. The result’s that, on subsequent visits to your web site, the browser will load these assets from cache, slightly than needing to make community requests. And even on the primary go to to your web site, because the service employee simply pre-cached the whole lot, the subsequent place your consumer clicks will in all probability be pre-cached already, permitting the browser to utterly bypass a community request.
Versioning and manifests
You could be questioning what occurs with a service employee when your code is up to date. In case your service employee is caching, say, a foo.js file, and also you modify that file, you need the service employee to drag down the up to date model, the subsequent time a consumer visits the positioning.
However in observe you don’t have a foo.js file. Normally, a construct system will create one thing like foo-ABC123.js, the place “ABC123” is a hash of the file. Should you replace foo.js, the subsequent deployment of your web site might ship over foo-XYZ987.js. How does the service employee deal with this?
It seems the Service Employee API is an extraordinarily low-level primitive. Should you’re searching for a local turnkey resolution between it and the cache API, you’ll be disenchanted. Principally, the creation of your service employee must be automated, partly, and linked to the construct system. You’d have to see all of the belongings your construct created, hard-code these file names into the service employee, have code to pre-cache them, and extra importantly, maintain observe of the information which might be cached.
If code updates, the service employee file additionally modifications, containing the new filenames, full with hashes. When a consumer makes their subsequent go to to the app, the brand new service employee might want to set up, and examine the brand new file manifest with the manifest that’s at present in cache, ejecting information which might be not wanted, whereas caching the brand new content material.
That is an absurd quantity of labor and extremely troublesome to get proper. Whereas it may be a enjoyable venture, in observe you’ll need to use a longtime product to generate your service employee — and one of the best product round is Workbox, which is from the parents at Google.
Even Workbox is a little bit of a low-level primitive. It wants detailed details about the information you’re pre-caching, that are buried in your construct instrument. That is why we use the VitePWA plugin. It makes use of Workbox below the hood, and configures it with all the data it wants concerning the bundles that Vite creates. Unsurprisingly, there are additionally webpack and Rollup plugins when you occur to desire working with these bundlers.
Our first service employee
I’ll assume you have already got a Vite-based web site. If not, be at liberty to create one from any of the obtainable templates.
First, we set up the VitePWA plugin:
npm i vite-plugin-pwa
We’ll import the plugin in our Vite config:
import { VitePWA } from “vite-plugin-pwa”
Then we put it to make use of within the config as effectively:
plugins: [
VitePWA()
We’ll add more options in a bit, but that’s all we need to create a surprisingly useful service worker. Now let’s register it somewhere in the entry of our application with this code:
import { registerSW } from “virtual:pwa-register”;
if (“serviceWorker” in navigator) {
// && !/localhost/.test(window.location)) {
registerSW();
}
Don’t let the code that’s commented out throw you for a loop. It’s extremely important, in fact, as it prevents the service worker from running in development. We only want to install the service worker anywhere that’s not on the localhost where we’re developing, that is, unless we’re developing the service worker itself, in which case we can comment out that check (and revert before pushing code to the main branch).
Let’s go ahead and open a fresh browser, launch DevTools, navigate to the Network tab, and run the web app. Everything should load as you’d normally expect. The difference is that you should see a whole slew of network requests in DevTools.
That’s Workbox pre-caching the bundles. Things are working!
What about offline functionality?
So, our service worker is pre-caching all of our bundled assets. That means it will serve those assets from cache without even needing to hit the network. Does that mean our service worker could serve assets even when the user has no network access? Indeed, it does!
And, believe it or not, it’s already done. Give it a try by opening the Network tab in DevTools and telling Chrome to simulate offline mode, like this.
The “No throttling” option is the default selection. Click that and select the “Offline” option to simulate an offline connection.
Let’s refresh the page. You should see everything load. Of course, if you’re running any network requests, you’ll see them hang forever since you’re offline. Even here, though, there are things you can do. Modern browsers ship with their own internal, persistent database called IndexedDB. There’s nothing stopping you from writing your own code to sync some data to there, then write some custom service worker code to intercept network requests, determine if the user is offline, and then serve equivalent content from IndexedDB if it’s in there.
But a much simpler option is to detect if the user is offline, show a message about being offline, and then bypass the data requests. This is a topic unto itself, which I’ve written about in much greater detail.
Before showing you how to write, and integrate your own service worker content, let’s take a closer look at our existing service worker. In particular, let’s see how it manages updating/changing content. This is surprisingly tricky and easy to mess up, even with the VitePWA plugin.
Before moving on, make sure you tell Chrome DevTools to put you back online.
How service workers update
Take a closer look at what happens to our site when we change the content. We’ll go ahead and remove our existing service worker, which we can do in the Application tab of DevTools, under Storage.
Click the “Clear site data” button to get a clean slate. While I’m at it, I’m going to remove most of the routes of my own site so there’s fewer resources, then let Vite rebuild the app.
Look in the generated sw.js to see the generated Workbox service worker. There should be a pre-cache manifest inside of it. Mine looks like this:
If sw.js is minified, run it through Prettier to make it easier to read.
Now let’s run the site and see what’s in our cache:
Let’s focus on the settings.js file. Vite generated assets/settings.ccb080c2.js based on the hash of its contents. Workbox, being independent of Vite, generated its own hash of the same file. If that same file name were to be generated with different content, then a new service worker would be re-generated, with a different pre-cache manifest (same file, but different revision) and Workbox would know to cache the new version, and remove the old when it’s no longer needed.
Again, the filenames will always be different since we’re using a bundler that injects hash codes into our file names, but Workbox supports dev environments which don’t do that.
Since the time writing, the VitePWA plugin has been updated and no longer injects these revision hashes. If you’re attempting to follow along with the steps in this article, this specific step might be slightly different from your actual experience. See this GitHub issue for more context.
If we update our settings.js file, then Vite will create a new file in our build, with a new hash code, which Workbox will treat as a new file. Let’s see this in action. After changing the file and re-running the Vite build, our pre-cache manifest looks like this:
Now, when we refresh the page, the prior service worker is still running and loading the prior file. Then, the new service worker, with the new pre-cache manifest is downloaded and pre-cached.
The new pre-cached manifest is displayed in the list of cached assets. Notice that both versions of our settings file are there (and both versions of a few other assets were affected as well): the old version, since that’s what’s still being run, and the new version, since the new service worker has pre-cached it.
Note the corollary here: our old content is still being served to the user since the old service worker is still running. The user is unable to see the change we just made, even if they refresh because the service worker, by default, guarantees any and all tabs with this web app are running the same version. If you want the browser to show the updated version, close your tab (and any other tabs with the site), and re-open it.
The cache should now only contain the new assets.
Workbox did all the legwork of making this all come out right! We did very little to get this going.
A better way to update content
It’s unlikely that you can get away with serving stale content to your users until they happen to close all their browser tabs. Fortunately, the VitePWA plugin offers a better way. The registerSW function accepts an object with an onNeedRefresh method. This method is called whenever there’s a new service worker waiting to take over. registerSW also returns a function that you can call to reload the page, activating the new service worker in the process.
That’s a lot, so let’s see some code:
if (“serviceWorker” in navigator) {
// && !/localhost/.test(window.location) && !/lvh.me/.test(window.location)) {
const updateSW = registerSW({
onNeedRefresh() {
Toastify({
text: `<h4 style=’display: inline’>An update is available!</h4>
<br><br>
<a class=’do-sw-update’>Click to update and reload</a> `,
escapeMarkup: false,
gravity: “bottom”,
onClick() {
updateSW(true);
}
}).showToast();
}
});
}
I’m using the toastify-js library to show a toast UI component to let users know when a new version of the service worker is available and waiting. If the user clicks the toast, I call the function VitePWA gives me to reload the page, with the new service worker running.
Now when we have pending updates, a nice toast component pops up on the front end. Clicking it reloads the page with the new content in there.
One thing to remember here is that, after you deploy the code to show the toast, the toast component won’t show up the next time you load your site. That’s because the old service worker (the one before we added the toast component) is still running. That requires manually closing all tabs and re-opening the web app for the new service worker to take over. Then, the next time you update some code, the service worker should show the toast, prompting you to update.
Why doesn’t the service worker update when the page is refreshed? I mentioned earlier that refreshing the page does not update or activate the waiting service worker, so why does this work? Calling this method doesn’t only refresh the page, but it calls some low-level Service Worker APIs (in particular skipWaiting) as well, giving us the outcome we want.
Runtime caching
We’ve seen the bundle pre-caching we get for free with VitePWA for our build assets. What about caching any other content we might request at runtime? Workbox supports this via its runtimeCaching feature.
Here’s how. The VitePWA plugin can take an object, one property of which is workbox, which takes Workbox properties.
const getCache = ({ name, pattern }: any) => ({
urlPattern: pattern,
handler: “CacheFirst” as const,
options: {
cacheName: name,
expiration: {
maxEntries: 500,
maxAgeSeconds: 60 * 60 * 24 * 365 * 2 // 2 years
},
cacheableResponse: {
statuses: [200]
}
}
});
// …
plugins: [
VitePWA({
workbox: {
runtimeCaching: [
getCache({
pattern: /^https://s3.amazonaws.com/my-library-cover-uploads/,
name: “local-images1”
}),
getCache({
pattern: /^https://my-library-cover-uploads.s3.amazonaws.com/,
name: “local-images2”
})
]
}
})
],
// …
I do know, that’s a number of code. However all it’s actually doing is telling Workbox to cache something it sees matching these URL patterns. The docs present rather more information if you wish to get deep into specifics.
Now, after that replace takes impact, we are able to see these assets being served by our service employee.
And we are able to see the corresponding cache that was created.
Including your personal service employee content material
Let’s say you need to get superior along with your service employee. You need to add some code to sync information with IndexedDB, add fetch handlers, and reply with IndexedDB information when the consumer is offline (once more, my prior put up walks by means of the ins and outs of IndexedDB). However how do you set your personal code into the service employee that Vite creates for us?
There’s one other Workbox possibility we are able to use for this: importScripts.
VitePWA({
workbox: {
importScripts: [“sw-code.js”],
Right here, the service employee will request sw-code.js at runtime. In that case, be sure that there’s an sw-code.js file that may be served by your software. The best option to obtain that’s to place it within the public folder (see the Vite docs for detailed directions).
If this file begins to develop to a dimension such that you might want to break issues up with JavaScript imports, ensure you bundle it to forestall your service employee from making an attempt to execute import statements (which it might or might not have the ability to do). You may create a separate Vite construct as an alternative.
Wrapping up
On the finish of 2021, CSS-Tips requested a bunch of front-end of us what one factor somebody cans do to make their web site higher. Chris Ferdinandi advised a service employee. Effectively, that’s precisely what we achieved on this article and it was comparatively easy, wasn’t it? That’s because of the VitePWA with hat tricks to Workbox and the Cache API.
Service staff that leverage the Cache API are able to enormously enhancing the perf of your internet app. And whereas it might sound a little bit scary or complicated at first, it’s good to know we’ve got instruments just like the VitePWA plugin to simplify issues an amazing deal. Set up the plugin and let it do the heavy lifting. Positive, there are extra superior issues {that a} service employee can do, and VitePWA can be utilized for extra complicated performance, however an offline web site is a improbable start line!
Making a Web site Work Offline Utilizing the VitePWA Plugin initially printed on CSS-Tips. It is best to get the e-newsletter and develop into a supporter.
Subscribe to MarketingSolution.
Receive web development discounts & web design tutorials.
Now! Lets GROW Together!