perf(webui): lazily load module descriptors
This commit is contained in:
71
docs/WEBUI_BUNDLE_BUDGETS.md
Normal file
71
docs/WEBUI_BUNDLE_BUDGETS.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# WebUI Loading And Bundle Budgets
|
||||
|
||||
The Core WebUI host owns the loading boundary for installed module packages.
|
||||
Vite discovers configured packages at build time, but emits an asynchronous
|
||||
loader for each package's `src/module.ts` contribution descriptor. At runtime,
|
||||
Core imports only descriptors whose backend manifests are enabled and identify
|
||||
the matching `frontend.package_name`.
|
||||
|
||||
The direct descriptor entry is intentional. A package root may re-export pages
|
||||
for consumers; importing that barrel as module wiring can cause those pages to
|
||||
be evaluated before navigation. Route pages and substantial panels should use
|
||||
`React.lazy`, and Core wraps routes in the shared loading/error boundary.
|
||||
|
||||
## Enforced Budgets
|
||||
|
||||
`webui/bundle-budget.json` contains the production limits:
|
||||
|
||||
| Measurement | Raw limit | Gzip limit |
|
||||
| --- | ---: | ---: |
|
||||
| Initial JavaScript static import closure | 512 KiB | 160 KiB |
|
||||
| Largest individual asynchronous JavaScript chunk | 384 KiB | 110 KiB |
|
||||
|
||||
`npm run build` writes a Vite manifest, measures the entry and its recursive
|
||||
static imports, writes `dist/bundle-metrics.json`, and fails when either budget
|
||||
is exceeded. `npm run test:module-permutations` applies the same gate to every
|
||||
permutation and records the collected results in
|
||||
`dist/module-permutation-bundle-metrics.json`. In CI, each result is also added
|
||||
to the step summary.
|
||||
|
||||
Budgets are limits, not targets. A change that approaches a limit should add a
|
||||
new lazy boundary or remove unnecessary entry code instead of raising the
|
||||
limit without measurement and review.
|
||||
|
||||
## 2026-07-30 Baseline
|
||||
|
||||
Measurements use the same full-product source tree and Node 22 runtime. The
|
||||
post-change build additionally includes the Search module in the default and
|
||||
full-product sets.
|
||||
|
||||
| Initial-load measurement | Before | After | Reduction |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| JavaScript assets in initial static closure | 1 | 1 | 0% |
|
||||
| Raw JavaScript | 1,387,043 B | 453,769 B | 67.3% |
|
||||
| Gzip level 9 | 364,767 B | 141,725 B | 61.1% |
|
||||
| Brotli quality 11 | 254,797 B | 106,701 B | 58.1% |
|
||||
| Parse proxy median | 18.776 ms | 7.750 ms | 58.7% |
|
||||
| Parse proxy p95 | 21.980 ms | 8.739 ms | 60.2% |
|
||||
|
||||
The parse proxy constructs a fresh `node:vm` `SourceTextModule` from the entry
|
||||
source 30 times with a randomized source marker. It is useful for a controlled
|
||||
before/after comparison, but is not enforced in CI because absolute timings
|
||||
vary across runners. Transfer budgets use deterministic raw and gzip byte
|
||||
counts.
|
||||
|
||||
The first budgeted full-product build reported:
|
||||
|
||||
- initial JavaScript: 453,769 B raw / 141,725 B gzip;
|
||||
- largest async chunk: `CampaignWorkspace`, 353,724 B raw / 98,142 B gzip.
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
cd /mnt/DATA/git/govoplan-core/webui
|
||||
npm run build
|
||||
npm run check:bundle-budget
|
||||
npm run test:module-permutations
|
||||
```
|
||||
|
||||
The build gate also catches accidental eager imports: a page pulled into the
|
||||
entry closure consumes the initial budget, while an oversized page or module
|
||||
descriptor consumes the asynchronous chunk budget.
|
||||
Reference in New Issue
Block a user