一觉醒来,大模型就帮我排查完页面性能问题

最近遇到一个性能问题,我让大模型自己去处理,然后就去午休了,醒来之后,它还真的把问题找出来并修复了

先说结果

最终定位出来的根因是:

这个业务工作台的查询表单,把 Formily 的 form 实例塞进了带 devtools 的 Zustand store。

这件事在开发环境里会非常要命,因为 form 不是一个轻量对象,它里面带着大量字段状态、reaction、effect、schema 和联动信息。

一旦进了 store,又被 devtools 观察、快照、序列化,内存就会被瞬间放大。

如果把这里面的角色先拆开,其实更容易理解:

  • Formily form 是一个重量级运行时实例,里面不只是表单值,还带着字段树、联动关系和各种内部状态
  • Zustand store 适合放跨组件共享的轻量状态,比如布尔值、筛选条件、选中 id 这类数据
  • devtools 在开发环境下会持续记录 store 变化,所以一旦把重对象塞进去,调试链路里的成本也会跟着被放大

更准确地说,这里的问题不是"Zustand 绝对不能放 Formily 对象",而是:

Formily form 这种重量级运行时实例,本来就不适合进会被 devtools 观察、快照和序列化的 store state;而 devtools 又把这个问题成倍放大了。

所以如果只问"主要是不是 devtools 的影响",答案是:

  • 是,主要是 devtools 把问题放大了
  • 根上还是对象放错了位置
  • 就算没有 devtools,这种设计也依然不优雅,只是未必会炸得这么明显

修完之后,Playwright 复现出来的内存数据从:

  • 修复前:839MB ~ 887MB

变成了:

  • 修复后:165MB ~ 207MB

这里的数字来自页面里已有的内存日志采样,主要用来做同一场景下的前后对比,不是一份精确的 heap profile。

也就是说,那个最扎眼的"900MB 级内存高峰",确实被打下来了。

问题是什么?

我做的是一个后台工作台页面,页面主体可以简单理解成三块:

  • 一块复杂的查询表单
  • 一块结果表格
  • 一块会反向改写查询条件的数据报表

也就是说,这不是一个"填完表单点搜索就结束"的页面,而是多个区域都会读写同一套查询状态。这也是为什么问题后来会收敛到 query form:当不同模块都想访问同一个表单实例时,最容易出现"顺手把 form 放进全局 store"这种设计。

最开始看到的问题很直接:

  • 页面一打开,内存就冲到 900MB 左右
  • 这个高内存不是闪一下就没了,而是会持续一段时间
  • 差不多要到 30 秒左右 才明显掉下来
  • 一热更新,页面还很容易直接崩掉
  • 切到别的页面时,也经常会被一起拖崩

这里先补一句:30 秒后会回落 这个现象确实存在,但这次排查的重点不是解释"它为什么回落",而是先找出"为什么峰值会被顶到这么离谱"。因为哪怕后面会掉下来,开发态里先冲到 900MB 也已经足够把热更新和页面稳定性一起拖垮了。

用户自己已经先排过一轮,确认问题大概率和查询表单有关,所以这次不是从整页瞎猜,而是直接围着 query form 往下查。

我是怎么给大模型下指令的

这个业务工作台页面打开以后,内存一直很高,差不多 900MB,要到第 30 秒左右才掉下来。

我已经查到是查询表单导致的,但具体原因还没找到。

你直接用 Playwright 打开页面看看。

我给它的信息其实很朴素:页面是一个业务工作台页面,现象是内存大约 900MB、30 秒后才回落,已知范围是查询表单,证据入口是 bootstrap.tsx 里那段内存日志,要求它必须直接用 Playwright 打开页面去看,而不是只给我猜测。

这里的 bootstrap.tsx 不是一个神秘线索,它只是项目启动入口里原本就有一段内存日志,所以模型一上来就能先接住现成证据,而不是从零开始搭监控。

这类输入的核心不是"怎么把提示词写得像魔法",而是:

把问题描述成一个可以执行、可以验证、可以收敛的任务。

大模型做了什么

它做的事情其实很像一个靠谱的前端同学在接手问题。

先用 Playwright 直接打开页面,不是先猜。它先拿 /index 做基线,确认首页内存大约在 110MB ~ 127MB ,说明不是整个站点天然就高;再去打开这个业务工作台页面,很快就把异常复现出来了:内存一路冲到 839MB ~ 887MB ,并且一直到 30 秒左右 才掉到 79MB 左右。到这一步,问题就从"感觉不对"变成了"机器也能稳定看到"。

这里的数字口径都来自同一套页面内存日志采样,重点是看同一页面、同一种采样方式下的前后差异,不是拿它去做跨页面的精确内存对账。

