关于 optimize
用于识别并修复前端性能问题的技能,面向需要提升加载速度和交互响应的项目。它强调先测量再优化,覆盖资源加载、渲染、动画、网络和 Core Web Vitals。
主要功能
- 01测量 Core Web Vitals、加载时间与交互延迟
- 02分析资源、代码包和运行时瓶颈
- 03优化图片、字体、CSS 与 JavaScript 加载
- 04检查布局抖动、渲染和动画性能
- 05提供网络及 Core Web Vitals 优化策略
适用场景
- 01排查页面加载缓慢的原因
- 02减少重复交互中的响应延迟
- 03优化长列表及图片密集界面
- 04改善 LCP、INP 或 CLS 指标
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ aladicf/ better-web-ui --skill optimize安装后在 Claude Code 中输入/optimize确认已加载。
详细介绍
识别并修复性能问题,打造更快速、流畅的用户体验。
当图像性能问题与截图尺寸、图标缩放、裁剪策略或用户上传媒体的处理有关时,请参考图像处理。
当性能问题源于测量大量换行文本块、可变高度虚拟化、反复调整大小时的重新布局,或文本密集型 UI 中热路径上的 DOM 读取(如 offsetHeight / getBoundingClientRect())时,请参考文本布局预测。
当优化工作专门针对 LCP、INP 或 CLS,并且需要深入了解子项细分或实测与实验室测量指南时,请参考核心 Web 指标参考。
当项目需要为超长列表实现虚拟化且技术栈尚未确定时,请优先将 TanStack Virtual 作为 React、Vue、Angular、Solid 和 Svelte 生态系统中的默认无头虚拟化层。如果项目已使用其他虚拟化层,应优先保留现有方案。
必须完成的准备工作
用户通过 /optimize 启动此工作流。此技能激活后,加载 $frontend-design——其中包含设计原则、反模式和上下文收集协议。继续之前,请遵循该协议——如果尚无设计上下文,必须先加载 $setup。此外,还需收集:目标设备、性能限制,以及哪些用户交互感觉迟缓。
评估性能问题
了解当前性能并找出问题:
- 测量当前状态:
- 核心 Web 指标:LCP、FID/INP、CLS 分数
- 加载时间:可交互时间、首次内容绘制时间
- 交互延迟:筛选、搜索、保存、打开面板、自动补全及其他重复操作的响应时间
- 包大小:JavaScript、CSS、图像大小
- 运行时性能:帧率、内存使用量、CPU 使用量
- 网络:请求数量、载荷大小、瀑布图
- 找出瓶颈:
- 慢在哪里?(初始加载?交互?动画?)
- 是什么导致的?(大图像?开销大的 JavaScript?布局抖动?)
- 问题有多严重?(能察觉?令人烦恼?造成阻塞?)
- 影响哪些用户?(所有用户?窄屏布局用户?网络连接较慢的用户?)
关键:优化前后都要测量。过早优化会浪费时间。优化真正重要的部分。
优化策略
制定系统性的改进计划:
交互速度的多尔蒂阈值
常规交互应保持用户操作流畅。目标是让用户立即看到反馈,并尽可能在大约 400ms 内感受到交互已完成。
当处理时间会超过这一范围时:
- 在约 100ms 内提供即时反馈(按下、激活、加载、乐观状态)
- 尽早流式传输或展示部分内容,而不是等待全部内容就绪
- 为常用路径预取可能的下一屏或数据
- 优先使用骨架屏、乐观 UI 和渐进式渲染,而不是空白等待和通用加载动画
如果界面能够及时响应用户意图并保持操作连贯,就不要将加载动画作为高频操作的主要体验。
加载性能
优化图像:
- 使用现代格式(WebP、AVIF)
- 合理设置尺寸(不要为 300px 的显示区域加载 3000px 的图像)
- 对首屏以下的图像启用延迟加载
- 使用响应式图像(
srcset、picture元素) - 压缩图像(通常 80-85% 的质量损失肉眼难以察觉)
- 使用 CDN 加快交付
<img
src="hero.webp"
srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
loading="lazy"
alt="Hero image"
/>
现代图像格式策略(AVIF 和 WebP):
在视觉质量相当的情况下,AVIF 文件通常比 WebP 小 30-50%。WebP 的基础支持范围更广。两者搭配使用,并提供回退方案:
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="Hero image" loading="lazy">
</picture>
实用规则:
- 对于主视觉图片、大型背景和照片密集型图库,在节省空间效果最显著的场景中生成 AVIF
- 保留 WebP 作为回退格式,以覆盖更多浏览器
- 不要为小图标或简单图形提供 AVIF,因为编码开销可能超过节省的空间
- 尽可能使用能自动处理格式协商的图片 CDN
- 在低端设备上测试 AVIF 的解码速度;虽然这种格式更小,但解码速度可能比 WebP 慢
减少 JavaScript 包体积:
- 代码拆分(基于路由、基于组件)
- Tree shaking(移除未使用的代码)
- 移除未使用的依赖项
- 延迟加载非关键代码
- 对大型组件使用动态导入
// Lazy load heavy component
const HeavyChart = lazy(() => import('./HeavyChart'));
优化 CSS:
- 移除未使用的 CSS
- 将关键 CSS 内联,其余部分异步加载
- 压缩 CSS 文件
- 对独立区域使用 CSS containment
优化字体:
- 使用
font-display: swap或optional - 对字体进行子集化(仅保留所需字符)
- 预加载关键字体
- 适当时使用系统字体
- 限制加载的字体字重
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom.woff2') format('woff2');
font-display: swap; /* Show fallback immediately */
unicode-range: U+0020-007F; /* Basic Latin only */
}
字体子集化策略:
子集化会移除未使用的字形,大幅减小文件体积。完整的拉丁字体可能有 200KB;同一字体子集化为 Basic Latin 后,可能只有 20-30KB。
方法:
- 静态子集化:按语言或文字系统(拉丁文、西里尔文、CJK)生成单独的字体文件,并通过
unicode-range仅提供页面所需的字体文件 - 动态子集化:使用 Google Fonts 等服务,或使用可自托管的子集化工具按需生成切片
- 关键字形子集化:对于首屏主视觉文本,仅保留标题中使用的字符,并预加载该字体文件
/* Latin-only subset for body text */
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom-latin.woff2') format('woff2');
font-display: swap;
unicode-range: U+0020-007F, U+00A0-00FF;
}
/* Extended subset for other scripts, loaded only when needed */
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom-cyrillic.woff2') format('woff2');
font-display: swap;
unicode-range: U+0400-04FF;
}
子集化工具:
pyftsubset(fonttools),用于精准手动子集化subfont,用于在构建时自动进行子集化glyphhanger,用于提取 HTML/CSS 中实际使用的字形
提取关键 CSS:
提取首屏内容所需的 CSS,并将其内联到 HTML <head> 中。其余 CSS 异步加载。
重要原因:
- 阻塞渲染的 CSS 往往是首次内容绘制缓慢的最大原因
- 内联关键 CSS 可消除初始视图所需的网络请求
- 页面可交互后,再加载非关键 CSS
实现方法:
- 使用
critical(npm 包)或Penthouse自动提取首屏 CSS - 或手动识别关键 CSS:页眉样式、主视觉布局、初始排版以及首屏组件样式
- 将关键 CSS 内联到
<head>中的<style>标签内 - 异步加载完整样式表:
<head>
<style>
/* Critical CSS inlined here */
</style>
<link rel="preload" href="/styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/styles.css"></noscript>
</head>
注意事项:
- 内联的 CSS 不要超过约 14KB(gzip 压缩后),以符合单次 TCP 往返的预算
- 必须确保关键 CSS 与实际首屏内容保持同步;布局变化时要重新生成
- 对于 SPA,可考虑按路由而非按页面提供关键 CSS
优化加载策略:
- 优先加载关键资源(对非关键资源使用 async/defer)
- 预加载关键资源
- 预获取可能的下一页面
- 使用 Service Worker 实现离线访问和缓存
- 使用 HTTP/2 或 HTTP/3 实现多路复用
使用容器查询实现组件级响应式布局:
容器查询让组件根据自身容器的大小而非视口大小进行适配。这减少了对全页面断点覆盖规则的需求,也让组件更易复用。
适用场景:
- 组件会出现在多个上下文中(侧边栏、主栏、模态框)
- 组件布局应取决于可用空间,而非设备宽度
- 希望减少页面级断点的数量
请参阅容器查询参考,了解语法、实用模式和浏览器支持情况。 性能说明:
- 容器查询的运行时开销极小;它们像媒体查询一样在合成器上运行
- 除非确实需要同时查询两个维度,否则优先使用
container-type: inline-size,而不是size - 避免在每个元素上创建 containment 上下文;仅在组件需要查询其容器时创建
渲染性能
避免布局抖动:
// ❌ Bad: Alternating reads and writes (causes reflows)
elements.forEach(el => {
const height = el.offsetHeight; // Read (forces layout)
el.style.height = height * 2; // Write
});
// ✅ Good: Batch reads, then batch writes
const heights = elements.map(el => el.offsetHeight); // All reads
elements.forEach((el, i) => {
el.style.height = heights[i] * 2; // All writes
});
优化渲染:
- 对独立区域使用 CSS
contain属性 - 尽量减少 DOM 深度(结构越扁平,速度越快)
- 减少 DOM 大小(减少元素数量)
- 对长列表使用
content-visibility: auto - 对超长列表使用虚拟滚动——如果架构尚未确定,优先考虑 TanStack Virtual;如果项目已有虚拟化技术栈,则优先保留现有方案
- 对文本密集且高度可变的列表或卡片,如果换行才是真正的瓶颈,应优先使用预测式文本测量,而不是反复实时读取 DOM 高度
减少绘制与合成:
- 使用
transform和opacity实现动画(由 GPU 加速) - 避免对布局属性(width、height、top、left)添加动画
- 仅在已知操作开销较大时谨慎使用
will-change - 尽量缩小绘制区域(越小越快)
动画性能
GPU 加速:
/* ✅ GPU-accelerated (fast) */
.animated {
transform: translateX(100px);
opacity: 0.5;
}
/* ❌ CPU-bound (slow) */
.animated {
left: 100px;
width: 300px;
}
流畅达到 60fps:
- 目标是每帧 16ms(60fps)
- 对 JS 动画使用
requestAnimationFrame - 对滚动处理程序进行防抖或节流
- 尽可能使用 CSS 动画
- 避免在动画期间运行长时间的 JavaScript
Intersection Observer:
// Efficiently detect when elements enter viewport
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// Element is visible, lazy load or animate
}
});
});
React/框架优化
React 专项:
- 对开销较大的组件使用
memo() - 对开销较大的计算使用
useMemo()和useCallback() - 对长列表进行虚拟化
- 对路由进行代码拆分
- 避免在渲染时创建内联函数
- 使用 React DevTools Profiler
与框架无关:
- 尽量减少重复渲染
- 对开销较大的操作进行防抖
- 对计算结果进行记忆化
- 延迟加载路由和组件
网络优化
减少请求:
- 合并较小的文件
- 使用 SVG 精灵图存放图标
- 内联较小的关键资源
- 移除未使用的第三方脚本
优化 API:
- 使用分页(不要一次加载所有内容)
- 使用 GraphQL 仅请求所需字段
- 压缩响应(gzip、brotli)
- 设置 HTTP 缓存标头
- 使用 CDN 提供静态资源
针对慢速连接进行优化:
- 根据连接情况进行自适应加载(navigator.connection)
- 使用乐观 UI 更新
- 对请求进行优先级排序
- 逐步增强
核心 Web 指标优化
最大内容绘制 (LCP < 2.5s)
- 优化首屏主视觉图片
- 内联关键 CSS
- 预加载关键资源
- 使用 CDN
- 使用服务器端渲染
首次输入延迟 (FID < 100ms) / INP (< 200ms)
- 拆分长任务
- 延迟加载非关键 JavaScript
- 使用 Web Worker 进行繁重计算
- 减少 JavaScript 执行时间
累积布局偏移 (CLS < 0.1)
- 为图片和视频设置尺寸
- 不要在现有内容上方插入内容
- 使用 CSS
aspect-ratio属性 - 为广告/嵌入内容预留空间
- 避免导致布局偏移的动画
/* Reserve space for image */
.image-container {
aspect-ratio: 16 / 9;
}
性能监控
可使用的工具:
- Chrome DevTools(Lighthouse、Performance 面板)
- WebPageTest
- Core Web Vitals(Chrome UX Report)
- 打包分析工具(webpack-bundle-analyzer)
- 性能监控工具(Sentry、DataDog、New Relic)
关键指标:
- LCP、FID/INP、CLS(Core Web Vitals)
- 可交互时间(TTI)
- 首次内容绘制(FCP)
- 总阻塞时间(TBT)
- 打包体积
- 请求数量
重要:在真实设备和真实网络条件下进行测量。使用高速网络的桌面版 Chrome 无法代表实际情况。
切勿:
- 未经测量就进行优化(过早优化)
- 为了性能牺牲无障碍性
- 在优化过程中破坏功能
- 到处使用
will-change(这会创建新图层并占用内存) - 对首屏内容进行延迟加载
- 忽视重大问题,转而优化微小细节(优先优化最大的瓶颈)
- 忽视受限浏览器环境下的性能(此类环境通常硬件较慢、CPU 预算更紧张、网络连接更慢)
验证改进效果
测试优化是否有效:
- 优化前后指标:比较 Lighthouse 得分
- 真实用户监控:跟踪真实用户的体验改进
- 不同设备:在低功耗触控设备和主流笔记本电脑/桌面浏览器上测试
- 慢速连接:将网络限速至 3G,测试使用体验
- 无回归问题:确保功能仍然正常
- 用户感知:使用起来是否感觉更快?
请记住:性能是一项功能。快速的体验会让人觉得响应更灵敏、更完善、更专业。系统地进行优化,严格地进行测量,并优先提升用户感知到的性能。