perf(前端): zod 移出首屏包,首屏 JS 721KB → 594KB(gzip 237 → 199KB)

首屏包里带着整个 zod 运行时(~100KB),来路有三条,缺一条都挪不出去(逐条撤回实测过):

- 契约包没有 sideEffects: false,入口从桶里取任何一个值都会把所有 schema 模块带上。
  契约包里没有顶层副作用(核过),加上之后 rolldown 才能按导出摇树
- roles.ts 里的 USER_TYPE / toAdminType 是入口要的,同文件却定义了两个 z.enum。
  schema 挪到 admin.ts(唯一用处),roles.ts 文件头写明不许 import zod
- 启动就要调的 getWebsiteConfig / getAILoginSummary 在 oj/api.ts 里,而那个文件同步
  import 了题目详情、提交详情两个 schema。这两个函数挪到 shared/api.ts;那两道
  闸门保持同步 —— submissionDetailSchema 有 .catch(),放行原文和解析结果不等价
- /me 的契约闸门改成 contractDeferred:先放行原文,schema 按需加载后在后台核、只记
  日志。userProfileSchema 没有 default / transform,两者等价。动态 import 的目标是
  单独的 shared/profileSchema.ts —— 直接 import 契约桶会让 rolldown 重排共享 chunk,
  实测首屏反而涨到 1811KB

浏览器实跑核过:登录后 profile 正常加载、profileSchema 按需拉取、无控制台报错;
故意喂一份不符的数据,后台闸门照常打出 [契约] 分歧日志。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-09-27 15:23:31 +08:00
1 parent 6b2da41882
commit 83b9d23c68
9 files changed
+60 -22

No files matched your search

-10
View File
@@ -27,7 +27,6 @@ import {
type AiDetail,
type DurationData,
type HeatmapItem,
type LoginSummary,
type SolvedList,
type ProblemSet,
type ProblemSetBadge,
@@ -60,7 +59,6 @@ import type {
SubmissionListPayload,
SubmitCodePayload,
OnlineCount,
WebsiteConfig,
Tutorial,
TutorialProgress,
} from "utils/types"
@@ -77,10 +75,6 @@ function detailProblem(value: unknown): Problem {
return contract("GET /problems/:id", problemDetailSchema, value)
}
export function getWebsiteConfig() {
return api.get<WebsiteConfig>("site")
}
/** 当前在线人数。只有聚合数字,「谁在线」在榜单接口里、且只对老师下发 */
export function getOnlineCount() {
return api.get<OnlineCount>("site/online")
@@ -373,10 +367,6 @@ export function getAIHeatmapData(username?: string) {
})
}
export function getAILoginSummary() {
return api.get<LoginSummary>("ai/login-summary")
}
export function getAIPinnedReport() {
return api.get<AiAnalysisRecord | null>("ai/pinned")
}