接着它没有在整页里乱翻,而是顺着"查询表单"继续往下查,很快就把链路串起来了:

  • 查询表单组件先创建了 Formily form
  • 为了让别的模块也能访问它,代码又把这个 form 实例写进了查询表单对应的 Zustand store
  • 这个 store 在开发环境下又接了 zustand/middleware/devtools

如果把这条链路翻译成人话,就是:

  1. 组件内部创建了一个很重的 form 运行时对象
  2. 为了跨模块共享,代码把它塞进了 store state
  3. store 又接了 devtools,所以这个重对象进入了调试链路
  4. devtools 在当前开发环境下会持续记录和处理这些 state 变化
  5. 于是问题就不再只是"内存里有一个重对象",而是"这个重对象还被反复观察和放大"

根因也就很清楚了:Formily form 这种重量级运行时实例,本来就不适合放进会被 devtools 观察、快照和序列化的 store state;而 devtools 又把这个问题放大成了 900MB 级高峰。 所以这事不是"表单字段太多",而是"重对象被放错了位置,而且被 devtools 放大了"。

确定根因之后,它没有停在"给建议",而是直接把代码改了:修复前,查询表单把 Formily form 直接塞进 Zustand store state;修复后,store state 里只保留轻量的 hasForm,真正的实例改成闭包保存,通过 getForm() 按需读取,再把依赖 state.form 的地方一起改掉。

这里要注意,闭包保存实例 不是说这个对象突然就不存在了,而是说它不再进入 store state 的快照和调试链路。真正重新划清的边界是:

  • 适合进 store 的,是 hasForm、筛选条件、选中项、布尔状态这类轻量数据
  • 不适合进 store 的,是 Formily form、DOM 节点、带大量内部引用的运行时实例

这种改法本质上是在重新划清边界:轻状态进 store,重实例不要进会被调试链路持续观察的状态面。 当然,这也不代表把实例移出 state 就万事大吉了,后面依然要按组件生命周期处理好它的创建、读取和清理。

最后它又回到同一个页面,再用 Playwright 复验一遍。修完之后,页面打开时的内存降到了 165MB ~ 207MB ,30 秒左右会进一步降到 96MB 左右。也就是说,这不是"看起来像修好了",而是前后数据能直接对上。整个排查、修改和验证的闭环,大约就用了十来分钟

总结

这次最值得复用的,不是一条具体的性能结论,而是这套工作方式:让大模型配合浏览器,自己完成复现、排查、修改和验证。

如果以后你也想照着做,输入里至少要把几件事说清楚:

  • 页面和场景:到底是哪一页、哪个交互有问题
  • 现象和数据:比如内存到多少、持续多久、有没有日志
  • 已知边界:哪怕只是"大概率和查询表单模块有关",也很有用,能帮大模型减少很多不必要的查询路径
  • 证据入口:日志文件、入口代码、复现路径
  • 闭环要求 :别只让它分析,直接要求它用浏览器先复现,再定位,再修改,最后验证

如果你懒得自己组织,可以直接按这个最小模板来:

页面是一个带复杂查询联动的后台页面。

现象是页面打开后内存升到 900MB 左右,30 秒后才回落。

我已经确认问题大概率和查询表单有关。

证据入口是启动入口里的内存日志。

请你先用浏览器复现,再定位原因,改完以后给我修复前后的对比数据。

一句话说,这次真正能抄作业的地方就是:

把大模型当成一个会用浏览器、会自己验证结果的工程执行者,而不只是一个会聊天的分析助手。

相关推荐
魔术师Grace1 小时前
我给 AI 做了场入职培训
前端·程序员
玩嵌入式的菜鸡2 小时前
网页访问单片机设备---基于mqtt
前端·javascript·css
前端一小卒2 小时前
我用 Claude Code 的 Superpowers 技能链写了个服务,部署前差点把服务器搞炸
前端·javascript·后端
滑雪的企鹅.3 小时前
HTML头部元信息避坑指南大纲
前端·html
一拳不是超人3 小时前
老婆天天吵吵要买塔罗牌,我直接用 AI 2 小时写了个在线塔罗牌
前端·ai编程
空中海4 小时前
Kubernetes 入门基础与核心架构
贪心算法·架构·kubernetes
米高梅狮子5 小时前
08.CronJob和Service
云原生·容器·架构·kubernetes·自动化
excel5 小时前
如何解决 Nuxt DevTools 中关于 unstorage 包的报错
前端
Rust研习社5 小时前
使用 Axum 构建高性能异步 Web 服务
开发语言·前端·网络·后端·http·rust