Skip to content

Commit

Permalink
perf: enable navigationPreload (#4548)
Browse files Browse the repository at this point in the history
  • Loading branch information
manucorporat committed Jun 21, 2023
1 parent a70895f commit d155bba
Show file tree
Hide file tree
Showing 14 changed files with 60 additions and 51 deletions.
1 change: 1 addition & 0 deletions packages/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dev": "vite --mode ssr --open",
"dev.debug": "node --inspect-brk ../../node_modules/vite/bin/vite.js --mode ssr --force",
"preview": "qwik build preview && vite preview --open",
"preview.only": "NODE_DEBUG=net,http node --inspect-brk ../../node_modules/vite/bin/vite.js preview",
"preview.wrangler": "wrangler pages dev ./dist",
"build.showcase": "pnpm node scripts/showcase.js",
"start": "pnpm dev",
Expand Down
3 changes: 1 addition & 2 deletions packages/docs/src/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default component$(() => {
<head>
<meta charSet="utf-8" />
<RouterHead />
{/* <script dangerouslySetInnerHTML={`(${collectSymbols})()`} /> */}
<ServiceWorkerRegister />
</head>
<body
class={{
Expand All @@ -30,7 +30,6 @@ export default component$(() => {
}}
>
<RouterOutlet />
<ServiceWorkerRegister />
<RealMetricsOptimization builderApiKey={BUILDER_PUBLIC_API_KEY} />
</body>
</QwikCityProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ In the [Caching Request and Response Pairs](#cache-api) docs we explained the po

As an example, let's say an end-user currently has a slow 3G connection. When they first request the landing page, as fast as this slow network allows, the device downloads the HTML and renders the content (an area where Qwik really shines). On this slow connection, it'd be a shame if they'd have to also download a few more hundred kilobytes just to [make their app work and become interactive](https://www.builder.io/blog/hydration-is-pure-overhead).

However, because the app was built with Qwik, the end-user doesn't need to download the entire application for it to become interactive. Instead, the end-user already downloaded the SSR rendered HTML app, and any interactive parts, such as an "Add to cart" button, can be prefetched immediately.
However, because the app was built with Qwik, the end-user doesn't need to download the entire application for it to become interactive. Instead, the end-user already downloaded the SSR rendered HTML app, and any interactive parts, such as an "Add to cart" button, can be prefetched immediately.

> Note that we're only prefetching the actual listener code, and _not_ the entire stack of the component tree render functions.
Expand Down Expand Up @@ -148,7 +148,7 @@ setupServiceWorker();

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

addEventListener('activate', () => self.clients.claim());
addEventListener('activate', (ev) => ev.waitUntil(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.
Expand All @@ -163,7 +163,7 @@ So while Qwik City does provide a way to help prefetch and cache bundles, it doe

## Disabled During Development and Preview

One gotcha during development and using Vite's preview mode is that the service worker is disabled which also disables speculative module fetching. During development we want to always ensure the latest development code is being used, rather than previous code.
One gotcha during development and using Vite's preview mode is that the service worker is disabled which also disables speculative module fetching. During development we want to always ensure the latest development code is being used, rather than previous code.

Speculative module fetching only kicks on a production build. To see speculative module fetching in action you'll need to run the production build on a server other than development or preview.

Expand Down
4 changes: 2 additions & 2 deletions packages/docs/src/routes/service-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ import { setupServiceWorker } from '@builder.io/qwik-city/service-worker';

setupServiceWorker();

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

addEventListener('activate', () => self.clients.claim());
self.addEventListener('activate', (ev) => ev.waitUntil(self.clients.claim()));
5 changes: 5 additions & 0 deletions packages/docs/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ export default defineConfig(async () => {

const routesDir = resolve('src', 'routes');
return {
preview: {
headers: {
'Cache-Control': 'public, max-age=600',
},
},
ssr: {
noExternal: [
'@mui/material',
Expand Down
4 changes: 2 additions & 2 deletions packages/qwik-city/buildtime/vite/dev-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,6 @@ const STATIC_CONTENT_TYPES: { [ext: string]: string } = {
};

const DEV_SERVICE_WORKER = `/* Qwik City Dev Service Worker */
addEventListener('install', () => self.skipWaiting());
addEventListener('activate', () => self.clients.claim());
self.addEventListener('install', () => self.skipWaiting());
self.addEventListener('activate', (ev) => ev.waitUntil(self.clients.claim()));
`;
9 changes: 2 additions & 7 deletions packages/qwik-city/middleware/node/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,10 @@ export async function fromNodeHttp(
res.setHeader('Set-Cookie', cookieHeaders);
}
return new WritableStream<Uint8Array>({
start(controller) {
res.on('close', () => controller.error());
},
write(chunk, controller) {
write(chunk) {
res.write(chunk, (error) => {
if (error) {
// FIXME: Ideally, we would like to inform the writer that this was an error.
// Not all writers seem to handle rejections, though.
// controller.error(error);
console.error(error);
}
});
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ export function renderQwikMiddleware(render: Render) {
const isStatic = getRequestMode(requestEv) === 'static';
const result = await render({
base: requestEv.basePathname + 'build/',
stream: stream,
stream,
serverData: getQwikCityServerData(requestEv),
containerAttributes: {
['q:render']: isStatic ? 'static' : '',
Expand Down
6 changes: 4 additions & 2 deletions packages/qwik-city/runtime/src/service-worker/cached-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ export const cachedFetch = (
cache: Cache,
fetch: Fetch,
awaitingRequests: AwaitingRequests,
request: Request
request: Request,
preloadResponse?: Promise<Response> | undefined
) =>
new Promise<Response>((promiseResolve, promiseReject) => {
const url = request.url;
Expand Down Expand Up @@ -59,7 +60,8 @@ export const cachedFetch = (
} else {
// no cached response found or user didn't want to use the cache
// do a full network request
return fetch(request).then(async (networkResponse) => {
const responsePromise = preloadResponse ? preloadResponse : fetch(request);
return responsePromise.then(async (networkResponse) => {
if (networkResponse.ok) {
// network response was good, let's cache it
await cache.put(url, networkResponse.clone());
Expand Down
30 changes: 19 additions & 11 deletions packages/qwik-city/runtime/src/service-worker/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const setupServiceWorkerScope = (
ev.respondWith(
swScope.caches.open(qBuildCacheName).then((qBuildCache) => {
prefetchWaterfall(appBundles, qBuildCache, swFetch, url);
return cachedFetch(qBuildCache, swFetch, awaitingRequests, request);
return cachedFetch(qBuildCache, swFetch, awaitingRequests, request, ev.preloadResponse);
})
);
}
Expand Down Expand Up @@ -63,15 +63,23 @@ export const setupServiceWorkerScope = (
}
});

swScope.addEventListener('activate', async () => {
try {
const qBuildCache = await swScope.caches.open(qBuildCacheName);
const cachedRequestKeys = await qBuildCache.keys();
const cachedUrls = cachedRequestKeys.map((r) => r.url);
const cachedRequestsToDelete = getCacheToDelete(appBundles, cachedUrls);
await Promise.all(cachedRequestsToDelete.map((r) => qBuildCache.delete(r)));
} catch (e) {
console.error(e);
}
swScope.addEventListener('activate', async (event) => {
event.waitUntil(
(async () => {
if ((self as any as ServiceWorkerGlobalScope).registration.navigationPreload) {
// Enable navigation preloads!
await (self as any as ServiceWorkerGlobalScope).registration.navigationPreload.enable();
}
try {
const qBuildCache = await swScope.caches.open(qBuildCacheName);
const cachedRequestKeys = await qBuildCache.keys();
const cachedUrls = cachedRequestKeys.map((r) => r.url);
const cachedRequestsToDelete = getCacheToDelete(appBundles, cachedUrls);
await Promise.all(cachedRequestsToDelete.map((r) => qBuildCache.delete(r)));
} catch (e) {
console.error(e);
}
})()
);
});
};
32 changes: 16 additions & 16 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions starters/apps/base/src/routes/service-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import { setupServiceWorker } from '@builder.io/qwik-city/service-worker';

setupServiceWorker();

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

addEventListener('activate', () => self.clients.claim());
self.addEventListener('install', () => self.skipWaiting());
self.addEventListener('activate', (ev) => ev.waitUntil(self.clients.claim()));

declare const self: ServiceWorkerGlobalScope;
2 changes: 1 addition & 1 deletion starters/apps/basic/src/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ export default component$(() => {
<meta charSet="utf-8" />
<link rel="manifest" href="/manifest.json" />
<RouterHead />
<ServiceWorkerRegister />
</head>
<body lang="en">
<RouterOutlet />
<ServiceWorkerRegister />
</body>
</QwikCityProvider>
);
Expand Down
2 changes: 1 addition & 1 deletion starters/apps/site-with-visual-cms/src/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ export default component$(() => {
<meta charSet="utf-8" />
<link rel="manifest" href="/manifest.json" />
<RouterHead />
<ServiceWorkerRegister />
</head>
<body lang="en">
<RouterOutlet />
<ServiceWorkerRegister />
</body>
</QwikCityProvider>
);
Expand Down

0 comments on commit d155bba

Please sign in to comment.