从「首屏白屏 30 秒」到「一句对话定位瓶颈」------把排障方法论编码为 AI Agent 可复用的技能包。

背景:首屏慢得离谱,却无从下手
我们的项目是一个基于 Nuxt 3 的 SSR 应用,某天收到反馈:弱网环境下首屏白屏接近 30 秒,根组件的 onMounted 在 window.load 之后很久才触发。
第一反应当然是打开 Chrome DevTools 的 Performance 面板看火焰图。但问题来了:
- Dev 模式下 Vite 的 transform 和 HMR runtime 会严重放大首屏体感,和线上差距巨大
- preview 模式 更接近生产,但每次要
build + preview才能复测,迭代极慢 - DevTools 的 Network 面板看单个请求还行,但要从几百个 chunk 和子资源里找出「谁拖慢了 onMounted」,完全靠肉眼排查
- 每次排查都要手动数 TTFB、DCL、onMounted 之间的间隔,然后对照 Resource Timing 去猜瓶颈在哪一段
更痛苦的是,这些问题不是我一个人的问题 ------ 团队里每个人遇到性能问题时都要重新走一遍这个排查流程。
核心痛点拆解
反复排查几轮后,我意识到问题其实可以拆成几个独立的维度:
1. 不知道瓶颈在服务端还是客户端
Nuxt SSR 的首屏链路分两段:
- SSR 端 (服务端):Nitro 中间件 → server 插件 →
app.vue服务端 setup → 页面 setup → 输出 HTML - CSR 端 (浏览器):HTML 解析 → 客户端插件 → chunk 拉取与执行 → 路由 setup → 根
onMounted
TTFB 很短但整体慢?瓶颈在 CSR。TTFB 本身就长?瓶颈在 SSR。看似简单,但很多人第一步就判断错了方向。
2. 缺少采样边界
window.load 和 app:mounted 看着差不多,但实际上「用户感觉页面可用了」的时机是 根 app.vue 的 onMounted 全部执行完,而不是某个框架生命周期。没打 mark 的话,你甚至不知道这个「真正就绪」的时间点。
3. dev 和真实环境的巨大差异
dev 模式下 Vite 的 transform、HMR、sourcemap 带来巨大的额外开销。在 dev 下优化半天以为快了 2 秒,上 preview 发现只快了 200ms ------ 因为 dev 的 2 秒瓶颈是 Vite 自身。
4. 排障知识无法传递
每次有新成员加入或开新项目,性能排障的方法论都要口头传授一遍。检查清单、脚本、经验全在脑子里,无法复用。
方案选型:为什么是 Skill 而不是文档或 CLI 工具?
既然要把排障流程固化下来,有几种选择:
| 方案 | 优点 | 缺点 |
|---|---|---|
| 写一篇 wiki | 简单 | 没人看;每次要手动对着 wiki 操作 |
| 做一个 CLI 工具 | 可执行 | 只解决了采集,检查清单、阶段判断、结果解读还是靠人 |
| 做成 AI Agent Skill | AI 引导执行全套流程 | 依赖 Agent 环境(Cursor/Claude Code) |
最终选择了 Skill,理由:
- 排障不是一个纯自动化任务 --- 它需要根据项目结构(monorepo?哪个子包?跑 dev 还是 preview?)做判断,这正是 AI Agent 擅长的
- Skill 包含「触发后检查清单」 --- 不是跑一个命令就完了,而是严格保证每一步前提条件
- 输出格式标准化 --- 每次 profile 结果以同样的 Markdown 结构呈现,方便团队对照历史数据
- 可分发、可安装 --- 通过
npx skills add一键安装,不依赖特定项目配置
Skill 的实现原理
整体架构
bash
skills/nuxt-boot-timing/
├── SKILL.md # 技能入口:触发条件、执行流程、输出格式
├── references/
│ ├── boot-phases.md # 冷启动阶段模型(SSR/CSR 各阶段拆解)
│ └── trigger-checklist.md # 触发后必做检查清单
└── scripts/
├── verify-env.mjs # 环境校验(Node/Nuxt/Playwright 三重检查)
├── startup-resource-profile.mjs # 核心:Playwright + CDP 采集脚本
└── profile-cloudflare-startup.mjs # 一条龙:启动服务 + profile
阶段模型(SKILL.md 核心)
Skill 的核心是一张 SSR → CSR 阶段表,把 Nuxt 首屏链路拆成可独立度量的步骤:
SSR 端:
- Nitro / 服务端中间件
- Nuxt
*.server插件 - 根组件
app.vue服务端 setup(含可能阻塞的顶层await) - 页面 setup、
@nuxt/content等 - 输出 HTML
CSR 端:
- 文档与入口脚本
- 客户端插件链
- 路由与壳、
app:beforeMount - 根组件客户端 setup
- 布局与首屏子树 chunk
- 路由页 setup、根
onMounted、Suspense
有了这张表,每次排查就不再从零开始猜,而是对着阶段去定位。
采样机制:在根组件打入 mark
脚本要求必须在 app.vue 中插入一段代码:
ts
// app.vue
onMounted(() => {
// 放在最后一个 onMounted 里
window.__TEENPATTI_APP_ROOT_MOUNTED__ = true
performance.mark('teenpatti-app-root-mounted')
})
这个 mark 是整个 profile 的采样边界:
- 脚本启动 Playwright,打开页面
- 轮询等待
window.__TEENPATTI_APP_ROOT_MOUNTED__ === true - 在
page.evaluate中采集 Navigation Timing + 所有startTime ≤ mark.startTime的 Resource Timing
这样做的好处:不等价于 window.load 。在弱网 + 顶层 await 的场景下,onMounted 可能远晚于 load 事件。
环境校验脚本:三道防线
verify-env.mjs 在跑任何 profile 前做三层检查:
- Node 版本 --- 必须 ≥ 18
- Nuxt 项目 --- 从 cwd 向上遍历查找声明了
nuxt的package.json(兼容 monorepo) - Playwright 状态 --- 从项目依赖、用户全局安装、本机可执行能力三个维度汇报
任何一项不满足,给出明确的修复指引,而不是让用户面对一个神秘的报错。
如何使用
安装
bash
# 全局安装(推荐)
npx skills add vghub-official/nuxt-boot-perf-skills --skill nuxt-boot-timing -g -y
# 或在 Cursor 中手动复制
mkdir -p ~/.cursor/skills
cp -R skills/nuxt-boot-timing ~/.cursor/skills/nuxt-boot-timing
在 AI Agent 中使用(核心体验)
安装 Skill 后,你不需要手动做任何接入操作。在 Cursor 或 Claude Code 中直接对 Agent 说一句:
"首屏加载很慢,帮我排查一下"
Agent 会自动触发 nuxt-boot-timing 技能,按检查清单自动执行以下操作:

