| name | performance-optimization |
| description | 优化前端性能 / 包大小 / 渲染 / 网络时使用。覆盖 Web Vitals (LCP/INP/CLS) / Bundle / Image / Code Splitting / 虚拟列表。融合性能预算 + RUM + 优化优先级。 |
性能优化(Performance Optimization)
参考来源:Google Web Vitals、Web.dev、Vercel Edge / Next.js 性能实践、Stripe 前端性能指南、Core Web Vitals 2024 更新(FID → INP)。
适用场景
- 首屏速度优化(LCP / FCP)
- 交互响应(INP / TTI)
- 视觉稳定性(CLS)
- 包大小优化
- 大列表 / 大表格性能
- 长任务拆解
- 内存泄漏排查
核心原则
1. 测量优先,不要猜
先 Lighthouse / DevTools / RUM
定位瓶颈再优化
2. 性能预算
首屏 JS < 200KB(gzip)
首屏图片 < 200KB
LCP < 2.5s / INP < 200ms / CLS < 0.1
3. 关注真实用户(RUM)
实验室数据 ≠ 真实数据
4. 优先级:网络 → 渲染 → 计算
- 减少传输
- 减少阻塞
- 减少重排
5. 不过早优化
小项目过度优化 = 浪费时间
6. 持续监控
性能退化是渐进的
Core Web Vitals 2026
| 指标 | 含义 | 好 | 差 |
|---|
| LCP | Largest Contentful Paint | < 2.5s | > 4s |
| INP | Interaction to Next Paint(替代 FID) | < 200ms | > 500ms |
| CLS | Cumulative Layout Shift | < 0.1 | > 0.25 |
| FCP | First Contentful Paint | < 1.8s | > 3s |
| TTFB | Time to First Byte | < 800ms | > 1.8s |
性能预算(Performance Budget)
首屏(Critical Path):
HTML:< 30 KB
关键 CSS:< 15 KB(inline 或 critical CSS)
JS(首屏):< 200 KB(gzip)
字体:< 100 KB(仅必要字重)
首屏图片:< 200 KB(webp/avif)
总页面:
JS(全部):< 500 KB
CSS(全部):< 100 KB
图片(全部):< 1 MB
时间:
TTFB:< 800ms
LCP:< 2.5s
INP:< 200ms
CLS:< 0.1
测量工具
实验室
Lighthouse(Chrome DevTools / CLI):
- Performance 评分
- 4 个 Web Vitals
- 问题清单
WebPageTest:
- 多地理位置
- 网络节流
- 视频对比
Chrome DevTools:
- Performance 面板(火焰图)
- Network 面板
- Coverage(未用代码)
- Memory 面板(泄漏)
Bundle Analyzer:
- webpack-bundle-analyzer
- vite-bundle-visualizer
- Source Map Explorer
真实用户(RUM)
import { onLCP, onINP, onCLS, onFCP, onTTFB } from 'web-vitals';
function sendToAnalytics(metric: any) {
navigator.sendBeacon('/analytics', JSON.stringify({
name: metric.name,
value: metric.value,
id: metric.id,
rating: metric.rating,
url: window.location.pathname,
}));
}
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);
1. 网络优化(最大杠杆)
减少 / 推迟资源
策略:
□ 移除未用代码(Tree-shake)
□ 第三方脚本 lazy load(async / defer)
□ 字体子集 + font-display: swap
□ 图片 webp / avif + responsive
□ HTTP/2 / HTTP/3
□ Brotli 压缩
□ CDN 边缘
图片优化(最大坑)
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority
placeholder="blur"
blurDataURL="..."
/>
<picture>
<source type="image/avif" srcset="hero.avif" />
<source type="image/webp" srcset="hero.webp" />
<img
src="hero.jpg"
alt="Hero"
loading="lazy" // 非首屏 lazy
decoding="async"
width="1200"
height="600" // 必填(防 CLS)
/>
</picture>
<img
src="hero-small.jpg"
srcset="hero-small.jpg 400w, hero-medium.jpg 800w, hero-large.jpg 1200w"
sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px"
=
/>
字体优化
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter-var.woff2') format('woff2-variations');
font-display: swap;
font-weight: 100 900;
}
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter-latin.woff2') format('woff2');
unicode-range: U+0000-007F;
}
<link rel="preload" href="/fonts/Inter-var.woff2" as="font" type="font/woff2" crossorigin>
关键资源 preload / prefetch
<link rel="preload" href="/api/critical-data" as="fetch" crossorigin>
<link rel="prefetch" href="/dashboard">
<link rel="dns-prefetch" href="https://api.example.com">
<link rel="preconnect" href="https://api.example.com">
2. 包大小优化
Bundle 分析
npm i -D rollup-plugin-visualizer
import { visualizer } from 'rollup-plugin-visualizer';
plugins: [visualizer({ open: true, gzipSize: true })]
npm i -D webpack-bundle-analyzer
Tree Shaking
import { debounce } from 'lodash-es';
import _ from 'lodash';
import { debounce } from 'lodash';
Code Splitting
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
const HeavyChart = lazy(() => import('./HeavyChart'));
function App() {
return (
<Suspense fallback={<Skeleton />}>
<HeavyChart />
</Suspense>
);
}
const HeavyChart = lazy(() =>
import( './HeavyChart')
);
替换大库
moment.js (66KB) → date-fns (modular) / dayjs (2KB)
lodash → lodash-es (tree-shake) / 单独函数
axios → fetch / ky
全 ECharts → echarts/core + 按需导入模块
全 lucide-react → lucide-react/icons/Specific
动态导入
async function exportToExcel(data) {
const { utils, writeFile } = await import('xlsx');
const ws = utils.json_to_sheet(data);
}
3. 渲染优化
React 优化
const ExpensiveItem = memo(function ExpensiveItem({ item }) {
return <div>...</div>;
});
const filteredList = useMemo(
() => list.filter(complexFilter),
[list]
);
const handleClick = useCallback((id: number) => {
setSelectedId(id);
}, []);
const [state, setState] = useState({ ... 10 个字段 ... });
const [name, setName] = useState('');
const [age, setAge] = useState(0);
const userName = useStore((s) => s.user.name);
Vue 3 优化
<!-- 1. v-once 静态内容 -->
<header v-once>
<h1>{{ title }}</h1>
</header>
<!-- 2. v-memo 跳过重渲染 -->
<div v-for="item in list" v-memo="[item.id, item.selected]">
...
</div>
<!-- 3. 大列表 shallowRef -->
<script setup>
import { shallowRef } from 'vue';
const heavyData = shallowRef(largeArray);
</script>
<!-- 4. 异步组件 -->
<script setup>
import { defineAsyncComponent } from 'vue';
const HeavyChart = defineAsyncComponent(() => import('./HeavyChart.vue'));
</script>
虚拟列表(大数据必备)
import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualList({ items }: { items: Item[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
overscan: 5,
});
return (
<div ref={parentRef} style={{ height: 600, overflow: 'auto' }}>
<div style={{ height: virtualizer.getTotalSize(), position: 'relative' }}>
{virtualizer.getVirtualItems().map((virtualRow) => (
<div
key={virtualRow.key}
style={{
position: 'absolute',
top: 0,
left: 0,
'%',
,
`(${})`,
}}
>
{items[virtualRow.index].name}
))}
);
}
CLS 优化
原因:图片 / 广告 / 字体 / 异步内容插入
解决:
□ 图片必须有 width/height(保留位置)
□ 字体 size-adjust 适配
□ 骨架屏占位
□ 广告 / 嵌入式预留容器(min-height)
□ Skeleton 与最终尺寸一致
<img src="hero.jpg" alt="" width="1200" height="600" />
<div style="aspect-ratio: 16/9; background: #eee;">
<img src="hero.jpg" alt="" />
</div>
INP 优化
INP = 用户交互 → 下次绘制的时间
优化:
□ 长任务拆分(< 50ms)
□ 防抖输入
□ 节流滚动
□ Web Worker 计算
□ requestIdleCallback / scheduler.yield()
□ 避免 forced reflow
async function processLargeData(data: Data[]) {
const chunks = chunk(data, 100);
for (const c of chunks) {
processChunk(c);
await new Promise(resolve => setTimeout(resolve, 0));
}
}
async function processLargeData(data: Data[]) {
for (const item of data) {
processItem(item);
if ('scheduler' in window && 'yield' in scheduler) {
await scheduler.yield();
}
}
}
const worker = new Worker(new URL('./worker.ts', import.meta.url));
worker.postMessage(data);
worker.onmessage = (e.);
4. 缓存与离线
HTTP 缓存:
- Cache-Control: public, max-age=31536000, immutable(哈希文件名)
- ETag / Last-Modified(HTML)
Service Worker:
- 预缓存关键资源
- Stale-While-Revalidate
- 离线兜底页
应用层:
- TanStack Query 内存缓存
- localStorage 用户偏好
- IndexedDB 大量数据
5. SSR / 流式渲染
SSR 优势:
- 首屏快(HTML 直出)
- SEO 好
潜在问题:
- TTFB 慢(服务端渲染要时间)
- hydration 慢
优化:
- Streaming SSR(HTML 流式返回)
- Server Components(React 19)
- Selective Hydration(部分 hydrate)
- Islands(Astro / Qwik)
- Resumability(Qwik)
性能优化优先级(ROI)
1. 图片优化(40% 收益)
2. 包大小(20%)
3. Code splitting(15%)
4. 虚拟列表(10%)
5. memo / useMemo(5%)
6. 微优化(5%)
不要:
- 还没测就 memo 全部
- useMemo 所有计算
- useCallback 所有函数
工作流程
1. 测量基线
- Lighthouse / RUM
- 记录 Web Vitals
2. 找瓶颈
- 网络(最常见)
- 渲染(中等)
- 计算(少)
3. 性能预算
- 首屏 JS / CSS / 图片预算
- LCP / INP / CLS 目标
4. 实施优化(按优先级)
5. 重新测量
6. 持续监控(RUM)
7. 性能退化告警
- LCP P75 > 2.5s 持续 → 告警
配套模板
templates/performance-checklist.md — Web Vitals + 包大小 + 图片 + 字体 + 渲染 + 监控
质量自检
□ Lighthouse Performance ≥ 90
□ LCP < 2.5s(P75)
□ INP < 200ms(P75)
□ CLS < 0.1
□ 首屏 JS < 200KB(gzip)
□ 总 CSS < 100KB
□ 图片 webp/avif + 懒加载
□ 字体 woff2 + font-display: swap
□ 路由级 code splitting
□ 大列表虚拟化
□ Tree-shake 友好导入
□ 包分析检查
□ RUM 监控接入
□ 性能预算文档化
常见坑
- 图片不优化——LCP 直接挂
- 图片不限尺寸——CLS 爆炸
- 字体阻塞渲染——FOIT
- 整库引入——包翻倍
- 不 code splitting——首屏 1MB
- memo 全部——过度优化反而慢
- useEffect 滥用——无限重渲染
- 大列表不虚拟化——卡死
- 同步阻塞操作——长任务
- 不监控 RUM——线上才知道
- 第三方脚本同步加载——阻塞首屏
- CSS 大量 @import——串行加载
- 图片用 background-image——不能 lazy
- scroll handler 不节流——卡顿
- 不预加载关键字体——FOUT
与其他 skill 的协作
上游:
ui-ux-designer 工作流 → 设计 / 图片资源
api-designer → 接口性能 / 缓存策略
下游:
build-deploy → 打包优化
observability(前端)→ RUM 接入
testing-frontend → 性能测试
相关参考
- 项目根
references/frontend-tech-stack-guide.md — 性能工具