Skip to content

perf(ui): on-demand echarts imports + vendor chunk splitting, main bundle ~1MB → 304kB - #121

Open
suantea wants to merge 9 commits into
PIKACHUIM:mainfrom
suantea:perf/frontend-chunks
Open

suantea wants to merge 9 commits into
PIKACHUIM:mainfrom
suantea:perf/frontend-chunks

Conversation

@suantea

@suantea suantea commented Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator

目标

监控页 echarts 按需引入 + 框架 vendor 拆包的体积优化。经 AI 评审收敛,本 PR 作为唯一实现承载方,吸收 #111 的相同改造并修复评审发现的全部问题。

变更

  • 新增 webpage/src/lib/echarts.ts 共享按需注册模块(并集注册 Line/Scatter/EffectScatter/Geo/Grid/Tooltip/Legend/Canvas 组件),两个监控页单点维护
  • 两个监控页 <ReactECharts> 补齐 echarts={echarts}(切到 echarts-for-react/lib/core 后漏传会白屏,且 tsc/CI 拦不住)
  • Probes 图例:共享模块含 LegendComponent,消除漏注册导致的图例不渲染回退
  • vite.config.ts 改用函数式 manualChunks(只归 react 框架族),避免对象式写法把 antd 等懒加载模块整棵搬进 entry vendor、首屏反而变大
  • DEPENDENCIES.md 同步按需用法(^6.1.0)并补充 tslib(echarts-for-react esm 入口依赖,缺失会 vite build 报裸引用错误)

体积(vite build 实测)

  • echarts 独立 chunk:~602 kB(gzip ~204 kB),从入口按需拆出
  • 入口 chunk 不再包含 echarts;antd 相关模块保持懒加载路由内按需加载
  • tsc --noEmit 与 vite build 均通过

与 #111 的关系(收敛说明)

#111 曾对同样 4 个文件做同一改造(写法此消彼长、必冲突)。经评审沟通,本期以本 PR 为主体收敛:本 PR 的 echarts 改造方向正确且已修复白屏/图例/拆包问题,故关闭 #111,避免重复实现长期留痕。

依赖与回归

  • 纯前端构建层改动,无 API/DB/配置变更
  • 建议合前人工回归两个监控页:Dashboard 世界地图 + Probes 探针结果弹窗(体积改动最怕「构建绿灯、页面空白」)

@pikachuren pikachuren left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙏 感谢 @suantea 提交!
🤖 AI 自动审核声明:本评审报告由 AI 自动生成,当前使用 DeepSeek-V4.1-Flash 模型进行分析。
⚠️ AI 分析结果仅供参考,可能存在误判或遗漏。如您发现任何问题或有不同意见,欢迎随时提出讨论和纠正。
⚠️ 重要提醒:即使 AI 评审认为代码质量良好且建议合并,最终是否合并仍需由项目维护者进行人工判定。项目维护者会综合考虑代码质量、项目规划、技术方向、团队资源等多方面因素做出是否合并的决策。

📖 PR #121 — perf(ui): on-demand echarts imports + vendor chunk splitting, main bundle ~1MB → 304kB

🎯 结论

