perf(web): contain offscreen vehicle rows

This commit is contained in:
lingniu
2026-07-16 02:51:12 +08:00
parent 7396544269
commit 02868344ca
3 changed files with 28 additions and 3 deletions

View File

@@ -1,9 +1,32 @@
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { describe, expect, test } from 'vitest';
import mainSource from '../main.tsx?raw';
const v2Styles = readFileSync(resolve(process.cwd(), 'src/v2/styles/v2.css'), 'utf8');
describe('V2 production entry', () => {
test('does not load the legacy Semi UI theme and global stylesheet', () => {
expect(mainSource).not.toContain("./styles/global.css");
expect(mainSource).toContain("./v2/styles/v2.css");
});
test('applies rendering containment to off-screen vehicle items instead of the visible scroller', () => {
const ruleBody = (selector: string) => {
const start = v2Styles.indexOf(`${selector} {`);
expect(start).toBeGreaterThanOrEqual(0);
const end = v2Styles.indexOf('}', start);
expect(end).toBeGreaterThan(start);
return v2Styles.slice(start, end);
};
const scrollRule = ruleBody('.v2-vehicle-scroll');
const rowRule = ruleBody('.v2-vehicle-row');
const mobileCardRule = ruleBody('.v2-monitor-mobile-cards > article');
expect(scrollRule).not.toContain('content-visibility');
expect(rowRule).toContain('content-visibility: auto');
expect(rowRule).toContain('contain-intrinsic-size: auto 60px');
expect(mobileCardRule).toContain('content-visibility: auto');
expect(mobileCardRule).toContain('contain-intrinsic-size: auto 154px');
});
});

View File

@@ -108,8 +108,8 @@ button, a { -webkit-tap-highlight-color: transparent; }
.v2-vehicle-rail > header span, .v2-vehicle-rail > footer { color: var(--v2-muted); font-size: 10px; }
.v2-rail-search { display: flex; height: 34px; align-items: center; gap: 7px; margin: 0 9px 8px; border: 1px solid var(--v2-border); border-radius: 7px; padding: 0 9px; color: #8a98aa; font-size: 10px; }
.v2-rail-search.has-missing { border-color: #fed7aa; background: #fffaf3; color: #b45309; }
.v2-vehicle-scroll { min-height: 0; flex: 1; overflow: auto; overscroll-behavior: contain; content-visibility: auto; }
.v2-vehicle-row { display: grid; width: 100%; min-height: 60px; grid-template-columns: 10px minmax(0, 1fr) auto; align-items: center; gap: 8px; border: 0; border-top: 1px solid #eef2f7; background: #fff; padding: 8px 10px; text-align: left; cursor: pointer; }
.v2-vehicle-scroll { min-height: 0; flex: 1; overflow: auto; overscroll-behavior: contain; }
.v2-vehicle-row { display: grid; width: 100%; min-height: 60px; grid-template-columns: 10px minmax(0, 1fr) auto; align-items: center; gap: 8px; border: 0; border-top: 1px solid #eef2f7; background: #fff; padding: 8px 10px; text-align: left; cursor: pointer; content-visibility: auto; contain-intrinsic-size: auto 60px; }
.v2-vehicle-row:hover { background: #f8fbff; }
.v2-vehicle-row.is-selected { position: relative; z-index: 1; background: var(--v2-blue-soft); box-shadow: inset 3px 0 var(--v2-blue); }
.v2-status-dot { width: 7px; height: 7px; border-radius: 50%; background: #94a3b8; }
@@ -1119,7 +1119,7 @@ button, a { -webkit-tap-highlight-color: transparent; }
.v2-monitor-table-panel > header { align-items: flex-start; flex-direction: column; gap: 4px; padding: 10px 12px; }
.v2-monitor-table-scroll { display: none; }
.v2-monitor-mobile-cards { display: flex; min-height: 0; flex: 1; flex-direction: column; gap: 8px; overflow: auto; padding: 8px; background: #f5f7fa; }
.v2-monitor-mobile-cards > article { border: 1px solid #e0e6ee; border-radius: 10px; background: #fff; padding: 12px; box-shadow: 0 2px 8px rgba(21,32,51,.04); }
.v2-monitor-mobile-cards > article { border: 1px solid #e0e6ee; border-radius: 10px; background: #fff; padding: 12px; box-shadow: 0 2px 8px rgba(21,32,51,.04); content-visibility: auto; contain-intrinsic-size: auto 154px; }
.v2-monitor-mobile-cards > article > header { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.v2-monitor-mobile-cards > article > header div { display: flex; min-width: 0; flex-direction: column; gap: 3px; }
.v2-monitor-mobile-cards > article > header strong { font-size: 15px; }.v2-monitor-mobile-cards > article > header span { color: #7f8da1; font-size: 10px; }

View File

@@ -88,6 +88,8 @@ React documents that `lazy` caches the import promise and propagates a rejected
The first continuity pass only warmed Monitor, Tracks, History and Statistics in the background. A production cold-cache audit with 450 ms simulated latency still measured roughly 1.66 seconds for `/monitor` to `/access`, leaving Vehicles, Alerts, Access and Operations dependent on click-time loading. The idle queue now covers all eight route sections in bounded batches of two. It still skips `saveData`, `slow-2g` and `2g` connections, while pointer/focus/down intent preloading remains the fastest path for an immediately selected destination. Unit coverage proves every inactive route is queued and that cleanup cancels the next idle batch.
A production route-memory loop did not prove a retained application leak: after natural collection, JS heap returned to roughly its initial 31 MB range and old map documents/frames were released. It did expose an ineffective rendering hint: `content-visibility: auto` was attached to the always-visible vehicle scroll viewport instead of the 200 independently off-screen rows. The containment boundary now lives on every desktop rail row and mobile list card, paired with `contain-intrinsic-size` so skipped content keeps stable scroll geometry. This follows the CSS Containment specification's long-scroll-list use case while avoiding the documented scrollbar jump caused by missing intrinsic size: <https://www.w3.org/TR/css-contain-2/#content-visibility>.
The navigation and progressive-loading direction follows mature observability systems: persistent global controls, collapsible sections and loading only the content needed for the current task. Elastic documents these dashboard interaction and panel-organization patterns here: <https://www.elastic.co/docs/explore-analyze/dashboards/using> and <https://www.elastic.co/docs/explore-analyze/dashboards/arrange-panels>.
### Remaining audit queue