perf(webui): defer page-specific scripts to first navigation (Phase B)
7 page-specific controllers were eager-loaded in index.html on every cold
visit, even when the user lands on /dashboard and never opens /backup,
/admin, etc. Moved them to lazy-load via spa.js's existing loadScript()
helper, fired from each route's handler on first navigation:
/js/components/backup/backup-page.js — handleBackup()
/js/components/backup/backup-app-card.js — handleBackup()
/js/components/ssh/ssh-page.js — config-manager ssh-access
/js/components/peers/peers-page.js — config-manager peers
/js/components/admin/admin-overview.js — config-manager overview
/js/components/admin/charts.js — config-manager overview
/js/components/admin/admin-system.js — config-manager system
config-manager.js gets a tiny `lazyLoad` helper that delegates to
window.spaClean.loadScript with a graceful fallback when the SPA hasn't
booted (legacy paths). loadScript is idempotent — subsequent visits to
the same route are no-ops, so we don't re-fetch after the first nav.
Cold-load impact on /dashboard (the most common landing):
Before: 25 sync <script> tags loading ~1.7 MB raw / ~430 KB gzipped
After: 18 sync <script> tags loading ~1.5 MB raw / ~380 KB gzipped
+ corresponding parse-cost reduction on the client (no longer parsing
backup-page.js + apps-related JS just to render the dashboard)
Page-specific JS still loads cleanly when the user navigates there — a
single extra network round-trip per route on first visit, then cached
for 1h (per Phase A's cache headers). Compression (Phase A) means the
deferred JS is ~75 % smaller on the wire than it would have been
pre-Phase-A.
Sister update to .../Scripts/update.sh: rsync now uses --delete so
file removals in the source tree (this commit deletes 7 script tags;
earlier commits deleted config-manager-old.js) propagate to the live
install. Excludes still protect frontend/data/.
Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
parent
1cead7fc89
commit
d123eda869
@ -97,13 +97,14 @@
|
||||
<script src="/js/system/setup-wizard.js"></script>
|
||||
<script src="/js/system/setup-completion-watcher.js"></script>
|
||||
<script src="/js/system/system-orchestrator.js"></script>
|
||||
<script src="/js/components/backup/backup-page.js"></script>
|
||||
<script src="/js/components/backup/backup-app-card.js"></script>
|
||||
<script src="/js/components/ssh/ssh-page.js"></script>
|
||||
<script src="/js/components/peers/peers-page.js"></script>
|
||||
<script src="/js/components/admin/charts.js"></script>
|
||||
<script src="/js/components/admin/admin-overview.js"></script>
|
||||
<script src="/js/components/admin/admin-system.js"></script>
|
||||
<!--
|
||||
Page-specific controllers are loaded on demand by spa.js / config-manager.js
|
||||
when the user navigates to the relevant route. Keeping them out of the
|
||||
initial <script> block trims ~200 KB raw (~50 KB gzipped) off the cold-load
|
||||
cost AND avoids parsing them up front on the dashboard, which most users
|
||||
land on. Each handler's loadScript() call is idempotent — subsequent
|
||||
navigations to the same route are free.
|
||||
-->
|
||||
<script src="/js/spa.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -32,9 +32,21 @@ if (typeof window.ConfigManager === 'undefined') {
|
||||
// the first call, so the config-category path below is a cache hit.
|
||||
try { await this.core.loadConfig(category); } catch (e) {}
|
||||
|
||||
// Tool controllers are loaded on demand — they're not in index.html's
|
||||
// initial <script> block (Phase B of the WebUI lazy-load work). Falls
|
||||
// back gracefully if window.spaClean isn't around for some reason
|
||||
// (e.g. legacy bootstrap path).
|
||||
const lazyLoad = (src) =>
|
||||
window.spaClean?.loadScript ? window.spaClean.loadScript(src) : Promise.resolve();
|
||||
|
||||
// Overview is the Admin landing — an ops/health board, not a config form.
|
||||
if (category === 'overview') {
|
||||
try { this.sidebar.populateSidebar(); } catch (e) {}
|
||||
// charts.js is the chart-rendering helper admin-overview pulls in.
|
||||
await Promise.all([
|
||||
lazyLoad('/js/components/admin/admin-overview.js'),
|
||||
lazyLoad('/js/components/admin/charts.js')
|
||||
]);
|
||||
if (typeof AdminOverview !== 'undefined') {
|
||||
window.adminOverview = new AdminOverview('config-section');
|
||||
await window.adminOverview.init();
|
||||
@ -48,6 +60,7 @@ if (typeof window.ConfigManager === 'undefined') {
|
||||
// a config category — render its own controller into the main pane.
|
||||
if (category === 'ssh-access') {
|
||||
try { this.sidebar.populateSidebar(); } catch (e) {}
|
||||
await lazyLoad('/js/components/ssh/ssh-page.js');
|
||||
if (typeof SshPage !== 'undefined') {
|
||||
window.sshPage = new SshPage('config-section');
|
||||
await window.sshPage.init();
|
||||
@ -63,6 +76,7 @@ if (typeof window.ConfigManager === 'undefined') {
|
||||
// we inject its content template, then init PeersPage.
|
||||
if (category === 'peers') {
|
||||
try { this.sidebar.populateSidebar(); } catch (e) {}
|
||||
await lazyLoad('/js/components/peers/peers-page.js');
|
||||
try {
|
||||
const html = await fetch('/html/peers-content.html').then(r => r.text());
|
||||
configSection.innerHTML = html;
|
||||
@ -83,6 +97,7 @@ if (typeof window.ConfigManager === 'undefined') {
|
||||
// own controller, like SSH Access above.
|
||||
if (category === 'system') {
|
||||
try { this.sidebar.populateSidebar(); } catch (e) {}
|
||||
await lazyLoad('/js/components/admin/admin-system.js');
|
||||
if (typeof AdminSystem !== 'undefined') {
|
||||
window.adminSystem = new AdminSystem('config-section');
|
||||
await window.adminSystem.init();
|
||||
|
||||
@ -260,6 +260,12 @@ class LibrePortalSPAClean {
|
||||
|
||||
async handleBackup() {
|
||||
try {
|
||||
// backup-page.js + backup-app-card.js are loaded on first navigation.
|
||||
// loadScript is idempotent — subsequent /backup navigations are no-ops.
|
||||
await Promise.all([
|
||||
this.loadScript('/js/components/backup/backup-page.js'),
|
||||
this.loadScript('/js/components/backup/backup-app-card.js')
|
||||
]);
|
||||
const html = await this.fetchContent('/html/backup-content.html');
|
||||
this.loadContent(html, 'Backups');
|
||||
if (typeof BackupPage !== 'undefined') {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user