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:
@@ -8,8 +8,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
(`ojnext` 与 `../OnlineJudge` 都已下线且**完全冻结,一行都不改**)。Vue 3 + TypeScript,
|
||||
Vite(Rolldown 内核)、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` 属性,所以绑定值走
|
||||
|
||||
@@ -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",
|
||||
|
||||
1
apps/web/src/mermaid-legacy.d.ts
vendored
1
apps/web/src/mermaid-legacy.d.ts
vendored
@@ -1 +0,0 @@
|
||||
declare module "mermaid-legacy"
|
||||
@@ -19,8 +19,8 @@ import HelpRequestList from "./HelpRequestList.vue"
|
||||
*
|
||||
* 这个组件静态 import 进来的话,整套 CodeMirror(view / 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 MB(gzip 642 KB → 428 KB),
|
||||
* 老师那边只是在第一次接单时多一次 chunk 请求。
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 的默认构建 target(chrome111)低。所以这个插件必须留着:它同时管两件事
|
||||
// —— 把 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 polyfill(Array.at 等)
|
||||
// 不做 SystemJS 双构建,只给现代产物注入 polyfill;modernTargets 不写,
|
||||
// 用插件自带的基线(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)),
|
||||
|
||||
Reference in New Issue
Block a user