chore(前端): 浏览器基线从 Chrome < 94 提到 105,删掉 mermaid@9 那套 fallback

机房只有部分电脑还是 Chrome 105,其余更新,按最低那档定基线。

- legacy 插件留着:vite 8 的默认 build.target 是 chrome111,比机房高。
  modernTargets 不写,用插件自带的 chrome>=105 基线,正好是这一档。
- polyfill 清单按 105 重新探测,63 → 50 项,仍然写死:自动探测要对每个产物
  跑 Babel 扫描,构建 3s → 12s。写死后产出的 polyfills chunk 与自动探测同尺寸。
- 删 mermaid-legacy(mermaid@9)、useMermaid 里按 UA 分叉的 v9 回调式 render、
  为它存在的 cytoscape UMD→ESM 别名 —— 105 直接用 mermaid 11。
- View Transitions 要 111,darkTransition 的降级分支保留,注释改成 105。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 07:43:21 -06:00
parent 688005c081
commit 3559ae4d6f
10 changed files with 171 additions and 207 deletions

View File

@@ -8,8 +8,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
`ojnext``../OnlineJudge` 都已下线且**完全冻结,一行都不改**。Vue 3 + TypeScript
ViteRolldown 内核、Naive UI、Pinia、Vue Router。
**要兼容机房的老 Chrome< 94**`vite.config.ts` 的 legacy 配置与
`mermaid-legacy` 等 fallback 依赖不能动,理由写在该文件的注释里。
**浏览器基线是 Chrome 105**机房部分电脑那一档2026-09-16 从 < 94 上调):
`vite.config.ts``@vitejs/plugin-legacy` 和写死的 polyfill 清单不能删 —— vite 8
默认 target 是 chrome111比机房高。`mermaid-legacy` 那条 < 94 的 fallback 已删除。
理由写在该文件的注释里,详见 `../CLAUDE.md`
## Commands
@@ -138,7 +140,7 @@ return contract("GET /problems/:id", problemDetailSchema, value)
时区常量在契约 `@oj2/contract``TIME_ZONE_OFFSET_MINUTES`,和后端 `time.ts` 共用。
实现是「平移固定偏移 + 读 `getUTC*`」,不用 `Intl` 的时区选项:东八区没有夏令时,
纯算术在表格里逐格调用也不费事,老 Chrome 上结果也一致
纯算术在表格里逐格调用也不费事,和后端 `time.ts` 算得一模一样
**`n-date-picker` 要平移**`admin/contest/detail.vue``admin/problemset/edit.vue`
Naive 的日期选择器按浏览器本地时区渲染、没有 `timezone` 属性,所以绑定值走

View File

@@ -43,7 +43,6 @@
"lib0": "0.2.117",
"md-editor-v3": "^6.5.6",
"mermaid": "^11.17.2",
"mermaid-legacy": "npm:mermaid@^9.4.3",
"naive-ui": "^2.45.2",
"nanoid": "^6.0.1",
"normalize.css": "^8.0.1",

View File

@@ -1 +0,0 @@
declare module "mermaid-legacy"

View File

@@ -19,8 +19,8 @@ import HelpRequestList from "./HelpRequestList.vue"
*
* 这个组件静态 import 进来的话,整套 CodeMirrorview / state / language /
* autocomplete / lang-*)就跟着 App.vue 进了入口 chunk —— 首屏白白多下 640 KB
* gzip 后 210 KB而下面那个 v-if 决定了学生根本不渲染它机房那批
* Chrome 91 的老机器解析这些字节是实打实的开销。
* gzip 后 210 KB而下面那个 v-if 决定了学生根本不渲染它 —— 机房那批老机器
* 解析这些字节是实打实的开销。
*
* 拆成异步之后首页的 JS 从 1.9 MB 降到 1.3 MBgzip 642 KB → 428 KB
* 老师那边只是在第一次接单时多一次 chunk 请求。

View File