整个过程用户不需要手动操作脚本路径、不需要自己改 package.json,只需要在 Agent 引导下在终端执行 profile 命令。
报告格式长这样:
markdown
### 环境与前提
- 应用位置:<repo-root>/apps/web
- 运行方式:pnpm build && pnpm preview
- 约束:禁缓存 + DevTools Slow 4G
### 阶段判定
TTFB 仅 120ms,但 DCL → onMounted 间隔达 21s,瓶颈在 CSR 侧 chunk 拉取与执行。
### 证据摘要
| waitingMs | type | xfer | url |
|-----------|------|---------|-----|
| 18500 | script | 2.3 MiB | /_nuxt/app.vue-abc123.js |
| 12400 | script | 1.1 MiB | /_nuxt/GameModal-def456.js |
### 建议下一步
1. 检查 app.vue 顶层 await 是否可改为 lazy
2. GameModal 改为动态导入,不阻塞首屏
3. preview 模式下复测确认
可配置的环境变量
这个 Skill 的脚本暴露了丰富的环境变量,覆盖各种排障场景:
| 变量 | 作用 | 默认值 |
|---|---|---|
STARTUP_PROFILE_URL |
目标页面 URL | http://localhost:3000/ |
STARTUP_PROFILE_USE_CACHE |
是否使用 HTTP 缓存 | 禁用 |
STARTUP_PROFILE_NO_THROTTLE |
不限速(本机带宽) | 限速 |
STARTUP_PROFILE_THROTTLE_PRESET |
限速预设 | devtools-slow-4g |
STARTUP_PROFILE_JSON |
输出 JSON 便于 CI/jq | 关闭 |
STARTUP_PROFILE_TOP |
各排行榜条数 | 10 |
STARTUP_PROFILE_SETTLE_MS |
mounted 后额外等待 | 0 |
设计决策:为什么这样设计?
1. 脚本必须复制,不能现场生成
SKILL.md 明确规定:profile 脚本必须从技能目录直接复制,禁止 Agent 临时手写。这是为了防止 Agent「即兴发挥」写出不规范的脚本,导致采集口径漂移。
2. verify-env 不复制到业务仓库
校验脚本在技能目录保留单一副本,只在需要时通过绝对路径调用。因为它是排障工具的一部分,不是业务代码。
3. 不自动安装 Playwright 浏览器二进制
SKILL.md 要求 Agent 提示用户自行安装 ,而不是代劳。因为 npx playwright install chromium 会下载 ~150MB 的浏览器二进制,不应该在用户不知情的情况下执行。
4. Monorepo 兼容
脚本从 cwd 向上遍历找 package.json 中声明 nuxt 的目录,而不是写死路径。这在 monorepo(如 apps/web、packages/frontend)场景下至关重要。
实际效果
接入这个 Skill 后,团队的性能排障流程从:
"我看看 DevTools...这个 chunk 好像很大...TTFB 多少来着...等下我跑个 lighthouse..."
变成了:
Agent: "首屏瓶颈在 CSR 侧,根 onMounted 在 DCL 之后 21 秒。Top 3 拖慢项:
app.vuechunk (2.3MiB, waiting 18.5s)、GameModalchunk (1.1MiB, waiting 12.4s)、@nuxt/content文档数据 (waiting 8.2s)。建议:顶层 await 改 lazy、GameModal 异步加载。"
每次排查的时间从 30 分钟降到 3 分钟,而且复盘时有结构化的数据可对照。
开源地址
Skill 源码已开源在 GitHub:
安装只需一行:
bash
npx skills add vghub-official/nuxt-boot-perf-skills --skill nuxt-boot-timing -g -y
如果你也在被 Nuxt 首屏性能问题困扰,欢迎试试,也欢迎提 PR 一起完善。
标签:Nuxt、性能优化、AI Agent、Skill、SSR、Playwright