perf(P22PROD1-1,P22PROD1-2): 路由懒加载与Vite分包消除大包warning

这个提交包含在:
2026-03-12 21:48:47 +08:00
父节点 af42be14f4
当前提交 c846bb203c
共修改 27 个文件,包含 204 行新增和 47 行删除
@@ -0,0 +1,12 @@
import { Spinner } from "../ui/spinner";
export function RoutePageFallback() {
return (
<div className="flex min-h-[40vh] items-center justify-center">
<div className="card-std flex items-center gap-3 px-4 py-3 text-sm text-zinc-500">
<Spinner size="sm" />
<span>页面加载中...</span>
</div>
</div>
);
}
+55 -47
查看文件
@@ -1,38 +1,46 @@
import { Suspense, type ReactNode } from "react";
import { Navigate, Route, Routes } from "react-router-dom"; import { Navigate, Route, Routes } from "react-router-dom";
import { AppShellLayout } from "../layouts/AppShellLayout"; import { AppShellLayout } from "../layouts/AppShellLayout";
import { AuthLayout } from "../layouts/AuthLayout"; import { AuthLayout } from "../layouts/AuthLayout";
import { AuditLogsPage } from "../pages/AuditLogsPage";
import { ApprovalRulesPage } from "../pages/ApprovalRulesPage";
import { FeatureFlagsPage } from "../pages/FeatureFlagsPage";
import { ChangePasswordPage } from "../pages/ChangePasswordPage";
import { BudgetsPage } from "../pages/BudgetsPage"; import { BudgetsPage } from "../pages/BudgetsPage";
import { ChangePasswordPage } from "../pages/ChangePasswordPage";
import { DashboardPage } from "../pages/DashboardPage"; import { DashboardPage } from "../pages/DashboardPage";
import { DepartmentsPage } from "../pages/DepartmentsPage";
import { MasterDataPage } from "../pages/MasterDataPage";
import { DelegationsPage } from "../pages/DelegationsPage";
import { ForbiddenPage } from "../pages/ForbiddenPage"; import { ForbiddenPage } from "../pages/ForbiddenPage";
import { MobileApprovalsPage } from "../pages/MobileApprovalsPage";
import { MobileAssetScanPage } from "../pages/MobileAssetScanPage";
import { MobileCapturePage } from "../pages/MobileCapturePage";
import { InvoicePoolPage } from "../pages/InvoicePoolPage";
import { LoginPage } from "../pages/LoginPage"; import { LoginPage } from "../pages/LoginPage";
import { LeaveRequestPage } from "../pages/LeaveRequestPage";
import { NotificationsPage } from "../pages/NotificationsPage";
import { NotificationChannelsPage } from "../pages/NotificationChannelsPage";
import { ErpIntegrationPage } from "../pages/ErpIntegrationPage";
import { PurchaseRequestsPage } from "../pages/PurchaseRequestsPage";
import { TravelRequestsPage } from "../pages/TravelRequestsPage";
import { AssetRegistryPage } from "../pages/AssetRegistryPage";
import { InventoryChecksPage } from "../pages/InventoryChecksPage";
import { NotFoundPage } from "../pages/NotFoundPage"; import { NotFoundPage } from "../pages/NotFoundPage";
import { ProfilePage } from "../pages/ProfilePage"; import { ProfilePage } from "../pages/ProfilePage";
import { ReimbursementDetailPage } from "../pages/ReimbursementDetailPage"; import { RoutePageFallback } from "../components/common/RoutePageFallback";
import { ReimbursementsPage } from "../pages/ReimbursementsPage"; import {
import { UserManagementPage } from "../pages/UserManagementPage"; ApprovalRulesPage,
import { WorkflowTaskDetailPage } from "../pages/WorkflowTaskDetailPage"; AssetRegistryPage,
import { WorkflowTasksPage } from "../pages/WorkflowTasksPage"; AuditLogsPage,
DelegationsPage,
DepartmentsPage,
ErpIntegrationPage,
FeatureFlagsPage,
InventoryChecksPage,
InvoicePoolPage,
LeaveRequestPage,
MasterDataPage,
MobileApprovalsPage,
MobileAssetScanPage,
MobileCapturePage,
NotificationChannelsPage,
NotificationsPage,
PurchaseRequestsPage,
ReimbursementDetailPage,
ReimbursementsPage,
TravelRequestsPage,
UserManagementPage,
WorkflowTaskDetailPage,
WorkflowTasksPage,
} from "./lazy";
import { GuestOnly, RequireAuth, RequirePermission } from "./guards"; import { GuestOnly, RequireAuth, RequirePermission } from "./guards";
function withRouteFallback(node: ReactNode) {
return <Suspense fallback={<RoutePageFallback />}>{node}</Suspense>;
}
export function AppRouter() { export function AppRouter() {
return ( return (
<Routes> <Routes>
@@ -82,7 +90,7 @@ export function AppRouter() {
path="reimbursements" path="reimbursements"
element={ element={
<RequirePermission anyOf={["expense.view_own", "expense.view_all"]}> <RequirePermission anyOf={["expense.view_own", "expense.view_all"]}>
<ReimbursementsPage /> {withRouteFallback(<ReimbursementsPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -91,7 +99,7 @@ export function AppRouter() {
path="reimbursements/:reimbursementId" path="reimbursements/:reimbursementId"
element={ element={
<RequirePermission anyOf={["expense.view_own", "expense.view_all"]}> <RequirePermission anyOf={["expense.view_own", "expense.view_all"]}>
<ReimbursementDetailPage /> {withRouteFallback(<ReimbursementDetailPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -100,7 +108,7 @@ export function AppRouter() {
path="leave-requests" path="leave-requests"
element={ element={
<RequirePermission anyOf={["leave.view_own", "leave.view_all"]}> <RequirePermission anyOf={["leave.view_own", "leave.view_all"]}>
<LeaveRequestPage /> {withRouteFallback(<LeaveRequestPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -109,7 +117,7 @@ export function AppRouter() {
path="purchase-requests" path="purchase-requests"
element={ element={
<RequirePermission anyOf={["procurement.create", "procurement.view_own", "procurement.view_all", "procurement.manage"]}> <RequirePermission anyOf={["procurement.create", "procurement.view_own", "procurement.view_all", "procurement.manage"]}>
<PurchaseRequestsPage /> {withRouteFallback(<PurchaseRequestsPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -118,7 +126,7 @@ export function AppRouter() {
path="travel-requests" path="travel-requests"
element={ element={
<RequirePermission anyOf={["travel.create", "travel.view_own", "travel.view_all", "travel.manage", "travel.payment.manage", "integration.manage"]}> <RequirePermission anyOf={["travel.create", "travel.view_own", "travel.view_all", "travel.manage", "travel.payment.manage", "integration.manage"]}>
<TravelRequestsPage /> {withRouteFallback(<TravelRequestsPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -127,7 +135,7 @@ export function AppRouter() {
path="assets" path="assets"
element={ element={
<RequirePermission anyOf={["asset.view_own", "asset.view_all", "asset.manage", "asset.transfer", "asset.inventory"]}> <RequirePermission anyOf={["asset.view_own", "asset.view_all", "asset.manage", "asset.transfer", "asset.inventory"]}>
<AssetRegistryPage /> {withRouteFallback(<AssetRegistryPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -136,7 +144,7 @@ export function AppRouter() {
path="inventory-checks" path="inventory-checks"
element={ element={
<RequirePermission anyOf={["asset.inventory"]}> <RequirePermission anyOf={["asset.inventory"]}>
<InventoryChecksPage /> {withRouteFallback(<InventoryChecksPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -145,7 +153,7 @@ export function AppRouter() {
path="workflow/tasks" path="workflow/tasks"
element={ element={
<RequirePermission anyOf={["expense.approve", "leave.approve", "procurement.approve", "travel.approve"]}> <RequirePermission anyOf={["expense.approve", "leave.approve", "procurement.approve", "travel.approve"]}>
<WorkflowTasksPage /> {withRouteFallback(<WorkflowTasksPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -154,7 +162,7 @@ export function AppRouter() {
path="workflow/tasks/:taskId" path="workflow/tasks/:taskId"
element={ element={
<RequirePermission anyOf={["expense.approve", "leave.approve", "procurement.approve", "travel.approve"]}> <RequirePermission anyOf={["expense.approve", "leave.approve", "procurement.approve", "travel.approve"]}>
<WorkflowTaskDetailPage /> {withRouteFallback(<WorkflowTaskDetailPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -163,7 +171,7 @@ export function AppRouter() {
path="invoice-pool" path="invoice-pool"
element={ element={
<RequirePermission anyOf={["invoice.upload", "invoice.view_own", "invoice.manage"]}> <RequirePermission anyOf={["invoice.upload", "invoice.view_own", "invoice.manage"]}>
<InvoicePoolPage /> {withRouteFallback(<InvoicePoolPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -172,7 +180,7 @@ export function AppRouter() {
path="mobile/capture" path="mobile/capture"
element={ element={
<RequirePermission anyOf={["invoice.upload", "invoice.manage"]}> <RequirePermission anyOf={["invoice.upload", "invoice.manage"]}>
<MobileCapturePage /> {withRouteFallback(<MobileCapturePage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -181,7 +189,7 @@ export function AppRouter() {
path="mobile/assets/scan" path="mobile/assets/scan"
element={ element={
<RequirePermission anyOf={["asset.view_own", "asset.view_all", "asset.manage", "asset.transfer", "asset.inventory"]}> <RequirePermission anyOf={["asset.view_own", "asset.view_all", "asset.manage", "asset.transfer", "asset.inventory"]}>
<MobileAssetScanPage /> {withRouteFallback(<MobileAssetScanPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -190,18 +198,18 @@ export function AppRouter() {
path="mobile/approvals" path="mobile/approvals"
element={ element={
<RequirePermission anyOf={["expense.approve", "leave.approve", "procurement.approve", "travel.approve"]}> <RequirePermission anyOf={["expense.approve", "leave.approve", "procurement.approve", "travel.approve"]}>
<MobileApprovalsPage /> {withRouteFallback(<MobileApprovalsPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
<Route path="notifications" element={<NotificationsPage />} /> <Route path="notifications" element={withRouteFallback(<NotificationsPage />)} />
<Route <Route
path="audit-logs" path="audit-logs"
element={ element={
<RequirePermission anyOf={["system.audit.view"]}> <RequirePermission anyOf={["system.audit.view"]}>
<AuditLogsPage /> {withRouteFallback(<AuditLogsPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -210,7 +218,7 @@ export function AppRouter() {
path="delegations" path="delegations"
element={ element={
<RequirePermission anyOf={["expense.proxy_manage"]}> <RequirePermission anyOf={["expense.proxy_manage"]}>
<DelegationsPage /> {withRouteFallback(<DelegationsPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -219,7 +227,7 @@ export function AppRouter() {
path="system/users" path="system/users"
element={ element={
<RequirePermission anyOf={["system.users"]}> <RequirePermission anyOf={["system.users"]}>
<UserManagementPage /> {withRouteFallback(<UserManagementPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -228,7 +236,7 @@ export function AppRouter() {
path="system/departments" path="system/departments"
element={ element={
<RequirePermission anyOf={["system.users"]}> <RequirePermission anyOf={["system.users"]}>
<DepartmentsPage /> {withRouteFallback(<DepartmentsPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -237,7 +245,7 @@ export function AppRouter() {
path="system/master-data" path="system/master-data"
element={ element={
<RequirePermission anyOf={["masterdata.view", "masterdata.manage"]}> <RequirePermission anyOf={["masterdata.view", "masterdata.manage"]}>
<MasterDataPage /> {withRouteFallback(<MasterDataPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -246,7 +254,7 @@ export function AppRouter() {
path="system/notification-channels" path="system/notification-channels"
element={ element={
<RequirePermission anyOf={["notification.channel.manage"]}> <RequirePermission anyOf={["notification.channel.manage"]}>
<NotificationChannelsPage /> {withRouteFallback(<NotificationChannelsPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -255,7 +263,7 @@ export function AppRouter() {
path="system/integrations/erp" path="system/integrations/erp"
element={ element={
<RequirePermission anyOf={["integration.manage"]}> <RequirePermission anyOf={["integration.manage"]}>
<ErpIntegrationPage /> {withRouteFallback(<ErpIntegrationPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -264,7 +272,7 @@ export function AppRouter() {
path="system/approval-rules" path="system/approval-rules"
element={ element={
<RequirePermission anyOf={["system.settings"]}> <RequirePermission anyOf={["system.settings"]}>
<ApprovalRulesPage /> {withRouteFallback(<ApprovalRulesPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -273,7 +281,7 @@ export function AppRouter() {
path="system/feature-flags" path="system/feature-flags"
element={ element={
<RequirePermission anyOf={["system.settings"]}> <RequirePermission anyOf={["system.settings"]}>
<FeatureFlagsPage /> {withRouteFallback(<FeatureFlagsPage />)}
</RequirePermission> </RequirePermission>
} }
/> />
@@ -0,0 +1 @@
export { ApprovalRulesPage as default } from "../../pages/ApprovalRulesPage";
@@ -0,0 +1 @@
export { AssetRegistryPage as default } from "../../pages/AssetRegistryPage";
+1
查看文件
@@ -0,0 +1 @@
export { AuditLogsPage as default } from "../../pages/AuditLogsPage";
@@ -0,0 +1 @@
export { DelegationsPage as default } from "../../pages/DelegationsPage";
@@ -0,0 +1 @@
export { DepartmentsPage as default } from "../../pages/DepartmentsPage";
@@ -0,0 +1 @@
export { ErpIntegrationPage as default } from "../../pages/ErpIntegrationPage";
@@ -0,0 +1 @@
export { FeatureFlagsPage as default } from "../../pages/FeatureFlagsPage";
@@ -0,0 +1 @@
export { InventoryChecksPage as default } from "../../pages/InventoryChecksPage";
@@ -0,0 +1 @@
export { InvoicePoolPage as default } from "../../pages/InvoicePoolPage";
@@ -0,0 +1 @@
export { LeaveRequestPage as default } from "../../pages/LeaveRequestPage";
+1
查看文件
@@ -0,0 +1 @@
export { MasterDataPage as default } from "../../pages/MasterDataPage";
@@ -0,0 +1 @@
export { MobileApprovalsPage as default } from "../../pages/MobileApprovalsPage";
@@ -0,0 +1 @@
export { MobileAssetScanPage as default } from "../../pages/MobileAssetScanPage";
@@ -0,0 +1 @@
export { MobileCapturePage as default } from "../../pages/MobileCapturePage";
@@ -0,0 +1 @@
export { NotificationChannelsPage as default } from "../../pages/NotificationChannelsPage";
@@ -0,0 +1 @@
export { NotificationsPage as default } from "../../pages/NotificationsPage";
@@ -0,0 +1 @@
export { PurchaseRequestsPage as default } from "../../pages/PurchaseRequestsPage";
@@ -0,0 +1 @@
export { ReimbursementDetailPage as default } from "../../pages/ReimbursementDetailPage";
@@ -0,0 +1 @@
export { ReimbursementsPage as default } from "../../pages/ReimbursementsPage";
@@ -0,0 +1 @@
export { TravelRequestsPage as default } from "../../pages/TravelRequestsPage";
@@ -0,0 +1 @@
export { UserManagementPage as default } from "../../pages/UserManagementPage";
@@ -0,0 +1 @@
export { WorkflowTaskDetailPage as default } from "../../pages/WorkflowTaskDetailPage";
@@ -0,0 +1 @@
export { WorkflowTasksPage as default } from "../../pages/WorkflowTasksPage";
+25
查看文件
@@ -0,0 +1,25 @@
import { lazy } from "react";
export const AuditLogsPage = lazy(() => import("./AuditLogsPage"));
export const ApprovalRulesPage = lazy(() => import("./ApprovalRulesPage"));
export const AssetRegistryPage = lazy(() => import("./AssetRegistryPage"));
export const DelegationsPage = lazy(() => import("./DelegationsPage"));
export const DepartmentsPage = lazy(() => import("./DepartmentsPage"));
export const ErpIntegrationPage = lazy(() => import("./ErpIntegrationPage"));
export const FeatureFlagsPage = lazy(() => import("./FeatureFlagsPage"));
export const InventoryChecksPage = lazy(() => import("./InventoryChecksPage"));
export const InvoicePoolPage = lazy(() => import("./InvoicePoolPage"));
export const LeaveRequestPage = lazy(() => import("./LeaveRequestPage"));
export const MasterDataPage = lazy(() => import("./MasterDataPage"));
export const MobileApprovalsPage = lazy(() => import("./MobileApprovalsPage"));
export const MobileAssetScanPage = lazy(() => import("./MobileAssetScanPage"));
export const MobileCapturePage = lazy(() => import("./MobileCapturePage"));
export const NotificationChannelsPage = lazy(() => import("./NotificationChannelsPage"));
export const NotificationsPage = lazy(() => import("./NotificationsPage"));
export const PurchaseRequestsPage = lazy(() => import("./PurchaseRequestsPage"));
export const ReimbursementDetailPage = lazy(() => import("./ReimbursementDetailPage"));
export const ReimbursementsPage = lazy(() => import("./ReimbursementsPage"));
export const TravelRequestsPage = lazy(() => import("./TravelRequestsPage"));
export const UserManagementPage = lazy(() => import("./UserManagementPage"));
export const WorkflowTaskDetailPage = lazy(() => import("./WorkflowTaskDetailPage"));
export const WorkflowTasksPage = lazy(() => import("./WorkflowTasksPage"));
+89
查看文件
@@ -2,6 +2,86 @@ import { defineConfig } from "vite";
import react from "@vitejs/plugin-react"; import react from "@vitejs/plugin-react";
import { VitePWA } from "vite-plugin-pwa"; import { VitePWA } from "vite-plugin-pwa";
const PAGE_CHUNK_PATTERNS = {
"page-travel": ["TravelRequestsPage"],
"page-assets": ["AssetRegistryPage", "InventoryChecksPage"],
"page-procurement": ["PurchaseRequestsPage"],
"page-workflow": ["WorkflowTasksPage", "WorkflowTaskDetailPage"],
"page-system": [
"AuditLogsPage",
"ApprovalRulesPage",
"DelegationsPage",
"DepartmentsPage",
"ErpIntegrationPage",
"FeatureFlagsPage",
"InvoicePoolPage",
"LeaveRequestPage",
"MasterDataPage",
"NotificationChannelsPage",
"NotificationsPage",
"ReimbursementDetailPage",
"ReimbursementsPage",
"UserManagementPage",
],
"page-mobile": ["MobileCapturePage", "MobileAssetScanPage", "MobileApprovalsPage"],
} as const;
function resolveManualChunk(id: string) {
if (id.includes("node_modules")) {
if (["react", "react-dom", "react-router-dom"].some((pkg) => id.includes(`/node_modules/${pkg}/`))) {
return "vendor-react";
}
if (id.includes("/node_modules/@tanstack/react-query/")) {
return "vendor-query";
}
if (id.includes("/node_modules/@zxing/browser/")) {
return "vendor-scan";
}
if (
id.includes("/node_modules/@radix-ui/") ||
id.includes("/node_modules/class-variance-authority/") ||
id.includes("/node_modules/clsx/") ||
id.includes("/node_modules/tailwind-merge/") ||
id.includes("/node_modules/sonner/")
) {
return "vendor-ui";
}
return undefined;
}
if (
id.includes("/src/api/http.ts") ||
id.includes("/src/api/types.ts") ||
id.includes("/src/api/errorMessages.ts") ||
id.includes("/src/api/auth.ts") ||
id.includes("/src/auth/") ||
id.includes("/src/components/common/") ||
id.includes("/src/hooks/") ||
id.includes("/src/lib/") ||
id.includes("/src/pwa.ts")
) {
return "app-core";
}
if (id.includes("/src/api/masterData.ts") || id.includes("/src/components/ai/")) {
return "app-domain-shared";
}
for (const [chunkName, pageNames] of Object.entries(PAGE_CHUNK_PATTERNS)) {
for (const pageName of pageNames) {
if (id.includes(`/src/router/lazy/${pageName}.ts`)) {
return chunkName;
}
}
}
return undefined;
}
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
react(), react(),
@@ -89,6 +169,15 @@ export default defineConfig({
}, },
}), }),
], ],
build: {
rollupOptions: {
output: {
manualChunks(id) {
return resolveManualChunk(id);
},
},
},
},
server: { server: {
proxy: { proxy: {
"/api": { "/api": {