Prefetching and Caching with a Service Worker

Traditionally, a service worker is used to prefetch most, or all, of the bundles an application uses. Service workers are commonly seen only as a way to make an application work offline.

Qwik City, however, uses service workers quite differently to provide a powerful prefetching and caching strategy. Instead, the goal is not to download the entire application, but rather to use the service worker to dynamically prefetch what's possible to execute. By not prefetching the entire application, this free's up resources to only request the small parts of the app a user could use for what the user has on their screen at that time.

Background Task

An advantage of using a service worker is that it's also an extension of a worker, which runs in a background thread.

Web Workers makes it possible to run a script operation in a background thread separate from the main execution thread of a web application. The advantage of this is that laborious processing can be performed in a separate thread, allowing the main (usually the UI) thread to run without being blocked/slowed down.

By moving the prefetching and caching logic to a service worker (which is a worker), we're able to essentially run the code in a background task, in order to not interfere with the main UI thread. By not interfering with the main UI we're able to help improve the performance of the Qwik application for users.

Interactive Prefetching And Caching

Qwik itself should be configured to use the prefetchEvent implementation (which is also Qwik's default). When Qwik emits the qprefetch event, the service worker registration actively forwards the prefetch event data to the installed and active service worker.

The service worker then prefetches and caches the requested bundles on demand. The main thread simply has to emit data of what bundles it may need, while the service worker is only focused on ensuring it has the bundles cached. To do this the service worker pre-populates the browser's Cache.

  1. If the browser already has it cached? Great, do nothing!
  2. If the browser hasn't already cached this bundle, then let's kick off the fetch request.

Read more about Caching Request and Response Pairs.

Additionally, the service worker ensures that multiple requests for the same bundle do not happen at the same time. More about this in the Parallelizing Network Requests documentation.

User Service Worker Code

The service worker that is installed is still controlled entirely by the developer. For example, the source file src/routes/service-worker.ts becomes /service-worker.js, which is the script requested by the browser. Notice how it's place within src/routes still follows the same directory based routing pattern.

Below is an example of a default src/routes/service-worker.ts source file:

import { setupServiceWorker } from '@builder.io/qwik-city/service-worker';

setupServiceWorker();

addEventListener('install', () => self.skipWaiting());

addEventListener('activate', () => self.clients.claim());

The source code for src/routes/service-worker.ts can be modified by the developer however they'd like. This includes opting-in, or opting-out, of setting up Qwik City's service worker.

Notice that the setupServiceWorker() function is imported from @builder.io/qwik-city/service-worker and executed at the top of the source file. If, and where, this function is called can be modified by the developer. For example, the developer may want to handle the fetch requests first, in which case they'd add their own fetch listener above setupServiceWorker(). Or if they didn't want to use Qwik City's service worker at all, they would just have to remove setupServiceWorker() from the file.

Additionally, the default src/routes/service-worker.ts file comes with an install and activate event listeners, each added at the bottom of the file. The callbacks provided are the recommended callbacks. However, the developer can modify these callbacks depending on their own app's requirements.

Another important note is that Qwik City's request intercepting is only for Qwik bundles, it does not attempt to handle any requests which are not a part of its build.

So while Qwik City does provide a way to help prefetch and cache bundles, it does not take full control of the app's service worker. This still allows developers to add their service worker logic without conflicting with Qwik.

Made with โค๏ธ by