Skip to content

Commit 181a928

Browse files
committed
perf(qwikloader): put qwikloader in a separate chunk
this doesn't impact LCP and reduces download size
1 parent 3f49ff2 commit 181a928

File tree

4 files changed

+42
-20
lines changed

4 files changed

+42
-20
lines changed

packages/qwik/src/optimizer/src/manifest.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,10 @@ export function generateManifestFromBundles(
475475
manifest.preloader = bundleFileName;
476476
} else if (modulePaths.some((m) => /[/\\]qwik[/\\]dist[/\\]core\.[^/]*js$/.test(m))) {
477477
manifest.core = bundleFileName;
478+
} else if (
479+
modulePaths.some((m) => /[/\\]qwik[/\\]dist[/\\]qwikloader(\.debug)?\.[^/]*js$/.test(m))
480+
) {
481+
manifest.qwikLoader = bundleFileName;
478482
}
479483
}
480484

packages/qwik/src/optimizer/src/plugins/plugin.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,19 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) {
405405
debug(`transformedOutputs.clear()`);
406406
clientTransformedOutputs.clear();
407407
serverTransformedOutputs.clear();
408+
409+
if (opts.target === 'client') {
410+
const ql = await _ctx.resolve('@builder.io/qwik/qwikloader.js', undefined, {
411+
skipSelf: true,
412+
});
413+
if (ql) {
414+
_ctx.emitFile({
415+
id: ql.id,
416+
type: 'chunk',
417+
preserveSignature: 'allow-extension',
418+
});
419+
}
420+
}
408421
};
409422

410423
const getIsServer = (viteOpts?: { ssr?: boolean }) => {
@@ -869,6 +882,7 @@ export const isDev = ${JSON.stringify(isDev)};
869882
mapping: manifest.mapping,
870883
preloader: manifest.preloader,
871884
core: manifest.core,
885+
qwikLoader: manifest.qwikLoader,
872886
};
873887
}
874888
return `// @qwik-client-manifest
@@ -907,11 +921,12 @@ export const manifest = ${JSON.stringify(serverManifest)};\n`;
907921
function manualChunks(id: string, { getModuleInfo }: Rollup.ManualChunkMeta) {
908922
// The preloader has to stay in a separate chunk if it's a client build
909923
// the vite preload helper must be included or to prevent breaking circular dependencies
910-
if (
911-
opts.target === 'client' &&
912-
(id.endsWith(QWIK_PRELOADER_REAL_ID) || id === '\0vite/preload-helper.js')
913-
) {
914-
return 'qwik-preloader';
924+
if (opts.target === 'client') {
925+
if (id.endsWith(QWIK_PRELOADER_REAL_ID) || id === '\0vite/preload-helper.js') {
926+
return 'qwik-preloader';
927+
} else if (/qwik[\\/]dist[\\/]qwikloader\.js$/.test(id)) {
928+
return 'qwik-loader';
929+
}
915930
}
916931

917932
const module = getModuleInfo(id)!;

packages/qwik/src/optimizer/src/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@ export interface QwikManifest {
229229
preloader?: string;
230230
/** The Qwik core bundle fileName */
231231
core?: string;
232+
/** The Qwik loader bundle fileName */
233+
qwikLoader?: string;
232234
/** CSS etc to inject in the document head */
233235
injections?: GlobalInjections[];
234236
/** The version of the manifest */
@@ -249,7 +251,7 @@ export interface QwikManifest {
249251
*/
250252
export type ServerQwikManifest = Pick<
251253
QwikManifest,
252-
'manifestHash' | 'injections' | 'bundleGraph' | 'mapping' | 'preloader' | 'core'
254+
'manifestHash' | 'injections' | 'bundleGraph' | 'mapping' | 'preloader' | 'core' | 'qwikLoader'
253255
>;
254256

255257
/**

packages/qwik/src/server/render.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -133,22 +133,25 @@ export async function renderToStream(
133133

134134
const includeMode = opts.qwikLoader?.include ?? 'auto';
135135
const positionMode = opts.qwikLoader?.position ?? 'bottom';
136+
const qwikLoaderChunk = resolvedManifest?.manifest.qwikLoader;
136137
let didAddQwikLoader = false;
137-
if (positionMode === 'top' && includeMode !== 'never') {
138-
didAddQwikLoader = true;
139-
const qwikLoaderScript = getQwikLoaderScript({
140-
debug: opts.debug,
141-
});
142-
beforeContent.push(
138+
if (includeMode !== 'never' && qwikLoaderChunk) {
139+
beforeContent.unshift(
140+
jsx('link', { rel: 'modulepreload', href: `${buildBase}${qwikLoaderChunk}` }),
143141
jsx('script', {
144-
id: 'qwikloader',
145-
dangerouslySetInnerHTML: qwikLoaderScript,
142+
type: 'module',
143+
async: true,
144+
src: `${buildBase}${qwikLoaderChunk}`,
146145
})
147146
);
147+
didAddQwikLoader = true;
148+
}
149+
if (positionMode === 'top') {
148150
// Assume there will be at least click and input handlers
149151
beforeContent.push(
150152
jsx('script', {
151-
dangerouslySetInnerHTML: `window.qwikevents.push('click','input')`,
153+
// not all ESM browsers support ||=
154+
dangerouslySetInnerHTML: `(window.qwikevents||window.qwikevents=[]).push('click','input')`,
152155
})
153156
);
154157
}
@@ -195,9 +198,9 @@ export async function renderToStream(
195198
);
196199
}
197200

198-
const needLoader = !didAddQwikLoader && (!snapshotResult || snapshotResult.mode !== 'static');
201+
const needLoader = !snapshotResult || snapshotResult.mode !== 'static';
199202
const includeLoader = includeMode === 'always' || (includeMode === 'auto' && needLoader);
200-
if (includeLoader) {
203+
if (!didAddQwikLoader && includeLoader) {
201204
const qwikLoaderScript = getQwikLoaderScript({
202205
debug: opts.debug,
203206
});
@@ -213,9 +216,7 @@ export async function renderToStream(
213216
// We emit the events separately so other qwikloaders can see them
214217
const extraListeners = Array.from(containerState.$events$, (s) => JSON.stringify(s));
215218
if (extraListeners.length > 0) {
216-
const content =
217-
(includeLoader ? `window.qwikevents` : `(window.qwikevents||=[])`) +
218-
`.push(${extraListeners.join(', ')})`;
219+
const content = `(window.qwikevents||window.qwikevents=[]).push(${extraListeners.join(',')})`;
219220
children.push(
220221
jsx('script', {
221222
dangerouslySetInnerHTML: content,

0 commit comments

Comments
 (0)