From 970ef5d7c0b78cb5fcf2ca712ef1676a05ee82bc Mon Sep 17 00:00:00 2001 From: lingniu Date: Thu, 16 Jul 2026 07:09:55 +0800 Subject: [PATCH] perf(web): reschedule route preloads after navigation --- .../apps/web/src/v2/layout/AppShell.test.tsx | 43 +++++++++++++++++++ .../apps/web/src/v2/layout/AppShell.tsx | 4 +- 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 vehicle-data-platform/apps/web/src/v2/layout/AppShell.test.tsx diff --git a/vehicle-data-platform/apps/web/src/v2/layout/AppShell.test.tsx b/vehicle-data-platform/apps/web/src/v2/layout/AppShell.test.tsx new file mode 100644 index 00000000..6a12d492 --- /dev/null +++ b/vehicle-data-platform/apps/web/src/v2/layout/AppShell.test.tsx @@ -0,0 +1,43 @@ +import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { afterEach, expect, test, vi } from 'vitest'; +import { MemoryRouter, Route, Routes } from 'react-router-dom'; +import { ROUTER_FUTURE } from '../routing/routerConfig'; + +const routing = vi.hoisted(() => ({ + preloadRoute: vi.fn(async () => undefined), + scheduleIdleRoutePreloads: vi.fn(() => vi.fn()), + shouldPreloadRouteOnIntent: vi.fn(() => true) +})); + +vi.mock('../routing/routeModules', () => routing); +vi.mock('../auth/AuthGate', () => ({ + usePlatformSession: () => ({ session: { name: 'test-user', role: 'viewer' }, logout: vi.fn() }) +})); + +import { AppShell } from './AppShell'; + +afterEach(() => { + cleanup(); + vi.clearAllMocks(); +}); + +test('reschedules likely route preloads when the active module changes', async () => { + const firstCleanup = vi.fn(); + const secondCleanup = vi.fn(); + routing.scheduleIdleRoutePreloads + .mockReturnValueOnce(firstCleanup) + .mockReturnValueOnce(secondCleanup); + + render( + }>页面内容} /> + ); + + expect(routing.scheduleIdleRoutePreloads).toHaveBeenCalledWith({ activePathname: '/monitor' }); + fireEvent.click(screen.getByRole('link', { name: '历史数据' })); + + await waitFor(() => expect(routing.scheduleIdleRoutePreloads).toHaveBeenLastCalledWith({ activePathname: '/history' })); + expect(firstCleanup).toHaveBeenCalledTimes(1); + + cleanup(); + expect(secondCleanup).toHaveBeenCalledTimes(1); +}); diff --git a/vehicle-data-platform/apps/web/src/v2/layout/AppShell.tsx b/vehicle-data-platform/apps/web/src/v2/layout/AppShell.tsx index 90502f70..6e52a789 100644 --- a/vehicle-data-platform/apps/web/src/v2/layout/AppShell.tsx +++ b/vehicle-data-platform/apps/web/src/v2/layout/AppShell.tsx @@ -39,11 +39,11 @@ const pageNames: Record = { export function AppShell() { const location = useLocation(); - const [initialPathname] = useState(() => location.pathname); const section = location.pathname.split('/')[1] || 'monitor'; + const activeRoutePath = `/${section}`; const { session, logout } = usePlatformSession(); const roleLabel = { viewer: '只读', operator: '处置员', admin: '管理员' }[session.role]; - useEffect(() => scheduleIdleRoutePreloads({ activePathname: initialPathname }), [initialPathname]); + useEffect(() => scheduleIdleRoutePreloads({ activePathname: activeRoutePath }), [activeRoutePath]); return (