@@ -22,7 +22,7 @@ export function useDarkTransition() {
function toggleDark(event: MouseEvent) {
if (!document.startViewTransition) {
// 机房那批 Chrome 低于 94没有 View Transitions,直接切、不做动画。
// View Transitions 要 Chrome 111机房那批 105 没有,直接切、不做动画。
isDark.value = !isDark.value
return
}

View File

@@ -251,26 +251,14 @@ function applyFlowchartDisplayStyle(container: HTMLElement) {
svg.insertBefore(style, svg.firstChild)
}
function getChromeVersion(): number {
const match = navigator.userAgent.match(/Chrome\/(\d+)/)
return match ? parseInt(match[1]) : Infinity
}
let mermaidPromise: Promise<any> | null = null
let mermaidIsLegacy = false
function loadMermaid(): Promise<any> {
// 缓存 Promise 而不是实例:同一屏里两个组件一起挂载时,缓存实例会让两边都
// 落进 if (!mermaidInstance)import 两次、initialize 两次
if (!mermaidPromise) {
mermaidPromise = (async () => {
let instance: any
if (getChromeVersion() < 94) {
instance = (await import("mermaid-legacy")).default
mermaidIsLegacy = true
} else {
instance = (await import("mermaid")).default
}
const instance = (await import("mermaid")).default
instance.initialize({
startOnLoad: false,
securityLevel: "strict",
@@ -313,17 +301,7 @@ export function useMermaid() {
try {
const m = await loadMermaid()
const id = `mermaid-${getRandomId()}`
// v9 (mermaid-legacy): callback-based render(id, code, cb)
// v10+: Promise-based render(id, code) → { svg }
const svg = mermaidIsLegacy
? await new Promise<string>((resolve, reject) => {
try {
m.render(id, mermaidCode, resolve)
} catch (e) {
reject(e)
}
})
: (await m.render(id, mermaidCode)).svg
const { svg } = await m.render(id, mermaidCode)
if (gen !== renderGeneration) return
container.innerHTML = svg
applyFlowchartDisplayStyle(container)

View File

@@ -6,21 +6,25 @@ import AutoImport from "unplugin-auto-import/vite"
import Components from "unplugin-vue-components/vite"
import { NaiveUiResolver } from "unplugin-vue-components/resolvers"
// 显式保留 Chrome 90 所需的运行时兼容项,避免 plugin-legacy 对每个产物执行 Babel 扫描。
// 升级前端依赖后需要重新审计此列表。
// 机房电脑的 Chrome 是 105 一档,比 plugin-legacy 的现代基线chrome>=105正好压线
// 但比 vite 8 的默认构建 targetchrome111低。所以这个插件必须留着它同时管两件事
// —— 把 build.target 压到 chrome105/es2020再给现代产物补 core-js 的 API polyfill。
//
// 下面这份清单是 `modernPolyfills: true` 自动探测出来的 50 项,抄下来写死:自动探测要
// 对每个产物跑一遍 Babel 扫描,构建从 3 秒涨到 12 秒。**升级前端依赖后重新审计一次**
// DEBUG=vite:legacy bun run build # 会打印 modern polyfills 的全集
// 少一项就是老机器上一个静默的 TypeError这批缺的大多是 Chrome 110+ 的
// Array.prototype.toSorted / Set 运算 / 迭代器辅助)。
const polyfills = [
"es.aggregate-error.cause",
"es.array-buffer.detached",
"es.array-buffer.transfer-to-fixed-length",
"es.array-buffer.transfer",
"es.array.at",
"es.array.find-last-index",
"es.array-buffer.transfer-to-fixed-length",
"es.array.includes",
"es.array.push",
"es.array.to-reversed",
"es.array.to-sorted",
"es.array.to-spliced",
"es.array.with",
"es.error.cause",
"es.iterator.constructor",
"es.iterator.drop",
"es.iterator.every",
@@ -34,9 +38,9 @@ const polyfills = [
"es.iterator.to-array",
"es.json.parse",
"es.json.stringify",
"es.map.get-or-insert-computed",
"es.map.get-or-insert",
"es.object.has-own",
"es.map.get-or-insert-computed",
"es.regexp.escape",
"es.regexp.flags",
"es.set.difference.v2",
"es.set.intersection.v2",
@@ -45,11 +49,6 @@ const polyfills = [
"es.set.is-superset-of.v2",
"es.set.symmetric-difference.v2",
"es.set.union.v2",
"es.string.at-alternative",
"es.typed-array.at",
"es.typed-array.find-last-index",
"es.typed-array.find-last",
"es.typed-array.set",
"es.typed-array.to-reversed",
"es.typed-array.to-sorted",
"es.typed-array.with",
@@ -57,8 +56,8 @@ const polyfills = [
"es.uint8-array.set-from-hex",
"es.uint8-array.to-base64",
"es.uint8-array.to-hex",
"es.weak-map.get-or-insert-computed",
"es.weak-map.get-or-insert",
"es.weak-map.get-or-insert-computed",
"esnext.array.group",
"web.dom-exception.stack",
"web.immediate",
@@ -95,11 +94,10 @@ export default defineConfig(({ mode }) => {
return {
plugins: [
vue(),
// 机房存在 Chrome 91 等旧浏览器:不做 SystemJS 双构建,
// 只按使用情况给现代产物注入 core-js API polyfillArray.at 等)
// 不做 SystemJS 双构建,只给现代产物注入 polyfillmodernTargets 不写
// 用插件自带的基线chrome>=105 / firefox>=106 / safari>=16.4),正好是机房那档。
legacy({
renderLegacyChunks: false,
modernTargets: "chrome>=90",
modernPolyfills: polyfills,
}),
AutoImport({
@@ -142,9 +140,6 @@ export default defineConfig(({ mode }) => {
envPrefix: "PUBLIC_",
resolve: {
alias: {
// mermaid-legacy (mermaid@9) 写死了 UMD 路径,新版 cytoscape 的 exports
// 不允许 import 条件访问它,转到 ESM 产物
"cytoscape/dist/cytoscape.umd.js": "cytoscape/dist/cytoscape.esm.mjs",
utils: fileURLToPath(new URL("./src/utils", import.meta.url)),
oj: fileURLToPath(new URL("./src/oj", import.meta.url)),
admin: fileURLToPath(new URL("./src/admin", import.meta.url)),