- 人工智能
- AI Agent
- 代码智能体
- 多智能体
- MCP Clients
- Agent 编排
【免费下载链接】oh-my-openagent
OmO: Just type "mass ulw" keyword with your prompt. Now you are the master of graph engineering.
导读
本文以 oh-my-openagent 仓库中 frontend 技能规则的 perfection 规则集 为骨架,完整讲解其核心主张:任何前端页面都必须在真实浏览器环境中、以移动端与桌面端双预设,达到 Lighthouse 四项(Performance / Accessibility / Best Practices / SEO)全部 100 分,且不得以牺牲任何 UX 质量为代价。读完本文,你将掌握一套可直接落地的「生产构建 → Playwright 驱动真实 Chrome → Lighthouse 审计 → React 渲染层双重门禁 → 根因修复」闭环流程,并能在 CI 中将其固化为硬性阈值。
一、该规则集在 oh-my-openagent 中的定位
在 oh-my-openagent 的 shared-skills 体系中,frontend 技能是一个「路由器而非规则书」,其路由规则定义在 SKILL.md。任何涉及「编写或修改前端代码,或审计性能 / SEO / 可访问性 / 质量」的请求,都会被路由到references/perfection/README.md,其原文路由表明确写着:
Writing or modifying frontend code, OR auditing performance / SEO / accessibility / quality → ALSO
references/perfection/README.md。Lighthouse 100 in every category, measured on real Playwright Chromium (never thelighthouseCLI), achieved through architecture — never by dropping animations or hiding content.
同时 SKILL.md 强调「design + perfection 必须同时加载」:Beauty with a 2 MB bundle fails; Lighthouse 100 that looks like AI slop fails. Both win or neither does.(漂亮但 2MB 的包会失败;Lighthouse 100 但长得像 AI 套壳垃圾同样失败。要么双赢,要么双输。)这正是 perfection 规则集第 5、6 条信条与 design 规则集联动的直接来源。
整个规则集位于packages/shared-skills/skills/frontend/references/perfection/目录,由两份文档和一个配套脚本组成:
| 文件 | 作用 |
|---|---|
| perfection/README.md | 主规则集:七大信条、审计工作流、根因清单、反模式、响应格式 |
| perfection/react-perf-tooling.md | React 专属工具链:react-scan / react-doctor 与 Playwright 的集成配方 |
| perfection/lighthouse-audit.py | 跨平台 Python 审计 CLI 的仓库级实现 |
二、七大信条:不可妥协的行为准则
1. 必须通过真实浏览器审计,绝不使用 CLI
lighthouseCLI 默认运行的是chrome-headless-shell,它不是你用户看到的东西,它产生的数字会骗你。任何基于 CLI 的报告都必须被拒绝,即使 CI 显示绿灯。
正确的路径分四步:
- 生产模式构建应用(
next build && next start、vite build && vite preview、astro build && astro preview、bun run build && bun run start),绝不对 dev server 做测量; - 用
channel: "chrome"启动 Playwright(真实 Chrome stable,而非 headless-shell 二进制); - 通过
playwright-lighthouse,或chrome-launcher+lighthouseNode API 运行 Lighthouse,并挂接到 Playwright 的 CDP 端点——这样 cookie、登录态、预热缓存都能镜像真实回访用户所见; - 用mobile 预设(4x CPU 节流、Fast 3G)作为主数字,desktop 预设作为次数字,两个都上报。
这一信条在仓库中有完整的脚本级实现:packages/shared-skills/skills/frontend/scripts/perfection/lighthouse-audit.py的 docstring 明确重申「NEVER uselighthouseCLI (uses headless-shell, not real Chrome)」,其_run_with_playwright函数通过p.chromium.launch(channel="chrome", headless=True, args=["--remote-debugging-port=0"])启动真实 Chrome,再从 WebSocket 端点解析出 CDP 端口,交给内嵌的 Node 端 Lighthouse runner 执行审计(见 lighthouse-audit.py)。
如果当前会话没有加载playwright技能,应通过skill工具立即加载。
2. 每项 100 分是地板,不是目标
99 分就是回归,95 分就是火灾。你不能把「performance 93, accessibility 100, SEO 100, best-practices 100」当作通过——你要诊断是哪个审计项吃掉了那 7 分,修复根因,重跑,直到移动端和桌面端四个面板全部 100 才上报。
3. 在架构层面赢得分数
性能在架构与代码质量层面就已注定:包体积、渲染路径、水合策略、资源管线、图片格式与尺寸、字体加载、第三方脚本、关键 CSS、延迟 JS、路由级代码分割——这些才是撬动分数的杠杆。给 hero 图加个loading="lazy"不是优化,是恐慌。
对每一个失败的审计项,都必须追溯到具体的一行代码或一个具体的构建配置决策,从源头修复,不许贴创可贴。
4. 绝不为凑分削弱 UX
如果某个修复移除了 hover 状态、丢掉了 CSS 过渡、把带动画的挂载换成生硬闪现、把平滑滚动到视野换成瞬间跳转、把 60fps 交互降级到 30fps、或隐藏了你本来会渲染的内容——拒绝这个修复。产品的动画语言、动效设计与触感是承重墙。
应该换这些思路:
- 进一步拆分 bundle(先路由级,再组件级,再 feature-flag 级);
- 把非关键的绘制工作推迟到
requestIdleCallback; - 用 Web Worker 把重活移出主线程(用 Comlink 提升开发体验);
- 跨路由过渡用
View Transitions API; - 屏外区块用
content-visibility: auto+contain-intrinsic-size; will-change要精准,只加在真正动画的那个属性上、且只在动画持续期间;- 预加载 LCP 图片:
<link rel="preload" as="image" fetchpriority="high" imagesrcset="...">; - 关键 chunk 用 HTTP/2 server-push 或
<link rel="modulepreload">; - 只做 GPU 合成动画(
transform、opacity、filter),绝不动画width、height、top、left、margin、padding。
5. 必须同步加载设计规则集
任何视觉或布局工作,都必须同时阅读设计规则集 design/README.md。该技能承载品牌级品味参考(Apple、Stripe、Linear、Vercel、Claude、Notion、Airbnb、Figma 等)和反 AI 套壳垃圾的立场。
一个拿 100 分却长得像 AI SaaS 垃圾的页面是失败的。速度服务于设计,设计骑乘在速度之上,要么双赢,要么双输。使用设计规则集的时机:
- 写 JSX/CSS之前:拉取相关品牌参考来锚定视觉方向;
- 实现过程中:对照反套壳护栏交叉检查;
- 宣布完成之前:验证页面通过设计品味门槛,而不只是 Lighthouse 门槛。
6. 设计系统合规不可选
design 规则集强制一道Phase 0 设计系统门——任何 UI 工作开始前,项目必须先有DESIGN.md(见 design/README.md 的 Phase 0 章节)。本技能负责另一侧:每次审计都必须验证合规性。
Lighthouse 分数通过后,还要跑设计系统合规检查:
- 颜色:grep 代码库中未在
DESIGN.md声明的裸 hex/rgb 值,每个都是一次违规; - 字体排印:CSS/Tailwind 中每个 font-size 都必须映射到
DESIGN.md的字体阶,不允许随意尺寸; - 间距:每个 margin/padding/gap 都必须是基准单位(4px)的倍数,理想情况下使用声明的 token;
- 组件:任何被使用 2 次以上的组件必须记录在
DESIGN.md第 5 节,没有就补上; - 深度:如果
DESIGN.md说「只用边框」,那么box-shadow声明必须是零;如果说「色调迁移」,那么表面分隔就不能用边框。
一个 Lighthouse 100 分但用了 14 个未声明 hex 码和 8 个魔法间距值的页面不算完成。设计系统就是架构,Lighthouse 测量的是该架构的性能。
7. React 专属性能工具是审计的一部分
如果项目使用 React,单靠 Lighthouse 看不到组件粒度的渲染层问题。还必须运行:
react-doctor(静态):成本最低。在任何浏览器审计前先跑npx react-doctor@latest --json,把 perf 类发现当作审计失败处理;react-scan/lite(运行时、无头):在 Playwright 运行中通过page.addInitScript注入,排空其onEvent流,若存在任何被归类为unnecessary的 commit 即判审计失败。
完整的配方——包括 Playwright +playwright-lighthouse+react-scan/lite的集成、每路由渲染预算断言、以及扩展了下方清单的 React 专属根因清单——存放在 react-perf-tooling.md。任何 React 审计前必须先读它。
Lighthouse 100 但react-scan报告每路由 30+ 次不必要渲染不算完成。两个门都必须通过:合成分数和渲染质量。如果 React 层在抖动,真实负载下合成分数会撒谎。
三、审计工作流
方式一:跨平台 Python CLI(macOS / Linux / Windows)
uv run $SKILL_DIR/scripts/perfection/lighthouse-audit.py https://localhost:3000 uv run $SKILL_DIR/scripts/perfection/lighthouse-audit.py https://localhost:3000 --threshold 95 uv run $SKILL_DIR/scripts/perfection/lighthouse-audit.py https://localhost:3000 --desktop-only其中$SKILL_DIR即仓库中的 packages/shared-skills/skills/frontend。该脚本的仓库实现细节值得展开(lighthouse-audit.py):
- 它是
uv run --script内联依赖脚本,requires-python >= 3.11,依赖playwright、typer、rich; - 前置条件:本机已安装 Chrome stable(脚本使用
channel="chrome",不会下载浏览器),并具备node与全局lighthouse/chrome-launcher——脚本启动时会用node -e "require('lighthouse'); require('chrome-launcher')"探测,缺失则自动npm install -g lighthouse chrome-launcher; - 默认执行 mobile + desktop 两次审计(可用
--desktop-only/--mobile-only跳过其一),--threshold默认 100; - 内嵌的 Node runner 配置(
LIGHTHOUSE_RUNNER_JS)可佐证预设细节:mobile 走默认节流(4x CPU、Fast 3G),desktop 显式设置rttMs: 40, throughputKbps: 10240, cpuSlowdownMultiplier: 1与1350x940屏幕仿真;onlyCategories限定为 performance / accessibility / best-practices / seo 四项; - 输出用 rich 表格打印每项分数与 PASS/FAIL 状态,任何一项低于阈值即以退出码 1 结束——这正是「未达标就不算完成」的脚本化表达(lighthouse-audit.py)。
方式二:测试套件内的 TypeScript 集成
// scripts/audit.ts import { chromium } from "playwright"; import { playAudit } from "playwright-lighthouse"; const browser = await chromium.launch({ channel: "chrome" }); const context = await browser.newContext(); const page = await context.newPage(); await page.goto("http://localhost:3000/<route>"); await playAudit({ page, port: 9222, thresholds: { performance: 100, accessibility: 100, "best-practices": 100, seo: 100 }, reports: { formats: { html: true, json: true }, name: "lighthouse-<route>" }, config: { extends: "lighthouse:default", settings: { formFactor: "mobile" } }, }); await browser.close();三条流程纪律
- 每条路由跑两遍:一遍
formFactor: "mobile",一遍formFactor: "desktop",两遍都必须 100/100/100/100; - 从 JSON 报告诊断,而不是 HTML:用程序解析
audits[*].score < 1找出问题项,不要肉眼盯着 HTML 报告看; - 跑 3–5 次取中位数:单次审计可能噪声很大;CI 必须在每个 PR 上强制执行阈值。
四、根因清单(优先命中这些,几乎总是元凶)
- 关键路径上的阻塞渲染 JS/CSS:延迟加载、按路由代码分割、只内联关键 CSS;
- 未定尺寸的媒体:每个
<img>、<video>、<iframe>必须有显式width/height或 aspect-ratio 容器,否则会造成 CLS; - 错误的图片格式或尺寸:优先 AVIF,WebP 兜底,JPEG 最后;在构建时生成所有响应式尺寸;绝不发送比渲染框更大的图片;LCP 图片加
fetchpriority="high"; - 字体:
font-display: swap是底线,非关键字体用optional,预加载唯一的关键字体,子集化到实际用到的字符; <head>中同步加载的第三方脚本:延迟、首次交互时懒加载,或者代理到自有域名,把第三方 DNS + TLS 握手移出关键路径;- 不需要水合的路线强行水合:React Server Components、islands、
client:load只放在交互真实存在的地方;静态路由零 JS; - 缺少语义化 HTML:按钮用
<button>,链接用<a href>,地标用<nav>/<main>/<header>/<footer>,每个表单输入都有 label,每个有意义的图片都有 alt 文本,每条路由有唯一<title>; - Tab 顺序、焦点环、对比度、prefers-reduced-motion、ARIA 正确性:Accessibility 100 意味着屏幕阅读器用户可以在没有帮助的情况下端到端驱动页面;
- Meta 标签:
<title>、<meta name="description">、OpenGraph、Twitter cards、结构化数据(JSON-LD)、<html>上的lang、viewport、canonical URL。
五、反模式:见一个拒一个
| 反模式 | 拒绝对策 |
|---|---|
| 上报 CLI Lighthouse 分数 | 拒绝。见信条 1 |
| 为修 INP 删动画 | 拒绝。改用 CSS-only 的 transform/opacity 动画,防抖监听器,把重活移出主线程 |
| 用占位图替换 hero 图来"修" LCP | 拒绝。正确修法是合适尺寸的 AVIF +fetchpriority="high"+ preconnect 图片 CDN |
| 为"拿 100 分"禁用某路由的 JS | 拒绝。要在 JS 启用的生产构建上、在真实用户设备配置下拿 100 |
给屏外内容设display: none躲避审计 | 拒绝。用content-visibility: auto加正确的懒挂载,绝不对页面撒谎 |
| 单次审计后宣布胜利 | 拒绝。跑 3–5 次取中位数,CI 强制执行阈值 |
| 在 localhost 拿 100 就不重新测量部署 URL 直接上线 | 拒绝。CDN、真实 DNS、真实 TLS 握手都算数 |
六、React 项目的双重门禁:react-scan / react-doctor
两种工具的定位
| 工具 | 形态 | 给你什么 |
|---|---|---|
react-scan(react-scan/lite) | 运行时插桩、无头 | 每个 fiber 的commit事件及changeDescription——"这个组件因为 <prop / state / context / parent / hook> 变了而重渲染"。可与long-animation-frame关联,把 LoAF 归因到具体组件 |
| react-doctor | 静态扫描、CI 友好 | 跨 state/effects、perf(memoization、列表 key、昂贵 children)、架构、安全、a11y 的确定性发现。npx react-doctor@latest一次性产出 JSON 报告。出自 Million.dev 团队 |
两者互补:react-scan告诉你此刻什么慢,react-doctor告诉你结构上什么错。二者都是仅开发期且免费的。
Lighthouse 运行 + react-scan/lite 的规范集成
核心要点:必须在 React 挂载之前通过page.addInitScript注入react-scan/lite(用page.evaluate会晚于 React 挂载,错过所有首次渲染事件),然后在运行期间排空其onEvent流,结束时对渲染预算做断言:
// scripts/audit-with-react-scan.ts import { chromium } from "playwright"; import { playAudit } from "playwright-lighthouse"; const browser = await chromium.launch({ channel: "chrome" }); const context = await browser.newContext(); // 在 app 启动前注入 react-scan/lite await context.addInitScript(() => { // @ts-ignore — pulled from the project's node_modules or a self-hosted bundle import("react-scan/lite").then(({ instrument }) => { (window as any).__renderEvents = []; instrument({ onEvent: (event: any) => { if (event.kind === "commit") (window as any).__renderEvents.push(event); }, recordChangeDescriptions: true, includeFiberSource: true, includeFiberIdentity: true, }); }); }); const page = await context.newPage(); await page.goto("http://localhost:3000/<route>"); await playAudit({ page, port: 9222, thresholds: { performance: 100, accessibility: 100, "best-practices": 100, seo: 100 }, reports: { formats: { html: true, json: true }, name: "lighthouse-<route>" }, config: { extends: "lighthouse:default", settings: { formFactor: "mobile" } }, }); // 拉取渲染事件并对渲染质量做断言 const events = await page.evaluate(() => (window as any).__renderEvents); const unnecessary = events.filter((e: any) => e.tree?.some((node: any) => node.changeDescription?.kind === "unnecessary"), ); if (unnecessary.length > 0) { console.error(`FAIL: ${unnecessary.length} unnecessary renders detected during audit`); for (const e of unnecessary.slice(0, 10)) console.error(" -", JSON.stringify(e, null, 2)); process.exit(1); } await browser.close();与基础 Lighthouse 流程一样,每条路由跑 mobile + desktop 两遍;两遍都必须 100/100/100/100且零不必要渲染。
react-doctor:静态性能门
在 Playwright 运行之前快速失败,扫描无需浏览器,应放在流水线更早的位置:
npx react-doctor@latest --json > .react-doctor-report.json解析报告中的 perf 类发现,把任何 perf 发现视为阻塞项——理由与 Lighthouse 分数 < 100 是阻塞项相同:这些确定性缺陷在节流下迟早会显形到 Lighthouse 里。
接进 CI 作为独立 job(便宜、快、无需浏览器):
- name: React Doctor static perf scan uses: millionco/react-doctor@main或内联带失败过滤:
- name: React Doctor static perf scan run: npx react-doctor@latest --json --fail-on perf审计时按什么顺序加载什么
按此顺序运行,遇到第一个失败即停:
react-doctor——成本最低。抓缺失 memoization、坏掉的列表 key、不稳定的 callback 引用、会不必要重渲染的昂贵 children。跑 Lighthouse 之前先把它报的全部修掉——性能分数的一半就赢在这里;react-scan交互式(dev)——在真实 Chrome 里用npx react-scan@latest init加载页面,走一遍 LCP 路由、最常点击的 CTA、任何动效密集视图。工具栏显示渲染计数,覆盖层把不必要渲染标灰。修到干净为止;- Lighthouse 运行中的
react-scan/lite——交互式已干净后,运行上面的 Playwright 审计。它抓住只在节流下或只在首帧时出现的问题; - Playwright + Lighthouse——标准运行。100 分 + 第 3 步的零不必要渲染 = 完成。
React 专属性能根因(对主根因清单的扩展)
- Context value 身份抖动:provider 的 value 忘了
useMemo,导致每次 provider 父级渲染时所有 consumer 都重渲染 →useMemo该 value,或拆分 context,让高频变化字段不与稳定字段同处; - memo 化子组件收到内联 object/array/callback props:
<Child config={{ a: 1 }} />每次渲染都打破React.memo→ 提升、useMemo或useCallback; - 列表 key 用数组下标:重排会撕裂 reconciler → 用数据中的稳定 id;
- 首屏上方无条件渲染昂贵组件→
lazy()+Suspense,或移到 LCP 之下,或服务端预渲染; - 每次渲染都触发的 effects:依赖数组缺失或依赖不稳定 → 稳定依赖、拆分 state、或提取到
useEvent风格 ref; - 把整个 context value 展开成 props:让每个 consumer 都耦合所有字段 → 只解构实际用到的字段;
- 水合不匹配:SSR 标记与客户端首渲染不一致 → react-doctor 结构性地标出,修复分歧源头(Date.now、locale、随机性、浏览器专属 API)。
react-doctor静态地发现这些问题,react-scan在运行中的应用里确认症状。两者都干净之前,Lighthouse 100 没有意义。
该工作流专属的反模式
- 忘记
page.addInitScript(改用page.evaluate):evaluate在 React 挂载之后运行,会错过所有初始渲染事件。必须用addInitScript; - Lighthouse 运行期间用非 lite 版
react-scan:完整 UI(工具栏、canvas 覆盖层)带来额外开销并拉低分数。测量只用react-scan/lite,完整版只用于交互式开发; - 上报 Lighthouse 100 而
react-scan显示每路由 30+ 次不必要渲染:React 层在抖动时分数毫无意义——即使合成运行通过了,真实负载下 INP 和 CLS 也会劣化。两个门都必须清; - 把
trackUnnecessaryRenders当免费功能:它有可观开销,在 Lighthouse 运行中可能把性能分拖低 2–3 分。它用于交互式诊断,不用于审计运行; - 因为"它只是个 linter"就跳过 react-doctor:它不是。它能检测 ESLint 插件检测不到的 React 专属缺陷(缺 key、坏的 memo、不稳定 ref、水合不匹配),因为它们需要 fiber 级推理。
七、工具的初始安装与开发期门禁
如果项目尚未接好这些工具,规范安装片段位于 react-dev-tooling-skill.md。该文档定义了三件默认开发期工具(react-grab、react-scan、react-doctor)的标准安装:
npx grab@latest init # react-grab — UI 元素 → AI 源上下文 npx react-doctor@latest install # react-doctor — agent-skill 安装 + 静态扫描 npx react-scan@latest init # react-scan — 渲染高亮三者的init/installCLI 会自动检测框架,并把运行时工具门禁在process.env.NODE_ENV === 'development'/import.meta.env.DEV之后,绝不进入生产。安装后要读 diff 确认:每个工具都必须只出现在开发门禁之后。该文档还提供 Next.js App/Pages、Vite、Webpack/CRA、Remix、Astro 的手工安装片段,以及*_DISABLE_REACT_DEVTOOLS环境变量特性开关和「生产构建后 curl 检查 unpkg 脚本是否泄漏」的验证方式。
八、响应格式:前端审计 / 构建任务的交付物
当用户请求前端审计或构建时,必须返回:
- 前后分数:移动端和桌面端、全部四个类别;
- 设计系统合规:发现的孤立 token / 已修复项、已记录的组件;
- 每个修复一行,可追溯到它清除的审计项;
- 你刻意没有做的事,以及为什么——尤其是每个为了保 UX 而拒绝的"送分项";
- 基于浏览器的 Design QA 结果:测过的断点、发现/修复的视觉 bug、验证过的状态;
- 如果再有一次迭代你会跑的下一次审计。
如果本轮没有达到每项 100,你要明确说出「尚未完成」,然后继续迭代。
九、一句话收束
100 on every Lighthouse category, on a real browser, with full features and full animations intact. Or it is not done.
用 oh-my-openagent 的表述:Lighthouse 100 + react-doctor 干净 + react-scan 零不必要渲染,三者全过才算完成。这套规则集的价值不在于「把分刷上去」,而在于把性能问题钉死在架构层面、把分数与真实用户体验绑定——真实浏览器、真实设备预设、完整功能、完整动画,缺一不可。
- 人工智能
- AI Agent
- 代码智能体
- 多智能体
- MCP Clients
- Agent 编排
【免费下载链接】oh-my-openagent
OmO: Just type "mass ulw" keyword with your prompt. Now you are the master of graph engineering.
相关推荐
oh-my-openagent 前端技能体系中的 React 性能审计:react-scan 与 react-doctor 双门禁支撑 Lighthouse 100
oh my openagent 前端技能体系中的 React 性能审计:react scan 与 react doctor 双门禁支撑 Lighthouse 1
人工智能AI Agent代码智能体多智能体MCP ClientsAgent 编排oh-my-openagent 前端设计 Lane D 实战:用 `.omo/frontend-design/state.md` 管理设计记忆、设计债与交接记录
oh my openagent 前端设计 Lane D 实战:用 .omo/frontend design/state.md 管理设计记忆、设计债与交接记录 本
人工智能AI Agent代码智能体多智能体MCP ClientsAgent 编排EasyLM多主机训练实战:在Google Cloud TPU Pods上的部署经验
EasyLM多主机训练实战:在Google Cloud TPU Pods上的部署经验 EasyLM是一个基于JAX/Flax的一站式大语言模型解决方案,支持预训
大模型深度学习人工智能
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考