This module efficiently precaches assets for Progressive Web Apps and service workers.
—
Helper functions for cache key management, request matching, and handler creation. These utilities provide access to precaching functionality without requiring a full controller instance.
Function for looking up cache keys for precached URLs.
/**
* Takes a URL and returns the corresponding cache key used in the precache.
* Handles revision parameters automatically.
* @param url - The URL to look up the cache key for
* @returns The cache key or undefined if not found
*/
function getCacheKeyForURL(url: string): string | undefined;Usage Examples:
import { getCacheKeyForURL, precacheAndRoute } from "workbox-precaching";
// First precache some assets
precacheAndRoute([
{ url: "/index.html", revision: "abc123" },
"/logo.png", // No revision, uses URL as cache key
]);
// Later, look up cache keys
const indexCacheKey = getCacheKeyForURL("/index.html");
console.log(indexCacheKey); // "/index.html?__WB_REVISION__=abc123"
const logoCacheKey = getCacheKeyForURL("/logo.png");
console.log(logoCacheKey); // "/logo.png"
const notFoundKey = getCacheKeyForURL("/nonexistent.html");
console.log(notFoundKey); // undefinedFunction for looking up requests in the precache without needing a controller instance.
/**
* Helper function that looks up a request in the precache.
* This is a wrapper around PrecacheController#matchPrecache using the default controller.
* @param request - The key (URL string or Request object) to look up
* @returns Promise resolving to cached response or undefined
*/
function matchPrecache(request: string | Request): Promise<Response | undefined>;Usage Examples:
import { matchPrecache, precacheAndRoute } from "workbox-precaching";
// Precache assets first
precacheAndRoute([
{ url: "/offline.html", revision: "v1.0" },
{ url: "/app-shell.html", revision: "v2.1" }
]);
// In service worker fetch event
self.addEventListener('fetch', async (event) => {
// Try to match against precache
const cachedResponse = await matchPrecache(event.request);
if (cachedResponse) {
console.log('Serving from precache:', event.request.url);
event.respondWith(cachedResponse);
return;
}
// Fallback to network
event.respondWith(fetch(event.request));
});
// Can also use with URL strings
const offlineResponse = await matchPrecache("/offline.html");
if (offlineResponse) {
// Use the cached offline page
return offlineResponse;
}
// Handle Request objects
const request = new Request("/app-shell.html");
const shellResponse = await matchPrecache(request);Function for creating route handlers bound to specific precached URLs.
/**
* Helper function that creates a route handler bound to a specific precached URL.
* Returns a handler function that serves the specified precached resource.
* @param url - The precached URL to bind the handler to
* @returns Route handler function that serves the precached resource
* @throws Will throw if the URL is not in the precache
*/
function createHandlerBoundToURL(url: string): RouteHandlerCallback;Usage Examples:
import {
createHandlerBoundToURL,
precacheAndRoute
} from "workbox-precaching";
import { registerRoute } from "workbox-routing";
// Precache assets first
precacheAndRoute([
{ url: "/", revision: "v1.0" },
{ url: "/offline.html", revision: "v1.1" },
{ url: "/app-shell.html", revision: "v2.0" }
]);
// Create handlers for specific URLs
const offlineHandler = createHandlerBoundToURL("/offline.html");
const shellHandler = createHandlerBoundToURL("/app-shell.html");
// Use handlers with routing
registerRoute(
// Serve offline page for failed navigation requests
({ request, event }) => {
return request.mode === 'navigate' && !navigator.onLine;
},
offlineHandler
);
registerRoute(
// Serve app shell for SPA routes
({ request, url }) => {
return request.mode === 'navigate' &&
url.pathname.startsWith('/app/');
},
shellHandler
);
// Direct usage in fetch event
self.addEventListener('fetch', (event) => {
if (event.request.mode === 'navigate' && event.request.url.includes('/dashboard')) {
// Always serve app shell for dashboard routes
const handler = createHandlerBoundToURL("/app-shell.html");
event.respondWith(handler({ request: event.request, event }));
}
});import {
matchPrecache,
createHandlerBoundToURL,
precacheAndRoute
} from "workbox-precaching";
// Precache fallback resources
precacheAndRoute([
{ url: "/offline.html", revision: "v1.0" },
{ url: "/fallback.css", revision: "v1.0" },
{ url: "/404.html", revision: "v1.0" }
]);
// Create fallback handlers
const offlineHandler = createHandlerBoundToURL("/offline.html");
const notFoundHandler = createHandlerBoundToURL("/404.html");
self.addEventListener('fetch', async (event) => {
// First, try precache
const cachedResponse = await matchPrecache(event.request);
if (cachedResponse) {
event.respondWith(cachedResponse);
return;
}
// Then try network
event.respondWith(
fetch(event.request)
.catch(async (error) => {
// Network failed, use appropriate fallback
if (event.request.mode === 'navigate') {
return offlineHandler({ request: event.request, event });
}
if (event.request.destination === 'document') {
return notFoundHandler({ request: event.request, event });
}
// For other resources, try to find a similar precached resource
return matchPrecache('/fallback.css') || new Response('Offline', {
status: 503,
statusText: 'Service Unavailable'
});
})
);
});import { getCacheKeyForURL, precacheAndRoute } from "workbox-precaching";
// Precache with revisions
precacheAndRoute([
{ url: "/critical.css", revision: "v1.5.2" },
{ url: "/app.js", revision: "v2.1.0" }
]);
// Utility to check if resources are properly cached
function validatePrecacheIntegrity(expectedAssets: Record<string, string>) {
const issues: string[] = [];
for (const [url, expectedRevision] of Object.entries(expectedAssets)) {
const cacheKey = getCacheKeyForURL(url);
if (!cacheKey) {
issues.push(`Missing from precache: ${url}`);
continue;
}
// Extract revision from cache key
const revisionMatch = cacheKey.match(/\?__WB_REVISION__=(.+)$/);
const actualRevision = revisionMatch ? revisionMatch[1] : 'none';
if (actualRevision !== expectedRevision) {
issues.push(`Version mismatch for ${url}: expected ${expectedRevision}, got ${actualRevision}`);
}
}
return issues;
}
// Check integrity
const issues = validatePrecacheIntegrity({
'/critical.css': 'v1.5.2',
'/app.js': 'v2.1.0'
});
if (issues.length > 0) {
console.warn('Precache integrity issues:', issues);
}import {
createHandlerBoundToURL,
getCacheKeyForURL,
precacheAndRoute
} from "workbox-precaching";
import { registerRoute } from "workbox-routing";
// Precache multiple app shells
precacheAndRoute([
{ url: "/mobile-shell.html", revision: "v1.0" },
{ url: "/desktop-shell.html", revision: "v1.0" },
{ url: "/tablet-shell.html", revision: "v1.0" }
]);
// Dynamic handler based on user agent
const createResponsiveHandler = () => {
const mobileHandler = createHandlerBoundToURL("/mobile-shell.html");
const desktopHandler = createHandlerBoundToURL("/desktop-shell.html");
const tabletHandler = createHandlerBoundToURL("/tablet-shell.html");
return async ({ request, event }) => {
const userAgent = request.headers.get('user-agent') || '';
if (/Mobile/.test(userAgent)) {
return mobileHandler({ request, event });
} else if (/Tablet|iPad/.test(userAgent)) {
return tabletHandler({ request, event });
} else {
return desktopHandler({ request, event });
}
};
};
// Register responsive route
registerRoute(
({ request }) => request.mode === 'navigate',
createResponsiveHandler()
);import { matchPrecache, getCacheKeyForURL } from "workbox-precaching";
// Check if resource is precached before serving
async function servePrecachedIfAvailable(request: Request): Promise<Response> {
const url = new URL(request.url);
// Check if we have this resource precached
const cacheKey = getCacheKeyForURL(url.pathname);
if (!cacheKey) {
throw new Error(`Resource not precached: ${url.pathname}`);
}
// Try to serve from precache
const cachedResponse = await matchPrecache(request);
if (cachedResponse) {
// Add custom headers to indicate precached response
const response = new Response(cachedResponse.body, {
status: cachedResponse.status,
statusText: cachedResponse.statusText,
headers: {
...cachedResponse.headers,
'X-Served-From': 'precache',
'X-Cache-Key': cacheKey
}
});
return response;
}
throw new Error(`Failed to retrieve precached resource: ${url.pathname}`);
}Install with Tessl CLI
npx tessl i tessl/npm-workbox-precaching