Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: enable navigationPreload #4548

Merged
merged 5 commits into from
Jun 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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