网站性能引擎
完整的网站性能优化系统。审计、诊断、修复和监控 — 无需外部工具(但与Lighthouse、WebPageTest、Chrome DevTools集成时可用)。
第一阶段:性能审计
快速健康检查
按顺序运行这些检查。当你发现瓶颈层时停止。
第一层 — 严重(阻止渲染):
- [ ] 第一个字节时间(TTFB)> 800ms → 服务器问题
- [ ] 第一个有意义的绘制(FCP)> 1.8s → 渲染阻塞资源
- [ ] 最大内容绘制(LCP)> 2.5s → 英雄元素问题
- [ ] 总阻塞时间(TBT)> 200ms → JavaScript问题
- [ ] 累积布局偏移(CLS)> 0.1 → 布局不稳定
- [ ] 交互到下一次绘制(INP)> 200ms → 事件处理程序问题
第二层 — 重要(影响体验):
- [ ] 页面重量 > 2MB
- [ ] 请求 > 80
- [ ] JavaScript > 500KB(压缩后)
- [ ] 图片 > 1MB总计
- [ ] 无压缩(gzip/brotli)
- [ ] 无缓存头
第三层 — 润色(竞争优势):
- [ ] 速度指数 > 3.4s
- [ ] 交互时间 > 3.8s
- [ ] 字体加载导致闪烁
- [ ] 第三方脚本 > 30%的JS
审计简报模板
audit:
url: ""
device: "mobile" # mobile | desktop | both
connection: "4G" # 3G | 4G | fiber
region: "" # 最接近目标用户
scores:
performance: null # 0-100
fcp_ms: null
lcp_ms: null
tbt_ms: null
cls: null
inp_ms: null
ttfb_ms: null
page_weight:
total_kb: null
html_kb: null
css_kb: null
js_kb: null
images_kb: null
fonts_kb: null
other_kb: null
requests:
total: null
by_type: {}
third_party_count: null
third_party_kb: null
无工具获取指标
如果没有Lighthouse/DevTools可用,使用基于网络的工具:
web_fetch "https://pagespeed.web.dev/analysis?url={encoded_url}"— 谷歌的免费工具web_search "webpagetest {url}"— 查找缓存结果web_search "site:{domain} core web vitals"— 查找CrUX数据- 检查
<head>以找出明显的问题:渲染阻塞CSS/JS,缺少预加载,没有meta视口
第二阶段:诊断 — 性能瀑布流
临界渲染路径分析
DNS → TCP → TLS → TTFB → HTML解析 → CSSOM → 渲染树 → FCP → LCP
↓
JS下载 → 解析 → 执行 → INP
瓶颈决策树:
高TTFB(>800ms)?
├─ 是 → 第三阶段A:服务器优化
└─ 否 → 高FCP(>1.8s)?
├─ 是 → 第三阶段B:渲染阻塞资源
└─ 否 → 高LCP(>2.5s)?
├─ 是 → 第三阶段C:英雄元素优化
└─ 否 → 高TBT(>200ms)?
├─ 是 → 第三阶段D:JavaScript优化
└─ 否 → 高CLS(>0.1)?
├─ 是 → 第三阶段E:布局稳定性
└─ 否 → 高INP(>200ms)?
├─ 是 → 第三阶段F:交互优化
└─ 否 → ✅ 性能良好!
资源影响评分
按影响对每个资源进行评分:
| 因素 | 权重 | 1分 | 3分 | 5分 |
|---|---|---|---|---|
| 大小(KB) | 3倍 | <10 | 10-100 | >100 |
| 渲染阻塞 | 5倍 | 无 | 部分 | 完全 |
| 上折叠影响 | 4倍 | 无 | 间接 | 直接 |
| 可缓存 | 2倍 | 长缓存 | 短缓存 | 无缓存 |
| 可压缩 | 2倍 | 已完成 | 可能 | 未压缩 |
优先级 = 因素 × 权重之和。首先修复最高分数。
第三阶段:修复剧本
3A:服务器优化(TTFB)
快速胜利:
# CDN:如果没有CDN,这是#1优先级
# 检查:curl -sI {url} | grep -i 'x-cache\|cf-cache\x-cdn'
# 压缩:必须有brotli或gzip
# 检查:curl -sI -H "Accept-Encoding: br,gzip" {url} | grep -i content-encoding
# HTTP/2或HTTP/3
# 检查:curl -sI --http2 {url} | head -1
服务器端清单:
- [ ] CDN在前(Cloudflare,Fastly,CloudFront)
- [ ] Brotli压缩启用(比gzip小20-30%)
- [ ] 最小HTTP/2,如果可能的话使用HTTP/3
- [ ] 服务器端缓存(Redis,Varnish)
- [ ] 数据库查询优化(每个查询<50ms)
- [ ] 连接池启用
- [ ] 动态内容的边缘计算(Workers,Lambda@Edge)
缓存头模板:
# 静态资产(CSS,JS,图片,字体)
Cache-Control: public, max-age=31536000, immutable
# HTML页面
Cache-Control: public, max-age=0, must-revalidate
# API响应
Cache-Control: private, max-age=60, stale-while-revalidate=300
3B:渲染阻塞资源(FCP)
CSS优化:
<!-- 之前:渲染阻塞 -->
<link rel="stylesheet" href="styles.css">
<!-- 之后:关键CSS内联 + 异步加载 -->
<style>/* 关键上折叠CSS在这里(< 14KB) */</style>
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
规则:
- 内联关键CSS(上折叠样式,< 14KB)
- 延迟非关键CSS
- 删除未使用的CSS(典型节省:60-90%)
- 合并媒体查询
- 避免
@import(创建顺序加载)
JavaScript优化:
<!-- 之前:渲染阻塞 -->
<script src="app.js"></script>
<!-- 之后:非阻塞 -->
<script src="app.js" defer></script>
<!-- 或:独立脚本 -->
<script src="analytics.js" async></script>
规则:
defer用于应用程序脚本(保持顺序,解析后运行)async用于独立脚本(分析,广告)- 永远不要在没有defer/async的情况下在
<head>中放置<script> - 内联小型脚本(< 1KB)
3C:英雄元素优化(LCP)
LCP元素类型和修复:
| LCP元素 | 修复 |
|---|---|
<img> |
预加载 + 响应式 + 现代格式 |
<video>海报 |
预加载海报图片 |
CSS background-image |
预加载 + 内联关键CSS |
| 文本块 | 预加载字体 + 字体显示:可选 |
图像优化清单:
<!-- 优化的英雄图像 -->
<link rel="preload" as="image" href="hero.webp"
imagesrcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
imagesizes="100vw">
<img src="hero.webp"
srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
sizes="100vw"
width="1200" height="600"
alt="英雄描述"
fetchpriority="high"
decoding="async">
图像格式决策:
照片/复杂图像? → WebP(比JPEG小25-35%)
→ AVIF(小50%,但编码速度慢)
简单图形/徽标? → SVG(可缩放,微小)
→ 如果需要透明度,则仅PNG
动画? → WebM/MP4视频(不是GIF — 90%更小)
图像大小目标:
| 视口 | 最大宽度 | 目标KB |
|---|---|---|
| 移动 | 400px | < 50KB |
| 平板 | 800px | < 100KB |
| 桌面 | 1200px | < 150KB |
| 英雄/横幅 | 1600px | < 200KB |
3D:JavaScript优化(TBT)
捆绑分析方法:
- 检查总JS大小:
web_fetch页面,计算<script>标签 - 识别大型库(React,Lodash,Moment.js)
- 检查捆绑包中的重复代码
- 识别未使用的导出
常见的JS膨胀和替代品:
| 库 | 大小 | 替代品 | 大小 |
|---|---|---|---|
| moment.js | 67KB | date-fns | 2-10KB |
| lodash(完整) | 71KB | lodash-es(tree-shake) | 2-5KB |
| jQuery | 87KB | 香草JS | 0KB |
| animate.css | 80KB | CSS动画 | 1-2KB |
| chart.js | 60KB | 轻量级图表 | 40KB |
代码分割规则:
- 基于路由的分割(每个页面加载自己的JS)
- 重型组件的组件级分割(模态框,编辑器,图表)
- 动态导入用于低于折叠功能:
const Chart = lazy(() => import('./Chart')) - 稳定依赖的供应商块(变化很少 = 长缓存)
长任务分解:
// 之前:在主线程上阻塞200ms+
function processLargeList(items) {
items.forEach(item => heavyComputation(item));
}
// 之后:让主线程
async function processLargeList(items) {
for (const item of items) {
heavyComputation(item);
// 每50ms让步
if (performance.now() - start > 50) {
await scheduler.yield(); // 或setTimeout(0)
start = performance.now();
}
}
}
3E:布局稳定性(CLS)
顶部CLS原因和修复:
| 原因 | 修复 |
|---|---|
| 没有尺寸的图像 | 总是设置width + height |
| 广告/嵌入物没有空间 | 使用aspect-ratio或min-height预留空间 |
| 动态内容注入 | 使用CSScontain或预留空间 |
| Web字体导致重流 | font-display: optional或swap与size-adjust |
| 延迟加载CSS | 内联关键CSS |
反CLS模式:
/* 为动态内容预留空间 */
.ad-slot { min-height: 250px; }
.embed-container { aspect-ratio: 16/9; }
/* 防止字体交换重流 */
@font-face {
font-family: 'Brand';
src: url('brand.woff2') format('woff2');
font-display: optional; /* 无交换 = 无偏移 */
size-adjust: 105%; /* 匹配回退度量 */
}
/* 包含布局偏移 */
.dynamic-widget {
contain: layout;
min-height: 200px;
}
3F:交互优化(INP)
事件处理程序规则:
- 保持处理程序<50ms
- 滚动/调整大小(100-150ms)
- 使用
requestAnimationFrame进行视觉更新 - 将繁重的计算卸载到Web Workers
- 使用
content-visibility: auto用于屏幕外内容
输入响应性:
// 之前:在输入时阻塞
input.addEventListener('input', (e) => {
expensiveFilter(e.target.value); // 100ms+
});
// 之后: debounced + 视觉反馈
input.addEventListener('input', (e) => {
showSpinner(); // 即时视觉反馈
debounce(() => expensiveFilter(e.target.value), 150);
});
第四阶段:资源加载策略
预加载/预取/预连接决策
<!-- 预连接:你很快需要的第三方来源 -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdn.example.com" crossorigin>
<!-- DNS预取:你可能需要的第三方来源 -->
<link rel="dns-prefetch" href="https://analytics.example.com">
<!-- 预加载:本页的关键资源 -->
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="hero.webp" as="image">
<link rel="preload" href="brand.woff2" as="font" type="font/woff2" crossorigin>
<!-- 预取:下一页的资源(低优先级) -->
<link rel="prefetch" href="/next-page.js">
<!-- 模块预加载:ES模块 -->
<link rel="modulepreload" href="app.mjs">
规则:
- 每页最多3-5个预加载(更多 = 竞争优先级)
- 总是预加载:LCP图像,关键字体,上折叠CSS
- 预连接到已知的第三方来源(最多4-6)
- 仅在快速连接上预取
懒加载策略
上折叠(视口): fetchpriority="high", 无懒加载
下折叠(1-2屏幕): loading="lazy", decoding="async"
远折叠: Intersection Observer,按需加载
屏幕外小部件: content-visibility: auto
字体加载优化
/* 优化的字体加载 */
@font-face {
font-family: 'Brand';
src: url('brand.woff2') format('woff2');
font-display: swap;
unicode-range: U+0000-00FF; /* 如果适用,仅限拉丁文 */
}
字体清单:
- [ ] 仅WOFF2格式(最佳压缩)
- [ ] 字体子集(拉丁文,如果需要则扩展)
- [ ] 最多2-3个字体系列
- [ ] 总共最多4个字体文件(常规,粗体,斜体,粗斜体)
- [ ] 预加载关键字体文件
- [ ] 考虑系统字体堆栈用于正文文本
系统字体堆栈:
/* 现代系统字体 — 零网络成本 */
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
/* 等宽 */
font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, monospace;
第五阶段:第三方脚本管理
影响评估
third_party_audit:
- script: "Google Analytics 4"
size_kb: 45
blocks_render: false
loads_more_scripts: true
total_impact_kb: 90
essential: true
mitigation: "gtag async, delay until interaction"
- script: "Intercom chat widget"
size_kb: 200
blocks_render: false
loads_more_scripts: true
total_impact_kb: 450
essential: false
mitigation: "Load on scroll/click, not page load"
第三方加载策略:
// 策略1:交互时加载
document.addEventListener('scroll', () => {
loadThirdParty('chat-widget.js');
}, { once: true });
// 策略2:页面空闲后加载
requestIdleCallback(() => {
loadThirdParty('analytics.js');
});
// 策略3:外观模式(在需要之前显示占位符)
chatButton.addEventListener('click', () => {
loadThirdParty('intercom.js').then(() => Intercom('show'));
});
规则:
- 每季度审计所有第三方脚本
- 每个脚本都需要业务理由
- 如果一个脚本加载>100KB,则需要加载策略
- 如果可以的话,自托管(字体,分析替代品)
- 在所有外部链接上使用
rel="noopener"
第六阶段:移动性能
移动特定优化
目标(4G上的移动):
| 指标 | 良好 | 需要工作 | 差 |
|---|---|---|---|
| FCP | < 1.8s | 1.8-3.0s | > 3.0s |
| LCP | < 2.5s | 2.5-4.0s | > 4.0s |
| TBT | < 200ms | 200-600ms | > 600ms |
| CLS | < 0.1 | 0.1-0.25 | > 0.25 |
| INP | < 200ms | 200-500ms | > 500ms |
移动特定清单:
- [ ] 视口meta标签存在
- [ ] 触摸目标≥ 48×48px
- [ ] 无水平滚动
- [ ] 图片响应式(srcset + sizes)
- [ ] JS预算< 300KB(压缩后的移动)
- [ ] 关键CSS< 14KB(适合第一个TCP往返)
- [ ] 避免复杂的CSS(重动画,大阴影)
第七阶段:性能预算
设置预算
performance_budget:
metrics:
lcp_ms: 2500
fcp_ms: 1800
tbt_ms: 200
cls: 0.1
inp_ms: 200
resources:
total_kb: 1500
js_kb: 350
css_kb: 80
images_kb: 800
fonts_kb: 100
requests:
total: 60
third_party: 15
lighthouse:
performance: 90
accessibility: 90
best_practices: 90
seo: 90
预算执行规则:
- 任何增加JS>10KB的PR都需要理由
- LCP回归>200ms阻止部署
- 每月Lighthouse审计 — 跟踪趋势
- SPA的每条路线预算(首页比管理员更严格)
预算监控模板
# 每周性能检查
date: "YYYY-MM-DD"
url: ""
device: "mobile"
scores:
lighthouse: null
lcp: null
fcp: null
tbt: null
cls: null
trend: "improving | stable | degrading"
regressions: []
actions: []
第八阶段:性能评分标准
对网站进行0-100评分:
| 维度 | 权重 | 0-2 | 3-4 | 5 |
|---|---|---|---|---|
| 核心网络生命力 | 25% | 全红 | 混合 | 全绿 |
| 页面重量 | 15% | >5MB | 2-5MB | <2MB |
| 缓存策略 | 15% | 无 | 部分 | 全部带有immutable |
| 渲染路径 | 15% | 多个阻塞 | 一些优化 | 清洁的关键路径 |
| 图像优化 | 10% | 未优化 | 部分 | WebP/AVIF + 响应式 |
| JavaScript健康 | 10% | >1MB, 没有分割 | 一些分割 | <350KB, 代码分割 |
| 第三方控制 | 5% | 未管理 | 一些延迟 | 全部管理 + 预算 |
| 移动体验 | 5% | 仅限桌面 | 响应式 | 移动优先优化 |
分数解释:
- 90-100:精英。维护和迭代。
- 70-89:良好。修复最弱的维度。
- 50-69:需要工作。遵循第三阶段剧本。
- <50:关键。从服务器 + 渲染阻塞修复开始。
第九阶段:常见架构 — 快速胜利
Next.js / React
- 使用
next/image(自动WebP,懒加载,模糊占位符) - 为静态页面启用ISR或SSG
- 使用
dynamic()用于重型组件 - 使用
@next/bundle-analyzer检查捆绑包 - 边缘缓存的中间件
WordPress
- 页面缓存插件(WP Super Cache, W3 Total Cache)
- 图像优化(ShortPixel, Imagify)
- 禁用未使用的插件(每个都增加JS+CSS)
- 使用CDN插件
- 考虑静态生成(Simply Static)
SPA(React/Vue/Svelte)
- 基于路由的代码分割(强制)
- 为SEO页面提供SSR或SSG
- 重复访问的服务工作者
- 骨架屏幕(不是旋转器)
- 长列表的虚拟滚动
静态站点
- 已经快 — 专注于图像优化
- 部署到CDN边缘(Cloudflare Pages, Netlify, Vercel)
- 内联所有关键CSS
- 最小JS(< 50KB)
第十阶段:高级技术
服务工作者缓存
// 静态资产的缓存优先
self.addEventListener('fetch', (event) => {
if (event.request.url.match(/\.(css|js|woff2|webp|avif)$/)) {
event.respondWith(
caches.match(event.request).then(cached => cached || fetch(event.request))
);
}
});
导航的资源提示
// 悬停时预测性预取
document.querySelectorAll('a').forEach(link => {
link.addEventListener('mouseenter', () => {
const prefetch = document.createElement('link');
prefetch.rel = 'prefetch';
prefetch.href = link.href;
document.head.appendChild(prefetch);
}, { once: true });
});
生产中的性能监控
// 报告核心网络生命力
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// 发送到分析
sendToAnalytics({
metric: entry.name,
value: entry.value,
rating: entry.rating, // "good" | "needs-improvement" | "poor"
});
}
}).observe({ type: 'largest-contentful-paint', buffered: true });
边缘情况
无限滚动/分页:
-
100项的虚拟滚动
- Intersection Observer加载批次
content-visibility: auto用于屏幕外项目- 内存管理:移除远离屏幕的DOM节点
具有客户端路由的SPA:
- 测量软导航(不仅仅是初始加载)
- 报告每条路线的指标
- 预取可能的下一个路线
- 保持路线JS < 100KB每个
电子商务产品页面:
- 预加载第一张产品图片
- 懒加载评论部分,相关产品
- 延迟推荐引擎JS
- 使用stale-while-revalidate缓存产品数据
媒体密集型网站:
- 懒加载以下所有内容
- 使用
<video>而不是GIF(90%更小) - 根据连接质量适应质量(Network Information API)
- 大型照片的渐进式JPEG
自然语言命令
- “审计{url}” → 运行完整的第一阶段审计
- “修复{url}上的LCP” → 第三阶段C剧本
- “什么拖慢了{url}?” → 第二阶段诊断树
- “为{project}设置性能预算” → 第七阶段模板
- “评分{url}” → 第八阶段标准
- “优化{url}上的图像” → 第三阶段C图像清单
- “减少{url}上的JavaScript” → 第三阶段D JS优化
- “修复{url}上的布局偏移” → 第三阶段E CLS剧本
- “移动性能审计{url}” → 第六阶段
- “第三方脚本审计{url}” → 第五阶段
- “每周性能检查{url}” → 第七阶段监控模板
- “比较{url1}与{url2}” → 并排审计