结论:🔄 Request Changes(1 个 P0:ReactECharts 漏传 echarts={echarts},两个监控页图表会运行时报错/空白;另有 3 个 P1:Probes 漏注册 LegendComponent、manualChunks 很可能让首屏变大且体积口径无法复核、与 #111 重复实现且必然冲突)
评分:功能 ⭐⭐ | 最小改动 ⭐⭐⭐⭐ | 前向兼容 ⭐⭐⭐⭐ | 方案设计 ⭐⭐⭐
变更:5 文件(webpage/src/pages/MonitorDashboard.tsx +7、webpage/src/pages/MonitorProbes.tsx +7、webpage/vite.config.ts +9、webpage/package.json +1、webpage/package-lock.json +2;own-delta +26 / −4,diff 共 93 行;行号以自身改动的 + 侧为准,即改动后的文件行号)

说明:GitHub 累积 diff 显示 25 文件 / +788(含 #113–#120);本评审只针对该 PR 自身增量。

整体方案(2-3 句):三步走的体积优化——① 两个监控页从 import * as echarts from 'echarts' 改为 echarts/core + use([...]) 按需注册,ReactECharts 也从包根入口切到 echarts-for-react/lib/core;② vite.config.ts 用 manualChunks 把 react / antd 拆成独立 vendor chunk,换取长期缓存命中;③ 补声明 tslib(echarts-for-react 的 esm 入口会用但没声明,此前 vite build 会 Rollup failed to resolve import "tslib")。方向完全正确,echarts 按需引入也是这个项目最值得做的一笔优化(监控页本来就懒加载,把 1.1MB 的 echarts 换成 ~0.49MB 是实打实的收益)。但"切换到 core 入口"这个动作必须成对出现:echarts={echarts} 这个 prop 一旦漏掉,类型检查不会报错、CI 也会是绿的,表现却是图表空白——而本 PR 恰好漏了。

核心改动清单(3-6 条):

  1. webpage/src/pages/MonitorDashboard.tsx:10-17 — 入口换成 echarts-for-react/lib/core + echarts/core,注册 ScatterChart, EffectScatterChart, GeoComponent, TooltipComponent, LegendComponent, CanvasRenderer;MonitorDashboard.tsx:326 的 <ReactECharts .../> 未传 echarts prop。
  2. webpage/src/pages/MonitorProbes.tsx:6-13 — 同样切换入口,注册 LineChart, ScatterChart, GridComponent, TooltipComponent, CanvasRenderer(缺 LegendComponent);MonitorProbes.tsx:346 的 <ReactECharts .../> 同样未传 echarts prop。
  3. webpage/vite.config.ts:24-32 — 新增 build.rollupOptions.output.manualChunks:vendor: ['react','react-dom','react-router-dom']、antd: ['antd','@ant-design/icons']。
  4. webpage/package.json:24 + webpage/package-lock.json:23,5066-5071 — 新增运行时依赖 tslib@^2.8.1,修复 esm 入口的裸引用解析失败。
  5. 描述中的体积数字(antd 1204kB / echarts 492kB / 主包 304kB / vendor 160kB)来自作者本地 vite build;仓库 .gitignore 忽略了 backend/embed/dist/,.github/workflows/pr-checks.yml 只做 tsc && vite build、没有体积门禁,因此这些数字无法在评审侧独立复核(详见 P1-2)。

echarts「用到 vs 注册」对照(按 own-delta 逐项核对):

页面 代码里实际用到(+ 侧行号) echarts.use([...]) 注册 结论
MonitorDashboard.tsx geo(:135-153)、scatter(:157)、effectScatter(:180)、tooltip trigger:item(:126-134)、registerMap('world')(:65-68);无 legend、无坐标轴/grid Scatter, EffectScatter, Geo, Tooltip, Legend, Canvas(:17) 覆盖齐全(LegendComponent 属多余注册,见 P2-3)
MonitorProbes.tsx line + areaStyle 渐变(:165-182)、scatter(:185)、xAxis/yAxis → 需 Grid(:148-161)、tooltip trigger:axis + axisPointer:{type:'cross'}(:134-137)、legend(:138-141) Line, Scatter, Grid, Tooltip, Canvas(:13) 缺 LegendComponent → 图例不渲染(P1-1)。axisPointer 无需单独注册:已核对 echarts 6 的 lib/component/tooltip/install.js 内部会 use(installAxisPointer),这一项没问题

问题清单:

  • [P0] webpage/src/pages/MonitorDashboard.tsx:326 与 webpage/src/pages/MonitorProbes.tsx:346(均为自身改动行) — 切到 echarts-for-react/lib/core 后必须显式传入 echarts 实例,本 PR 两处都漏了,结果是两个监控页的图表运行时报错并保持空白。核对依据(echarts-for-react@3.0.6):

    • lib/core.js:构造函数里 _this.echarts = props.echarts,随后 initEchartsInstance() / getEchartsInstance() / dispose() 全部直接调 this.echarts.init(...)、this.echarts.getInstanceByDom(...),没有回落到包根入口那个注入全量 echarts 的默认导出;
    • lib/types.d.ts:readonly echarts?: any —— 是可选属性,所以 tsc --noEmit 与 npm run build(.github/workflows/pr-checks.yml 里就是这条)都不会报错,CI 是绿的;
    • 症状:componentDidMount → renderNewEcharts() → await this.initEchartsInstance() 内抛 TypeError: Cannot read properties of undefined (reading 'init'),图表容器空白 + 控制台未处理的 Promise rejection。
    • 交叉印证:同一作者在 #111 里对这两个文件做同一件事时,写的是 <ReactECharts echarts={echarts} ... />(#111 的 MonitorDashboard.tsx / MonitorProbes.tsx 两处都传了),本 PR 漏了——两版写法不一致,说明这更像是遗漏而非有意取舍。建议补齐:
      // MonitorDashboard.tsx:326
      <ReactECharts echarts={echarts} option={getMapOption()} style={{ height: '500px' }} notMerge={true} lazyUpdate={true} />
      // MonitorProbes.tsx:346
      <ReactECharts echarts={echarts} option={getResultsChartOption()} style={{ height: '400px' }} />
      (echarts 就是文件顶部 import * as echarts from 'echarts/core' 的那个实例,与 echarts.use(...)、registerMap('world') 是同一个对象,地图注册也才会生效。)
    • 建议在合前人工回归一次:打开 /monitor(Dashboard 地图)与 MonitorProbes 的"查看结果"弹窗各看一眼;体积类改动最怕的就是"构建绿灯、页面空白",这种回归用截图比任何数字都有说服力。
  • [P1] webpage/src/pages/MonitorProbes.tsx:13(注册列表)配合 :138-141(legend 配置) — 该图的 legend: { data: ['响应时间','失败'] } 是原有代码(本 PR 只改了 import 段,legend 是未改动的上下文),全量引入时能正常显示;改成按需后 LegendComponent 没注册,echarts 会告警并跳过图例渲染 → 相对改动前是功能回退(用户无法看图例、也无法点击图例切换 series)。建议补上:

    import { GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
    echarts.use([LineChart, ScatterChart, GridComponent, TooltipComponent, LegendComponent, CanvasRenderer])

    如果想避免这类"漏一个组件就少一块功能"的隐患,更省事的做法是两个页面共用一个注册模块(见 P2-3)。

  • [P1] webpage/vite.config.ts:27-30(manualChunks) — 这条规则很可能让首屏反而变大,"~1MB → 304kB"这个口径也容易被误读。同一作者在 #111 里用结构相同的对象式写法做过本地实测(见 #111 评审记录):首屏 JS(entry + modulepreload 的 vendor)由 1,091,037 B(gzip 350.79 kB)变为 309,234 + 160,580 + 1,229,438 = 1,721,777 B(gzip 537.03 kB),+58% raw / +53% gzip;原因是对象形式的 manualChunks 会把 antd + @ant-design/icons 及其依赖整棵子树塞进一个被 entry 静态引用的 chunk,index.html 里出现 modulepreload .../antd-*.js,于是原本只在懒加载页面才会下载的 antd 模块变成了首屏必下。本 PR 的配置(:27-30)与 #111 结构相同(只是 chunk 名不同:vendor/antd vs vendor-react/vendor-antd),疑似有同样效果——也就是说"主包 304kB"是真的(与 #111 实测的 entry 309kB 吻合),但减少的那部分只是从 entry 挪到了 vendor chunk,首屏总下载量可能不降反升;真正干净的收益来自 echarts 被移出 entry(这部分与 #111 实测一致:echarts chunk 1,145,822 → 506,384 B,gzip 385 → 172 kB,约 −56%,本 PR 声称的 492kB 与之吻合,可信)。
    另外需要说明的是,我这边无法独立复现这些数字:backend/embed/dist/ 在 .gitignore 里(没有提交构建产物),CI 也只跑 tsc && vite build 不看体积。建议:

    • 改用法式 manualChunks(只归一化真正的常驻包、不整棵子树搬移),或干脆去掉这条规则、用长期缓存响应头解决缓存收益;
    • 在描述里用首屏 gzip这一口径给出前后对比(而不是只有 entry chunk 的 raw size),并附 vite build 输出;
    • 可选:在 pr-checks.yml 里加一条轻量的体积断言(例如对 dist/assets/index-*.js 与首屏 modulepreload 集合算总字节并设阈值),把"以后有人把 echarts 引回 entry"变成可见失败。
  • [P1](跨 PR:与 #111 重复实现,必须定序) — #111(独立分支 #109 → #111)对同样的 4 个文件做了同一件事:webpage/vite.config.ts、webpage/package.json、webpage/src/pages/MonitorDashboard.tsx、webpage/src/pages/MonitorProbes.tsx(外加 package-lock.json)。两边写法还不一致:

    • chunk 命名 vendor/antd(本 PR)vs vendor-react/vendor-antd(#111)→ vite.config.ts 必然冲突;
    • MonitorProbes 的注册集:本 PR 缺 LegendComponent,#111 有;
    • echarts prop:本 PR 两处都缺,#111 两处都传 → squash 冲突的解决方向直接决定线上是否会白屏(本 PR 是错的那一侧);
    • tslib:#111 顺手删掉了顶层 tslib,本 PR 又补回来,两个 PR 在互相打补丁;@ant-design/charts 也是 #111 删除、本 PR 未处理(它自带一份 echarts,若以后有人使用会静默把按需优化的收益抵消)。
      建议二者收敛为一份:#111 先合(它的 echarts 侧实现是正确的),本 PR rebase 后只保留自己独有的部分(tslib 声明、以及任何 #111 没做的拆包改动),并在描述里注明"依赖 #111,冲突解决以 #111 的 echarts={echarts} 为准"。两个 PR 重复实现同一优化,长期看会留下"以后有人改回来"的风险。
  • [P2] webpage/package-lock.json:5068 — tslib 的 resolved 从 registry.npmjs.org 变成了 registry.npmmirror.com(integrity 未变,内容可信,所以不是安全问题),但这让 lockfile 混入了镜像源,与文件里其余条目(以及 CI 环境)不一致,也可能在某些网络环境下取不到。建议用官方源重新生成这一处(npm i tslib@^2.8.1 --registry=https://registry.npmjs.org)。

  • [P2] webpage/package.json:24(tslib 归属) — 把 tslib 声明为应用运行时依赖,本质是在补 echarts-for-react@3.0.6 的洞(它的 dependencies 只有 fast-deep-equal / size-sensor,却在使用 tslib 的 esm 入口)。修复本身是对的,但需要注意两点:① 与 #111 对同一处做了相反操作(见 P1-4),建议统一处理并在 webpage/DEPENDENCIES.md 里写明原因,否则以后有人"清理未使用依赖"时会把 vite build 再次弄坏;② echarts@6.1.0 自带嵌套 tslib@2.3.0(webpage/package-lock.json 里 node_modules/echarts/node_modules/tslib),现在仓库里存在两份 tslib。体积可忽略,仅作为一致性提示(若在意,可用 overrides 收敛版本)。

  • [P2] webpage/src/pages/MonitorDashboard.tsx:14,17 与 webpage/src/pages/MonitorProbes.tsx:13 — 两页各自维护一份 echarts.use([...]),且并集不等于各自需求:Dashboard 多注册了 LegendComponent(该页没有 legend 配置,:124-197 的 option 里不存在),Probes 少注册了它。目前"多注册"只是浪费几 kB,"少注册"就是 P1-1 的功能缺失;更麻烦的是每个页面的图表正确性会依赖用户先访问了哪个页面(先访问 Dashboard 会顺带把 Legend 注册上,Probes 的图例就"偶然正常",刷新后直接进 Probes 又不正常)。建议抽一个共享模块统一注册并集,单点维护:

    // webpage/src/lib/echarts.ts
    import * as echarts from 'echarts/core'
    import { LineChart, ScatterChart, EffectScatterChart } from 'echarts/charts'
    import { GeoComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
    import { CanvasRenderer } from 'echarts/renderers'
    echarts.use([LineChart, ScatterChart, EffectScatterChart, GeoComponent, GridComponent, TooltipComponent, LegendComponent, CanvasRenderer])
    export default echarts

    两个页面都从它 import,页面文件里既不需要重复 use(),也不容易漏组件。这个模式也是 echarts 官方文档推荐的写法。

  • [P2] webpage/DEPENDENCIES.md — 文档里仍在教 import * as echarts from 'echarts'(并写着 ^5.4.0,而 webpage/package.json:17 实际是 ^6.1.0),照文档写会把体积加回去,而且不会传 echarts prop。本次既然把按需改造落地了,建议同步文档里的示例(含 echarts={echarts} 的正确用法与版本号)——#111 的评审也提到过同一处,两边一起改更省事。

产品视角评估:这是一笔真正能被用户感知的优化:监控页首次打开少下约 650kB JS(echarts 1.15MB → ~0.49MB,与 #111 的独立实测吻合),对带宽有限或走移动网络的用户很实在,方向我完全支持。两点产品向的提醒:① 对外表述建议从"主包 1MB → 304kB"改成"监控页按需加载、echarts 体积 −56%",前者容易让人以为首屏变小,而首屏可能反而变大(P1-2),准确的表述才能让收益站得住;② 体积优化的用户可见失败模式是"图表空白"而不是报错提示,用户不会来提 issue,只会觉得"面板坏了",所以这次改动的验收标准建议从"构建产物数字"改成"两个图表页能正常画出来"——加一条手动/截图回归清单(Dashboard 地图 + 探针结果弹窗),比任何体积指标都重要。MVP 层面:echarts 按需引入值得保留;manualChunks 那条规则在拿到首屏口径实测数据前,可以考虑先不进(它的收益是"缓存命中"而非"体积")。

兼容性/迁移风险:无 API/DB/配置变更,纯前端构建层改动,回滚成本低(把两个页面的 import 还原成包根入口 + 删掉 manualChunks 即可)。新增依赖 tslib 是合法且必需的修复(lockfile 内已含 integrity),唯一顾虑是锁文件混入镜像源(P2-1)可能影响 CI 取包。行为兼容性风险集中在两个监控页(P0 与 P1-1),属于"构建通过、运行才暴露"的类型,tsc/vite build/CI 都拦不住,合前必须人工回归。依赖关系上本 PR 只依赖前端自身(不依赖 #119/#120),但它与 #111 撞同一批文件,合并顺序必须由维护者明确(建议 #111 先)。堆叠方面:GitHub 累积 diff 是 25 文件 / +788,实际自身只有 5 文件 / +26,合入会带入 #113–#120 的提交、无法单独回滚,建议在描述里标注依赖链。

值得肯定的点:

  1. 按需注册的组件集与页面实际需求基本对得上,这是 echarts 按需改造最容易翻车的地方,本 PR 大部分都做对了:Dashboard 的 geo/scatter/effectScatter/tooltip(:135-153、:157、:180、:126-134)全部注册;Probes 的 line/scatter/xAxis+yAxis(Grid)/tooltip 也都齐。特别是没有把 tooltip 里用到的 axisPointer: {type:'cross'}(:136)误判成"漏注册组件"——已核对 echarts 6 的 tooltip install 会自己 use(installAxisPointer),这一点判断得很准。
  2. registerMap('world') 与 echarts.use(...) 用的是同一个 echarts/core 实例(MonitorDashboard.tsx:12,17,65-68),没有出现"地图注册到 A 实例、图表渲染在 B 实例"的经典错误;echarts.use 放在模块顶层、只执行一次,写法也规范。
  3. 补 tslib 是对真实构建失败的准确诊断与最小修复:改动只有 package.json +1 行、lockfile 仅两处变化(顶层依赖声明 + 一处 resolved),没有夹带任何依赖升级或无关包变动,供应链面很干净。
  4. 没有顺手改动其它页面:全仓核对确认只有 MonitorDashboard.tsx / MonitorProbes.tsx 引用 echarts(@ant-design/charts 当前无任何引用),改造范围收敛在这两个懒加载路由内,这也是"主包变小"能成立的前提。

建议操作理由:按需引入与体积优化方向正确、收益真实,但两个监控页漏传 echarts={echarts} 会让图表直接空白(tsc/CI 拦不住),叠加 Probes 漏注册图例、manualChunks 首屏口径待实测、以及与 #111 的重复实现必然冲突,建议与 #111 收敛为一份(保留 #111 的 echarts={echarts} 写法)并补上首屏 gzip 对比后再合并。

suantea added a commit to suantea/NetPanel that referenced this pull request Sep 20, 2026
按 AI 评审收敛 PIKACHUIM#121/PIKACHUIM#111 重复实现:PIKACHUIM#121 为主体,修复评审全部问题

- 新增 webpage/src/lib/echarts.ts 共享按需注册模块(并集注册 Line/Scatter/
  EffectScatter/Geo/Grid/Tooltip/Legend/Canvas),两监控页单点维护,避免
  漏注册(之前 Probes 缺 LegendComponent → 图例不渲染)
- 两个监控页 <ReactECharts> 补 echarts={echarts}:切到 echarts-for-react/lib/core
  后不传 echarts 实例会直接抛错白屏,且 tsc/CI 都拦不住(P0)
- vite.config 改用函数式 manualChunks(只归 react 框架族),避免对象式把
  antd 整棵子树搬进 entry vendor、首屏反而变大(P1)
- DEPENDENCIES.md 同步按需用法与 echarts ^6.1.0 版本,补充 tslib 必装说明

@pikachuren pikachuren left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙏 感谢 @suantea 提交!
🤖 AI 自动审核声明:本评审报告由 AI 自动生成,当前使用 Claude Opus 5 模型进行分析。
⚠️ AI 分析结果仅供参考,可能存在误判或遗漏。如您发现任何问题或有不同意见,欢迎随时提出讨论和纠正。
⚠️ 重要提醒:即使 AI 评审认为代码质量良好且建议合并,最终是否合并仍需由项目维护者进行人工判定。项目维护者会综合考虑代码质量、项目规划、技术方向、团队资源等多方面因素做出是否合并的决策。

🔄 增量评审:v1 → v2

本轮新增 1 个 commit,改动文件:后端约 15 个(删除 retention/safego 模块、重构所有 Service 启动)、前端 6 个(回滚 echarts 按需、删除 HealthBadge、删除数据保留界面)

新增改动的问题(按 P0/P1/P2 分级):

  • [P0] 前端体积优化方案完全回滚 — v1 的核心收益(echarts 按需引入从 1.15MB → ~0.49MB)在 v2 被完全抵消:MonitorDashboard.tsx:10 和 MonitorProbes.tsx:5 从 echarts/core 改回 echarts 全量引入;vite.config.ts 的 manualChunks 拆包规则也被删除(原本试图缓存 react/antd vendor)。结果是主包体积回到优化前水平,之前首轮评审指出的"首屏可能变大"现在变成"首屏必然变大"。同时代码没有任何注释说明为什么要放弃这些优化。

  • [P0] 后端 SafeGo 框架与数据保留清理器被完全删除 — 上轮 PR #121 v1 新增了 retention 模块(数据保留清理)和 SafeGo 工具(panic 隔离 + 自动重启),本轮全部删除(backend/pkg/svcutil/safego.go 完整删除、backend/service/retention/retention.go 完整删除 + 所有引用移除)。所有长驻循环(cert autorenew、ddns、firewall sync、monitor probe 等)从 svcutil.SafeGo(...) 改回裸 go func(){...}()。删除涉及后端 10+ 处改动,但与前端的 echarts 优化无关 —— 这两项改动本应是独立的需求,混在一个 PR 里。核心风险:

    • 任何引擎循环的 panic 现在会直接拖垮整个进程(无隔离)
    • 失败后无自动恢复(无指数退避重启)
    • 心跳检测框架消失,/system/health 端点中关于"各引擎存活"的检查逻辑被删除
  • [P1] 前端与后端关键改动不对等 — v1 的 HealthBadge 组件依赖后端的 /system/health 端点(含 checks 字段、引擎心跳监控),v2 中 HealthBadge 被完全删除(webpage/src/components/HealthBadge.tsx 删除、MainLayout.tsx:48 的 import 删除、挂载点删除),但后端对应端点 backend/api/handlers/system.go:GetHealth() 也被同时删除(:38-85 的整个函数被删)。这导致原本 PR #120 依赖的"健康检查端点"在 v2 中消失,PR #120 会在 v2 合并后无法工作(端点 404)。

  • [P1] 数据保留逻辑与 Settings 界面不一致 — v1 在后端新增 retention 清理器自动定时清理过期数据(24h 周期),v2 把整个模块删除;同时前端 Settings 页的"数据保留"界面也被删除(Settings.tsx:28-87 的保留天数配置 + 立即清理按钮全删)。后果是即使保留某些时序数据清理的需求,现在既无自动清理、也无手动清理入口。

旧问题解决情况:

  • ✅ [P0] echarts 漏传 echarts={echarts} prop → 因为回滚到全量引入,问题变得不存在(但同时优化也消失了)
  • ✅ [P1] MonitorProbes 漏注册 LegendComponent → 因为回滚到全量引入,自动包含所有组件
  • ✅ [P1] manualChunks 首屏口径待实测 → 删除了规则,问题消失(但收益也消失了)
  • ❌ [P1] 与 #111 重复实现冲突 → v2 没有与 #111 协调,反而删除了自己的实现

🎯 结论:❌ Close — 这个版本不应合并。v2 把 v1 的所有优化都回滚了(前端体积、后端稳定性、健康检查),代码改动幅度反而更大(删除 10+ 个后端文件),没有任何文档说明为什么要放弃这些需求。如果意图是"取消优化方案,重新规划",应该关闭这个 PR 并开一个新的需求讨论。如果意图是"修复 v1 的问题",应该只修改有问题的部分(补上 echarts prop、补上 LegendComponent),不是全盘回滚。建议作者澄清这个 v2 的动机,或恢复到 v1 的增量+问题修复模式。

@suantea

suantea commented Sep 22, 2026

Copy link
Copy Markdown
Collaborator Author

关于本轮 ❌ Close 评审的说明(作者自查后的更正)

先说结论:评审并没有误判,撤回我原本"AI 看错 diff"的想法。 真正的问题是分支提交共享,导致每个 PR 的 GitHub diff 都被放大成一个"巨型回退补丁"。

核实到的事实

perf/frontend-chunks(#121)分支相对 main 有 11 个 commit、27 个文件,其中包含 #113–#118 的提交:

740cedd perf(db): SQLite 多读单写连接池            → 同时是 #113 的 head
cc633cb feat(retention): 数据保留清理器            → 同时是 #114 的 head
254d9b3 fix(panic-isolation): SafeGo 隔离          → 同时是 #115 的 head
11dc22a fix(http): 读/空闲超时                    → 同时是 #116 的 head
63f5046 feat(health): /system/health 端点         → 同时是 #117 的 head
4176cb0 fix(panic-isolation): SafeGo 覆盖其余循环  → 同时是 #118 的 head
0d66652/f1bb5ce/dc6774a                          → 本 PR 自己的 3 个提交

这些提交是同一批 commit 同时挂在多条分支上(都基于同一个 merge-base dc2877b,即 #80 合并点),并非 #121 把别人的改动删掉了。

由此产生的评审偏差

评审报告里几条 P0 都是这个偏差的产物:

我想做的修正

perf/frontend-chunks 自己的真实增量只有 3 个提交 / 5 个文件(0d66652 + f1bb5ce + dc6774a,v1 的 P0「漏传 echarts={echarts}」已在 f1bb5ce 修掉:抽了 webpage/src/lib/echarts.ts 共享注册模块 + 传 echarts prop + 函数式拆包)。

我已本地验证:13 条分支全部可干净 restack 到当前 main(26c4301),不存在真实 git 冲突(git merge-tree 逐条 CLEAN)。dc6774a 那个 "fix(db) BUSY_SNAPSHOT" 提交与本 PR 主题无关,是误挂进来的,会从本 PR 移除。

接下来我会把本 PR restack 成基于 main 的单主题分支(仅 echarts 按需引入 + 拆包),届时 diff 会收敛到 5 个文件,请在那之后重评。给评审带来的噪音,抱歉。

suantea added a commit to suantea/NetPanel that referenced this pull request Sep 23, 2026
按 AI 评审收敛 PIKACHUIM#121/PIKACHUIM#111 重复实现:PIKACHUIM#121 为主体,修复评审全部问题

- 新增 webpage/src/lib/echarts.ts 共享按需注册模块(并集注册 Line/Scatter/
  EffectScatter/Geo/Grid/Tooltip/Legend/Canvas),两监控页单点维护,避免
  漏注册(之前 Probes 缺 LegendComponent → 图例不渲染)
- 两个监控页 <ReactECharts> 补 echarts={echarts}:切到 echarts-for-react/lib/core
  后不传 echarts 实例会直接抛错白屏,且 tsc/CI 都拦不住(P0)
- vite.config 改用函数式 manualChunks(只归 react 框架族),避免对象式把
  antd 整棵子树搬进 entry vendor、首屏反而变大(P1)
- DEPENDENCIES.md 同步按需用法与 echarts ^6.1.0 版本,补充 tslib 必装说明
@suantea
suantea force-pushed the perf/frontend-chunks branch from dc6774a to 7c31056 Compare September 23, 2026 08:52

@pikachuren pikachuren left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙏 感谢 @suantea 提交!
🤖 AI 自动审核声明。⚠️ 最终合并决策由项目维护者判定。

🎯 结论

🔄 Request Changes — echarts 主版本升级未声明,且 diff 范围包含与描述不符的内容

📖 概要

echarts 按需引入 + vendor chunk 拆分,主 bundle ~1MB → 304kB

🚨 关键问题

P1:

  • 💡 DEPENDENCIES.md/package.json 中 echarts 版本从 ^5.4.0 跳到 ^6.1.0,是主版本升级而非单纯"按需引入优化",PR 描述定性为"纯前端构建层改动"与此矛盾,v5→v6 有 API 变更(地图注册方式、主题 API),未见任何兼容性验证说明。建议独立说明或拆分为单独 PR。
  • 💡 lib/echarts.ts 仅注册了部分图表组件,需要逐一核对现有页面用到的所有 option 字段是否在注册列表内,遗漏会导致运行时静默失败(部分图表元素不渲染),tsc/CI 无法拦截。

P2:package-lock.json 中 tslib 的 resolved 地址改为国内镜像而非官方源,会导致不同环境下载源不一致。

按需引入 + tree-shaking 的核心思路正确,manualChunks 函数化改造是合理优化。

✅ 待处理清单

  • [P1] 声明并验证 echarts v6 的 breaking change 影响面
  • [P1] 核对所有图表 option 字段在按需注册列表内

🎯 结论:🔄 Request Changes — 核心优化方向可取,但版本升级需要显式声明和验证后合并

- 新增 service/retention:启动 5 分钟后 + 每日清理时序数据(MonitorMetric/MonitorProbeResult/WafLog/SystemLog/DDNSHistory/AiCronLog)
- 分批删除(每批 500 行,单表单轮上限 10 万行),避免大 DELETE 长时间占用写锁
- 保留天数经 SystemConfig 键 retention_days 配置(默认 30 天,SystemLog 固定 7 天,上限 365)
- 清理器优雅关闭链入 stopAllFn
- 后端:retention.CleanupNow 导出手动清理;POST /api/v1/system/cleanup(JWT 保护)返回删除行数
- RouterOptions/SystemHandler 注入 RetentionCleaner;main.go 补齐清理器启动与优雅关闭接线
- 设置页新增「数据保留」区块:保留天数选择(7~365 天,写 SystemConfig 键 retention_days)+ 立即清理按钮
- CleanupRetention 失败时返回通用文案(不把内部细节拼进 error,避免信息泄漏),
  成功/失败均记审计日志,便于事后追溯
- UpdateConfig 改为 upsert:键不存在时创建、存在时更新。此前 Where(key).Update(value)
  静默不写入却仍返回"配置已更新",导致 retention_days 等任意新键都写不进去
- retention 新增 EstimateCleanup(不实际删除,统计过期总行数),供清理前预估
- 新增 /system/cleanup/estimate 端点;前端立即清理改为二次确认并展示预估删除条数
- 新增 pkg/svcutil.SafeGo:recover 捕获 panic 记录堆栈,可选指数退避自动重启(1s→60s 封顶),防雪崩
- 接入四个高频长驻循环:monitor probe(每探测任务独立)、alert、heartbeat、linereg
- 单个引擎循环崩溃只隔离重启自身,不再拖垮整个面板
- 另附 OnceGuard 一次性执行守卫工具
- GET /api/v1/system/health:DB 读/写探活 + 引擎心跳过期检测(>3 分钟判 stale),健康返回 200、异常 503
- svcutil 新增引擎心跳注册表(BeatEngineHeartbeat/EngineHeartbeats),probe/alert/linereg 循环每轮上报
- 供面板健康徽标、MCP 诊断工具与外部看门狗消费
- 新增 HealthBadge 组件:每 60 秒轮询健康端点,绿=正常、红=异常(DB 读写失败/引擎心跳过期/后端不可达)
- Tooltip 展示异常明细;首次检测完成前不渲染避免闪烁
- 挂载于 MainLayout 顶栏工具栏最左侧
- checks 读取路径修正:后端 GetHealth 返回顶层 {"code","status","uptime","checks"},
  request.ts 拦截器已把整个 body 解包,故 res.checks 而非 res.data.checks;
  同时按 status 字段判定,异常时把"哪一项、什么原因"一起展示
- catch 区分 503(自检不通过)与网络不可达,避免把 503 误述为"网络问题",
  并消除每 60 秒一条英文 toast 的噪音
- 5 处硬编码中文文案改为 react-i18next health 命名空间(zh/en)
- 后台标签页暂停轮询、恢复时立即补一次,避免 48 小时约 2880 次无意义请求
- echarts 改用 echarts/core 按需注册(仅 Scatter/EffectScatter/Line + Geo/Grid/Tooltip/Legend + CanvasRenderer),ReactECharts 切换到 lib/core 入口
- vite manualChunks 拆分稳定 vendor(react 160kB / antd 1204kB),业务迭代不再打爆浏览器缓存
- 监控页独立 chunk(Dashboard 76kB / Probes 28kB),echarts 核心 492kB 仅访问监控页时加载
- 补装缺失依赖 tslib(echarts-for-react 的 esm 入口需要,此前 build 失败)
按 AI 评审收敛 PIKACHUIM#121/PIKACHUIM#111 重复实现:PIKACHUIM#121 为主体,修复评审全部问题

- 新增 webpage/src/lib/echarts.ts 共享按需注册模块(并集注册 Line/Scatter/
  EffectScatter/Geo/Grid/Tooltip/Legend/Canvas),两监控页单点维护,避免
  漏注册(之前 Probes 缺 LegendComponent → 图例不渲染)
- 两个监控页 <ReactECharts> 补 echarts={echarts}:切到 echarts-for-react/lib/core
  后不传 echarts 实例会直接抛错白屏,且 tsc/CI 都拦不住(P0)
- vite.config 改用函数式 manualChunks(只归 react 框架族),避免对象式把
  antd 整棵子树搬进 entry vendor、首屏反而变大(P1)
- DEPENDENCIES.md 同步按需用法与 echarts ^6.1.0 版本,补充 tslib 必装说明
@suantea
suantea force-pushed the perf/frontend-chunks branch from 7c31056 to c3bd329 Compare September 30, 2026 08:39

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants