关于 optimize
帮助团队优化 Web 界面的性能技能。它建议先测量并定位瓶颈,再处理加载、渲染、动画、React 和网络开销。
主要功能
- 01测量性能并分析瓶颈
- 02优化图片、字体与代码包
- 03提供渲染和动画优化指导
- 04React 性能分析与优化
- 05改进网络请求与加载
适用场景
- 01排查交互响应缓慢
- 02降低页面初始加载开销
- 03改善长列表渲染
- 04调查布局与动画问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ aladicf/ better-react-web-ui --skill optimize安装后在 Claude Code 中输入/optimize确认已加载。
详细介绍
识别并修复性能问题,打造更快速、流畅的用户体验。
如果图像性能问题涉及截图尺寸、图标缩放、裁剪策略或用户上传媒体的处理,请参阅图像处理。
如果性能问题源于测量大量换行文本块、虚拟化变高列表、反复调整大小后重新布局,或文本密集型 UI 热路径中的 DOM 读取(如 offsetHeight / getBoundingClientRect()),请参阅文本布局预测。
如果优化工作的重点是 LCP、INP 或 CLS,且需要更深入的子项拆解,或关于实测与实验室测量的指导,请参阅核心 Web 指标参考。
如果项目需要对超长列表进行虚拟化,且技术栈尚未确定,那么对于受支持的 React 项目,优先默认使用 TanStack Virtual 作为无头虚拟化层。如果项目已使用其他虚拟化层,则优先保留现有方案。
必须完成的准备工作
阅读 frontend-design,并遵循其中的上下文收集流程。复用可用上下文,只针对会造成重大影响的信息缺口进行询问。此外,还要了解:目标设备、性能限制,以及哪些用户交互让人感觉缓慢。
评估性能问题
了解当前性能并找出问题:
- 测量当前状态:
- 核心 Web 指标:LCP、FID/INP、CLS 分数
- 加载时间:可交互时间、首次内容绘制时间
- 交互延迟:筛选、搜索、保存、打开面板、自动补全及其他重复操作的响应时间
- 包大小:JavaScript、CSS、图像的大小
- 运行时性能:帧率、内存使用量、CPU 使用率
- 网络:请求数量、负载大小、瀑布图
- 找出瓶颈:
- 哪些地方很慢?(初始加载?交互?动画?)
- 是什么导致的?(图像过大?JavaScript 开销过高?布局抖动?)
- 问题有多严重?(能否察觉?是否恼人?是否阻塞操作?)
- 影响哪些用户?(所有用户?窄屏布局用户?网络连接较慢的用户?)
重要:优化前后都要测量。过早优化会浪费时间。只优化真正重要的问题。
优化策略
制定系统的改进计划:
交互速度的多尔蒂阈值
常规交互应保持用户操作流畅。力求让用户立即看到响应,并尽可能让交互在大约 400ms 内完成。
如果处理时间会超过这个范围:
- 在约 100ms 内提供即时反馈(按下、激活、加载、乐观状态)
- 尽早流式传输或显示部分内容,而不是等到全部完成
- 为常见操作路径预取可能访问的下一屏或数据
- 优先使用骨架屏、乐观 UI 和渐进式渲染,避免空等和通用加载指示器
对于高频操作,如果界面可以通过响应用户意图来保持操作流畅,就不要只依赖加载指示器作为主要体验。
加载性能
优化图像:
- 使用现代格式(WebP、AVIF)
- 使用合适的尺寸(不要为 300px 的显示区域加载 3000px 的图像)
- 对首屏以下的图像进行延迟加载
- 首屏中立即可见的图像应立即加载。使用已安装框架推荐的图像处理约定,为可能的 LCP 图像设置优先级。请参阅浏览器图像加载。
- 使用响应式图像(
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="eager"
fetchpriority="high"
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="eager" fetchpriority="high">
</picture>
实用规则:
- 对主视觉图片、大型背景图和以照片为主且最需要节省体积的图库生成 AVIF
- 保留 WebP 作为回退格式,以兼容更多浏览器
- 对体积很小的图标或简单图形,不要使用 AVIF,因为编码开销可能超过节省的空间
- 尽可能使用能自动处理格式协商的图片 CDN
- 在低端设备上测试 AVIF 解码速度;这种格式体积更小,但解码速度可能比 WebP 慢
减小 JavaScript 包体积:
- 代码拆分(按路由、按组件)
- Tree shaking(移除未使用的代码)
- 移除未使用的依赖项
- 延迟加载非关键代码
- 使用动态导入加载大型组件
// Lazy load heavy component
const HeavyChart = lazy(() => import('./HeavyChart'));
优化 Tailwind 输出:
- 保持 Tailwind 类名静态可发现,以便构建过程只生成正在使用的工具类
- 避免使用范围过广的 safelist 和运行时构建的类字符串,以免生成大量输出
- 相较于临时样式方案,优先使用 Tailwind 主题令牌、工具类和变体
- 仅对能从隔离中受益的独立区域使用 CSS containment 工具类或 Tailwind 任意属性
优化字体:
- 使用
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 中使用的字形
Tailwind 交付策略:
让 React 框架和 Tailwind 流程负责生成样式的交付。目标是让生成的 Tailwind 输出保持精简并适配路由,而不是添加手写的样式加载模式。
重要性:
- 大量生成的 Tailwind 输出可能会延迟 First Contentful Paint
- 范围过广的 safelist 和动态类名构造往往会生成未使用的工具类
- 框架级打包比手动预加载样式模式更可靠
实现方法:
- 保持工具类为字面量,或使用
clsx等框架支持的辅助工具,并提供已知的类名字符串 - 保持 safelist 范围精简,并记录说明
- 拆分负载较重的路由和 islands,使其 React、媒体和组件库代码仅在需要时加载
- 在性能优化过程中验证生成的 Tailwind 输出体积
- 对 React、Next.js、React Router、TanStack Start、Vite React 或 Astro React islands,使用框架推荐的 Tailwind 集成方式
优化加载策略:
- 优先加载关键资源(对非关键资源使用 async/defer)
- 预加载关键资源
- 预获取可能访问的后续页面
- 使用 service worker 实现离线支持和缓存
- 使用 HTTP/2 或 HTTP/3 实现多路复用
使用容器查询实现组件级响应式布局:
容器查询可让组件根据自身容器的大小而非视口进行适配。这样可以减少对页面级断点覆盖的需求,并提高组件的可复用性。
适用场景:
- 组件会出现在多个上下文中(侧边栏、主列、模态框)
- 组件布局应取决于可用空间,而非设备宽度
- 希望减少页面级断点的数量
请参阅 容器查询参考,了解语法、实用模式和浏览器支持。 性能说明:
- 容器查询的运行时开销极小;它们与媒体查询一样在合成器上运行
- 除非确实需要同时查询两个维度,否则优先使用
container-type: inline-size,而不是size - 避免在每个元素上都创建包含上下文;仅在组件需要查询其容器时创建
渲染性能
避免布局抖动:
// ❌ 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
});
优化渲染:
- 使用 Tailwind 包含工具类或
[contain:*]任意属性划分独立区域 - 尽量减少 DOM 深度(层级越浅,速度越快)
- 减少 DOM 大小(元素越少越好)
- 对较长的列表使用
content-visibility: auto - 对超长列表使用虚拟滚动——架构尚未确定时优先选择 TanStack Virtual;如果项目已有虚拟化方案,则优先保留现有方案
- 对文本密集且高度可变的列表或卡片,如果真正的瓶颈是文本换行,优先使用预测式文本测量,避免反复实时读取 DOM 高度
减少绘制和合成:
- 使用
transform和opacity制作动画(由 GPU 加速) - 避免对布局属性进行动画处理(width、height、top、left)
- 仅当测量结果表明临时且作用范围狭窄的
will-change能改善动画瓶颈时才添加;之后将其移除。单纯缺少will-change并不是问题。 - 尽量缩小绘制区域(越小越快)
动画性能
GPU 加速:
/* ✅ GPU-accelerated (fast) */
.animated {
transform: translateX(100px);
opacity: 0.5;
}
/* ❌ CPU-bound (slow) */
.animated {
left: 100px;
width: 300px;
}
流畅的 60fps:
- 目标是每帧耗时 16ms(60fps)
- 使用
requestAnimationFrame实现 JS 动画 - 对滚动处理程序进行防抖/节流
- 尽可能使用 Tailwind 工具类、兼容 Tailwind 的关键帧或 WAAPI
- 避免在动画期间运行耗时较长的 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 专项:
- 在添加记忆化之前,先分析具有代表性的交互。引入手动缓存前,先与项目的 React Compiler 配置保持一致。
- 当重复渲染成本高且 props 不变已被测量证实为瓶颈时,使用
memo() - 对经测量确认计算开销较大的计算使用
useMemo()。仅当稳定的函数标识能带来已证实的优化,或消费者有此要求时,才使用useCallback() - 对较长的列表进行虚拟化
- 对路由进行代码拆分
- 除非内联回调的标识变化造成了经测量确认的问题,否则保留内联回调。
- 使用 React DevTools Profiler
React 框架模式:
- 尽量减少可避免的重复渲染
- 对开销较大的操作进行防抖
- 在分析结果表明有价值时,对计算出的值进行记忆化
- 使用框架推荐的模式延迟加载路由和组件
网络优化
减少请求:
- 合并小文件
- 使用 SVG 精灵图存放图标
- 内联较小的关键资源
- 移除未使用的第三方脚本
优化 API:
- 使用分页(不要一次加载所有内容)
- 使用 GraphQL 仅请求所需字段
- 压缩响应(gzip、brotli)
- 使用 HTTP 缓存头
- 使用 CDN 提供静态资源
针对慢速连接进行优化:
- 根据连接情况进行自适应加载(navigator.connection)
- 乐观地更新 UI
- 对请求进行优先级排序
- 渐进增强
Core Web Vitals 优化
最大内容绘制(LCP < 2.5s)
- 优化首屏主视觉图片
- 保持生成的 Tailwind 输出精简,并避免在 LCP 元素出现前加载不必要的路由或 island 代码
- 预加载关键资源
- 使用 CDN
- 使用服务器端渲染
首次输入延迟(FID < 100ms)/ INP(< 200ms)
- 拆分长任务
- 延迟执行非关键 JavaScript
- 使用 Web Worker 处理繁重计算
- 减少 JavaScript 执行时间
累积布局偏移(CLS < 0.1)
- 为图片和视频设置尺寸
- 不要在现有内容上方注入内容
- 使用 Tailwind aspect-ratio 工具类
- 为广告/嵌入内容预留空间
- 避免导致布局偏移的动画
/* Reserve space for image */
.image-container {
aspect-ratio: 16 / 9;
}
性能监控
可使用的工具:
- Chrome DevTools(Lighthouse、Performance 面板)
- WebPageTest
- Core Web Vitals(Chrome UX Report)
- Bundle 分析器(webpack-bundle-analyzer)
- 性能监控工具(Sentry、DataDog、New Relic)
关键指标:
- LCP、FID/INP、CLS(Core Web Vitals)
- 可交互时间(TTI)
- 首次内容绘制(FCP)
- 总阻塞时间(TBT)
- Bundle 大小
- 请求数
重要:在真实设备和真实网络条件下进行测量。使用快速网络连接的宽屏本地浏览器并不能代表真实情况。
切勿:
- 不经测量就进行优化(过早优化)
- 为了性能牺牲无障碍性
- 在优化过程中破坏功能
- 到处使用
will-change(会创建新的图层并占用内存) - 延迟加载首屏内容
- 忽视重大问题,只优化微小问题(先优化最大的瓶颈)
- 忽视受限浏览器的性能(硬件通常较慢、CPU 预算更紧张、网络连接更慢)
验证改进效果
测试优化是否生效:
- 优化前后指标:比较 Lighthouse 分数
- 真实用户监控:跟踪真实用户的体验改进
- 不同设备:在低功耗粗指针设备和主流精确指针浏览器上测试
- 慢速连接:限速至 3G,测试体验
- 无回归问题:确保功能仍然正常
- 用户感知:使用起来是否感觉更快?
请记住:性能是一项功能。快速的体验给人的感觉是响应更灵敏、更精致、更专业。系统地优化,严格地测量,并优先提升用户感知到的性能。