网站性能优化指南
本教程介绍如何对网站进行全方位的性能优化,提升用户体验和SEO排名。
一、性能指标
1.1 核心Web指标 (Core Web Vitals)
| 指标 |
全称 |
含义 |
良好标准 |
| LCP |
Largest Contentful Paint |
最大内容绘制时间 |
≤ 2.5秒 |
| INP |
Interaction to Next Paint |
交互到下次绘制 |
≤ 200ms |
| CLS |
Cumulative Layout Shift |
累积布局偏移 |
≤ 0.1 |
1.2 其他重要指标
| 指标 |
含义 |
良好标准 |
| FCP |
首次内容绘制 |
≤ 1.8秒 |
| TTFB |
首字节时间 |
≤ 800ms |
| TTI |
可交互时间 |
≤ 3.8秒 |
1.3 性能测试工具
1
2
3
4
5
6
7
8
9
| 在线工具:
- PageSpeed Insights: https://pagespeed.web.dev/
- WebPageTest: https://www.webpagetest.org/
- GTmetrix: https://gtmetrix.com/
浏览器工具:
- Chrome DevTools (Lighthouse)
- Firefox Developer Tools
- WebKit Web Inspector
|
二、资源加载优化
2.1 图片优化
选择正确的格式
1
2
3
4
5
| JPEG - 照片、复杂图像
PNG - 需要透明背景的图像
WebP - 现代格式,体积更小(推荐)
AVIF - 最新格式,压缩率更高
SVG - 图标、Logo、简单图形
|
图片压缩
1
2
3
4
5
6
7
8
| # 使用命令行工具
npm install -g imagemin-cli
# 压缩图片
imagemin images/* --out-dir=dist/images
# 转换为WebP
npx sharp -i input.png -o output.webp
|
响应式图片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| <picture>
<source
srcset="image-small.webp 400w,
image-medium.webp 800w,
image-large.webp 1200w"
sizes="(max-width: 600px) 400px,
(max-width: 1000px) 800px,
1200px"
type="image/webp">
<source
srcset="image-small.jpg 400w,
image-medium.jpg 800w,
image-large.jpg 1200w"
sizes="(max-width: 600px) 400px,
(max-width: 1000px) 800px,
1200px">
<img src="image-medium.jpg" alt="描述" loading="lazy">
</picture>
|
懒加载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| <!-- 原生懒加载 -->
<img src="image.jpg" loading="lazy" alt="描述">
<!-- 懒加载背景图 -->
<div class="lazy-bg" data-bg="image.jpg"></div>
<script>
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const bg = entry.target.dataset.bg;
entry.target.style.backgroundImage = `url(${bg})`;
observer.unobserve(entry.target);
}
});
});
document.querySelectorAll('.lazy-bg').forEach(el => observer.observe(el));
</script>
|
2.2 CSS优化
关键CSS内联
1
2
3
4
5
6
7
8
9
| <head>
<style>
/* 内联关键CSS */
.header { background: #1a1a2e; }
.hero { min-height: 100vh; }
</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压缩
1
2
3
4
5
6
| # 使用cssnano
npm install cssnano-cli -g
cssnano styles.css styles.min.css
# 使用PurgeCSS移除未使用的CSS
npx purgecss --css styles.css --content index.html --output styles.min.css
|
2.3 JavaScript优化
代码分割
1
2
3
4
5
6
7
8
9
10
| // 动态导入
const module = await import('./heavy-module.js');
// React懒加载
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
// Vue异步组件
const AsyncComponent = defineAsyncComponent(() =>
import('./AsyncComponent.vue')
);
|
延迟加载非关键JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| <!-- defer:HTML解析完成后执行 -->
<script src="script.js" defer></script>
<!-- async:下载完成后立即执行 -->
<script src="analytics.js" async></script>
<!-- 动态加载 -->
<script>
window.addEventListener('load', () => {
const script = document.createElement('script');
script.src = 'non-critical.js';
document.body.appendChild(script);
});
</script>
|
Tree Shaking
1
2
3
4
5
6
7
8
9
10
11
12
13
| // 正确的ES模块导出
export function usedFunction() { }
export function unusedFunction() { }
// 导入时只导入需要的
import { usedFunction } from './module';
// 避免默认导出整个对象
// 不推荐
export default { usedFunction, unusedFunction };
// 推荐
export { usedFunction, unusedFunction };
|
2.4 字体优化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| <!-- 预加载关键字体 -->
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
<!-- 使用font-display -->
<style>
@font-face {
font-family: 'MyFont';
src: url('font.woff2') format('woff2');
font-display: swap; /* 或 optional, fallback */
}
</style>
<!-- 限制字体变体数量 -->
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">
|
三、缓存策略
3.1 浏览器缓存
Nginx配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # 静态资源长期缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# HTML文件不缓存
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# API请求不缓存
location /api/ {
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
|
3.2 文件版本控制
1
2
3
4
5
6
| <!-- 使用文件哈希 -->
<link rel="stylesheet" href="styles.a1b2c3d4.css">
<script src="app.e5f6g7h8.js"></script>
<!-- 构建工具自动处理 -->
<!-- Vite/Webpack会自动添加哈希 -->
|
3.3 Service Worker缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| // 注册Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js');
});
}
// sw.js - Service Worker
const CACHE_NAME = 'v1';
const ASSETS = [
'/',
'/styles.css',
'/app.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(ASSETS))
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then(response => response || fetch(event.request))
);
});
|
四、CDN加速
4.1 CDN原理
1
2
3
| 用户请求 → CDN边缘节点 → 源站服务器
↓
缓存命中则直接返回
|
4.2 Cloudflare缓存规则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| 页面规则配置:
规则1 - 静态资源
URL: *your-domain.com/static/*
- 缓存级别: 缓存所有内容
- 边缘缓存TTL: 1个月
- 浏览器缓存TTL: 4小时
规则2 - HTML页面
URL: *your-domain.com/*.html
- 缓存级别: 绕过
- 浏览器缓存TTL: 4小时
规则3 - API
URL: *your-domain.com/api/*
- 缓存级别: 绕过
|
4.3 使用第三方CDN
1
2
3
4
5
6
7
| <!-- 从CDN加载常用库 -->
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<!-- 预连接CDN域名 -->
<link rel="preconnect" href="https://cdn.jsdelivr.net">
<link rel="dns-prefetch" href="https://unpkg.com">
|
五、服务器优化
5.1 启用Gzip/Brotli压缩
1
2
3
4
5
6
7
8
9
10
11
12
| # Gzip压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json application/javascript application/xml;
# Brotli压缩(需安装ngx_brotli模块)
brotli on;
brotli_comp_level 6;
brotli_types text/plain text/css text/xml application/json application/javascript application/xml;
|
5.2 HTTP/2推送
1
2
3
4
5
6
7
8
9
10
| server {
listen 443 ssl http2;
# HTTP/2服务器推送
http2_push /styles.css;
http2_push /app.js;
# 或使用Link头
add_header Link "</styles.css>; rel=preload; as=style";
}
|
5.3 减少请求次数
1
2
3
4
5
6
7
8
9
10
11
12
13
| # 合并小文件(使用CSS Sprites或内联)
# 内联小图片(Base64)
<style>
.icon {
background-image: url('data:image/svg+xml;base64,...');
}
</style>
# 使用SVG Sprites
<svg class="icon">
<use xlink:href="#icon-name"></use>
</svg>
|
六、代码层面优化
6.1 减少重排重绘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| // 批量修改DOM
// 不推荐
element.style.width = '100px';
element.style.height = '100px';
element.style.margin = '10px';
// 推荐
element.classList.add('active');
// 使用DocumentFragment
const fragment = document.createDocumentFragment();
items.forEach(item => {
fragment.appendChild(createElement(item));
});
container.appendChild(fragment);
// 使用requestAnimationFrame
function animate() {
requestAnimationFrame(() => {
// 动画代码
});
}
|
6.2 虚拟列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| // 长列表虚拟滚动
class VirtualList {
constructor(container, itemHeight, renderItem) {
this.container = container;
this.itemHeight = itemHeight;
this.renderItem = renderItem;
this.visibleItems = Math.ceil(container.clientHeight / itemHeight);
container.addEventListener('scroll', this.onScroll.bind(this));
}
onScroll() {
const scrollTop = this.container.scrollTop;
const startIndex = Math.floor(scrollTop / this.itemHeight);
this.render(startIndex);
}
render(startIndex) {
// 只渲染可见项
}
}
|
6.3 防抖与节流
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| // 防抖
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
// 节流
function throttle(fn, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// 使用示例
window.addEventListener('scroll', throttle(handleScroll, 100));
searchInput.addEventListener('input', debounce(search, 300));
|
七、性能监控
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // 页面加载性能
const timing = performance.timing;
const metrics = {
dns: timing.domainLookupEnd - timing.domainLookupStart,
tcp: timing.connectEnd - timing.connectStart,
ttfb: timing.responseStart - timing.requestStart,
download: timing.responseEnd - timing.responseStart,
domReady: timing.domContentLoadedEventEnd - timing.navigationStart,
load: timing.loadEventEnd - timing.navigationStart
};
// Web Vitals
import { getCLS, getLCP, getINP } from 'web-vitals';
getCLS(console.log);
getLCP(console.log);
getINP(console.log);
|
7.2 性能预算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // performance-budget.json
{
"budgets": [{
"resourceType": "script",
"budget": 300
}, {
"resourceType": "stylesheet",
"budget": 100
}, {
"resourceType": "image",
"budget": 500
}, {
"resourceType": "total",
"budget": 1000
}]
}
|
八、优化检查清单
首屏优化
加载优化
运行优化
监控优化
下一步
性能优化完成后,继续阅读 SEO优化指南 学习搜索引擎优化。