From a8fa1d22e83260fb16134a3338d364dc81b9021e Mon Sep 17 00:00:00 2001 From: Speculator55005 <50082482+fas89@users.noreply.github.com> Date: Mon, 14 Sep 2026 22:42:17 +0200 Subject: [PATCH] perf: navigation blocked on a chunk fetch because prefetch was off Reported symptom: switching between pages feels slow. Confirmed, with a cause. `shouldPrefetch` and `shouldPreload` both DEFAULT TO TRUE in VuePress. This repo set both to false in the initial commit, in April, with no comment. The cost is paid on every navigation: with prefetch off the built site carries **zero** `` across 213 pages, so clicking a link blocks on fetching that page's chunk - one network round trip before anything renders. Measured on the built output: 0 prefetch links against 310 chunks. I could not reproduce the latency locally, and should say so: served from localhost there is no round trip to pay. The mechanism is certain from the built HTML; the felt slowness needs a real network, which is where it was reported. **Turning it fully back on would have been worse.** Of 19 MB of chunks, 12.4 MB is Monaco - the ts / css / html / json workers plus `editor.api` and `vs` - and that is reachable only from /playground/. The VuePress default of `true` would prefetch all of it for every visitor. So `shouldPrefetch` is now the documented function form, `(file, type) => boolean`, prefetching script chunks and skipping the editor. Result on a deep page: 214 prefetch links, 4.87 MB, and **0 Monaco chunks**. That is cached after the first page rather than paid per navigation, and it is strictly less than the framework default, not more. `shouldPreload` returns to `true`, its default. Preload only covers files the CURRENT page needs, so it is correct everywhere including /playground/, where Monaco genuinely is required. Verified: the playground still references its chunk and NOT Monaco's in HTML, both worker bundles still exist for on-demand load, and the build is green. Considered and rejected: quicklink and instant.page both solve this well, but they add a runtime dependency to do what the framework already exposes, and neither can see chunk names - so neither could have excluded Monaco, which is the whole difficulty here. --- docs/.vuepress/config.ts | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts index bc3e7e8..4df3a7c 100644 --- a/docs/.vuepress/config.ts +++ b/docs/.vuepress/config.ts @@ -41,8 +41,29 @@ export default defineUserConfig({ bundler: viteBundler(), - shouldPrefetch: false, - shouldPreload: false, + // Both of these default to TRUE in VuePress. They were set to false in the + // initial commit with no rationale, and the cost is paid on every single + // navigation: with prefetch off the built HTML carries zero + // ``, so clicking through to a page blocks on fetching + // that page's chunk. Measured on the built site: 0 prefetch links across 213 + // pages, against 310 chunks. + // + // Turning prefetch fully back on is not right either. Of 19 MB of chunks, + // 12.4 MB is Monaco - the `ts` / `css` / `html` / `json` workers plus + // `editor.api` and `vs` - and that is reachable only from /playground/. + // Prefetching it for every visitor would trade one problem for a worse one. + // + // So: prefetch the route chunks (299 of the 310 are under 100 KB), and skip + // the editor. The remaining page chunks are what make navigation feel + // instant. + shouldPrefetch: (file, type) => { + if (type !== 'script') return false + return !/(?:ts|css|html|json)\.worker-|editor\.api-|(?:^|\/)vs-/.test(file) + }, + + // Preload only covers files the CURRENT page needs, so the default is simply + // correct - including on /playground/, where Monaco genuinely is needed. + shouldPreload: true, head: [ // Favicon. This was `logo.png`, which is 69,734 bytes at 256x139 — a