v4: the page a visit lands on asks for its content while its code loads — a first load went one step after the other: the entry script, then the interface's messages, then the page's chunk and its twenty-odd dependencies, and only then the request for the feed. The built page now carries a script in its head that asks for the messages' chunk at once, beside the entry, picking the language as detectLocale does (preloadCatalog in vite.config.ts; the two must agree). And main.tsx sends the landing page's first request as the app boots (utils/shared/earlyFetch.ts): the feed's first page on / and its tabs, the dump on a dump's page, as the stored session. authFetch hands that response to the page that asks for the same URL, once, to the session it was sent as and within ten seconds, in place of a request of its own; past that the page asks again. feedApiUrl (utils/shared/urls.ts) is the feed's URL for both. A category's feed, a profile, a playlist and the other pages wait for their code as before. On a local build with 150 ms of latency added, the feed's request leaves at 0.69 s where it left at 1.70 s, and the first card shows at 1.59 s where it showed at 1.92 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -104,6 +104,42 @@ function precompress(): Plugin {
|
||||
};
|
||||
}
|
||||
|
||||
// The interface's messages are a chunk of their own per language, which
|
||||
// main.tsx imports before rendering anything (src/i18n.ts). Left to that
|
||||
// import, the chunk is only asked for once the entry and the framework have
|
||||
// loaded and run. This puts a script in the page's head that asks for it at
|
||||
// once, beside them, picking the language as `detectLocale` does there: the
|
||||
// two must agree, or the wrong language is fetched for nothing.
|
||||
function preloadCatalog(): Plugin {
|
||||
return {
|
||||
name: "preload-catalog",
|
||||
apply: "build",
|
||||
transformIndexHtml: {
|
||||
order: "post",
|
||||
handler(_html, ctx) {
|
||||
const catalogs: Record<string, string> = {};
|
||||
for (const chunk of Object.values(ctx.bundle ?? {})) {
|
||||
if (chunk.type !== "chunk") continue;
|
||||
const locale = linguiConfig.locales.find((l) =>
|
||||
chunk.moduleIds.some((id) => id.split("?")[0].endsWith(`/${l}.po`))
|
||||
);
|
||||
if (locale) catalogs[locale] = `/${chunk.fileName}`;
|
||||
}
|
||||
if (Object.keys(catalogs).length === 0) return [];
|
||||
return [{
|
||||
tag: "script",
|
||||
injectTo: "head-prepend",
|
||||
children: `(function(){var c=${JSON.stringify(catalogs)},l;` +
|
||||
`try{l=localStorage.getItem("locale")}catch(e){}` +
|
||||
`if(!c[l])l=navigator.language.indexOf("fr")===0?"fr":"en";` +
|
||||
`if(!c[l])return;var k=document.createElement("link");` +
|
||||
`k.rel="modulepreload";k.href=c[l];document.head.appendChild(k)})();`,
|
||||
}];
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Each dev instance (dev/instances.ts) runs a Vite server of its own; sharing
|
||||
// one dependency cache, two of them would rewrite it under each other.
|
||||
const INSTANCE = process.env.GERBEUR_INSTANCE;
|
||||
@@ -166,6 +202,7 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
emojibase(),
|
||||
preloadCatalog(),
|
||||
precompress(),
|
||||
lingui(),
|
||||
react({
|
||||
|
||||
Reference in New Issue
Block a user