前端性能优化清单
前端性能优化是提升用户体验的关键因素。本文提供了一份全面的性能优化清单,帮助开发者系统性地改善应用性能。
🎯 性能指标
Core Web Vitals
Google 的核心网页指标,直接影响 SEO 排名:
- LCP (Largest Contentful Paint): 最大内容绘制 < 2.5s
- FID (First Input Delay): 首次输入延迟 < 100ms
- CLS (Cumulative Layout Shift): 累积布局偏移 < 0.1
其他重要指标
- FCP (First Contentful Paint): 首次内容绘制 < 1.8s
- TTI (Time to Interactive): 可交互时间 < 3.8s
- FMP (First Meaningful Paint): 首次有意义绘制 < 2s
- Speed Index: 速度指数 < 3.4s
📦 资源优化
✅ 图片优化
格式选择:
- [x] 使用现代图片格式(WebP、AVIF)
- [x] 为不同设备提供不同尺寸的图片
- [x] 使用 SVG 替代简单图标
- [x] 压缩图片文件大小
html
<!-- 响应式图片 -->
<picture>
<source srcset="image.avif" type="image/avif">
<source srcset="image.webp" type="image/webp">
<img src="image.jpg" alt="描述" loading="lazy">
</picture>
<!-- 不同尺寸图片 -->
<img
srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
sizes="(max-width: 480px) 480px, (max-width: 800px) 800px, 1200px"
src="medium.jpg"
alt="描述"
>
懒加载实现:
javascript
// 原生懒加载
<img src="image.jpg" loading="lazy" alt="描述">
// 自定义懒加载
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
imageObserver.unobserve(img);
}
});
});
document.querySelectorAll('img[data-src]').forEach(img => {
imageObserver.observe(img);
});
✅ 字体优化
字体加载策略:
- [x] 使用
font-display: swap
避免文本闪烁 - [x] 预加载关键字体文件
- [x] 使用字体子集减少文件大小
- [x] 考虑使用系统字体
css
/* 字体优化 */
@font-face {
font-family: 'CustomFont';
src: url('font.woff2') format('woff2');
font-display: swap; /* 立即显示备用字体 */
unicode-range: U+0000-00FF; /* 字体子集 */
}
/* 系统字体栈 */
body {
font-family:
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
'Helvetica Neue',
Arial,
sans-serif;
}
字体预加载:
html
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
✅ CSS 优化
CSS 加载优化:
- [x] 内联关键 CSS
- [x] 异步加载非关键 CSS
- [x] 移除未使用的 CSS
- [x] 使用 CSS 压缩
html
<!-- 关键 CSS 内联 -->
<style>
/* 首屏关键样式 */
.header { display: flex; }
.main { min-height: 100vh; }
</style>
<!-- 非关键 CSS 异步加载 -->
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
CSS 性能最佳实践:
css
/* 避免复杂选择器 */
/* ❌ 性能差 */
.container .sidebar .widget .title span.highlight {
color: red;
}
/* ✅ 性能好 */
.widget-title-highlight {
color: red;
}
/* 使用 transform 和 opacity 进行动画 */
.element {
transition: transform 0.3s ease, opacity 0.3s ease;
}
.element:hover {
transform: translateY(-2px);
opacity: 0.8;
}
/* 避免触发重排的属性 */
/* ❌ 触发重排 */
.element {
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { left: -100px; }
to { left: 0; }
}
/* ✅ 只触发重绘 */
.element {
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { transform: translateX(-100px); }
to { transform: translateX(0); }
}
✅ JavaScript 优化
代码分割:
- [x] 路由级代码分割
- [x] 组件级代码分割
- [x] 第三方库分离
- [x] 动态导入
javascript
// 路由级代码分割
const HomePage = lazy(() => import('./pages/HomePage'));
const AboutPage = lazy(() => import('./pages/AboutPage'));
// 组件级代码分割
const HeavyComponent = lazy(() => import('./components/HeavyComponent'));
// 条件加载
const loadChart = async () => {
if (needsChart) {
const { Chart } = await import('chart.js');
return Chart;
}
};
// 第三方库按需加载
import { debounce } from 'lodash-es'; // 只导入需要的函数
Bundle 优化:
javascript
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
common: {
name: 'common',
minChunks: 2,
chunks: 'all',
enforce: true
}
}
}
}
};
🚀 加载性能优化
✅ 资源预加载
预加载策略:
- [x] DNS 预解析
- [x] 预连接重要域名
- [x] 预加载关键资源
- [x] 预获取下一页资源
html
<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="//api.example.com">
<link rel="dns-prefetch" href="//cdn.example.com">
<!-- 预连接 -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- 预加载关键资源 -->
<link rel="preload" href="hero-image.jpg" as="image">
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="app.js" as="script">
<!-- 预获取下一页资源 -->
<link rel="prefetch" href="next-page.html">
<link rel="prefetch" href="next-page.js">
✅ 缓存策略
HTTP 缓存:
javascript
// Service Worker 缓存策略
const CACHE_NAME = 'app-v1.0.0';
const urlsToCache = [
'/',
'/static/css/main.css',
'/static/js/main.js'
];
// 缓存优先策略
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
// 缓存命中,返回缓存
if (response) {
return response;
}
// 网络请求
return fetch(event.request);
})
);
});
// 网络优先策略(适用于 API 请求)
const networkFirst = async (request) => {
try {
const response = await fetch(request);
const cache = await caches.open(CACHE_NAME);
cache.put(request, response.clone());
return response;
} catch (error) {
const cachedResponse = await caches.match(request);
return cachedResponse || new Response('Offline');
}
};
浏览器缓存配置:
nginx
# Nginx 缓存配置
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location ~* \.(html)$ {
expires 1h;
add_header Cache-Control "public, must-revalidate";
}
✅ 压缩优化
Gzip/Brotli 压缩:
nginx
# Nginx 压缩配置
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/xml+rss
application/json;
# Brotli 压缩(更好的压缩率)
brotli on;
brotli_comp_level 6;
brotli_types
text/plain
text/css
application/json
application/javascript
text/xml
application/xml
application/xml+rss
text/javascript;
⚡ 运行时性能优化
✅ JavaScript 性能
避免阻塞主线程:
javascript
// 使用 Web Workers 处理重计算
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ data: largeDataSet });
worker.onmessage = (e) => {
console.log('计算结果:', e.data);
};
// worker.js
self.onmessage = (e) => {
const result = heavyComputation(e.data);
self.postMessage(result);
};
// 使用 requestIdleCallback 延迟非关键任务
const performNonCriticalWork = (deadline) => {
while (deadline.timeRemaining() > 0 && tasks.length > 0) {
const task = tasks.shift();
task();
}
if (tasks.length > 0) {
requestIdleCallback(performNonCriticalWork);
}
};
requestIdleCallback(performNonCriticalWork);
// 时间分片处理大量数据
const processLargeList = (list, chunkSize = 1000) => {
let index = 0;
const processChunk = () => {
const chunk = list.slice(index, index + chunkSize);
chunk.forEach(item => {
// 处理单个项目
processItem(item);
});
index += chunkSize;
if (index < list.length) {
setTimeout(processChunk, 0); // 让出控制权
}
};
processChunk();
};
内存管理:
javascript
// 避免内存泄漏
class ComponentManager {
constructor() {
this.listeners = [];
this.timers = [];
this.observers = [];
}
addListener(element, event, handler) {
element.addEventListener(event, handler);
this.listeners.push({ element, event, handler });
}
addTimer(callback, delay) {
const timerId = setTimeout(callback, delay);
this.timers.push(timerId);
return timerId;
}
addObserver(observer) {
this.observers.push(observer);
}
cleanup() {
// 清理事件监听器
this.listeners.forEach(({ element, event, handler }) => {
element.removeEventListener(event, handler);
});
// 清理定时器
this.timers.forEach(timerId => {
clearTimeout(timerId);
});
// 清理观察者
this.observers.forEach(observer => {
observer.disconnect();
});
// 清空数组
this.listeners = [];
this.timers = [];
this.observers = [];
}
}
// 对象池模式减少 GC 压力
class ObjectPool {
constructor(createFn, resetFn, initialSize = 10) {
this.createFn = createFn;
this.resetFn = resetFn;
this.pool = [];
// 预创建对象
for (let i = 0; i < initialSize; i++) {
this.pool.push(this.createFn());
}
}
acquire() {
return this.pool.length > 0 ? this.pool.pop() : this.createFn();
}
release(obj) {
this.resetFn(obj);
this.pool.push(obj);
}
}
✅ DOM 操作优化
批量 DOM 操作:
javascript
// ❌ 多次 DOM 操作
items.forEach(item => {
const element = document.createElement('div');
element.textContent = item.text;
container.appendChild(element);
});
// ✅ 批量 DOM 操作
const fragment = document.createDocumentFragment();
items.forEach(item => {
const element = document.createElement('div');
element.textContent = item.text;
fragment.appendChild(element);
});
container.appendChild(fragment);
// 使用 innerHTML 批量创建
const html = items.map(item => `<div>${item.text}</div>`).join('');
container.innerHTML = html;
虚拟滚动:
javascript
// 虚拟滚动实现
class VirtualScroller {
constructor(container, items, itemHeight, renderItem) {
this.container = container;
this.items = items;
this.itemHeight = itemHeight;
this.renderItem = renderItem;
this.visibleStart = 0;
this.visibleEnd = 0;
this.scrollTop = 0;
this.init();
}
init() {
this.container.style.height = `${this.items.length * this.itemHeight}px`;
this.container.style.position = 'relative';
this.container.addEventListener('scroll', this.handleScroll.bind(this));
this.updateVisibleItems();
}
handleScroll() {
this.scrollTop = this.container.scrollTop;
this.updateVisibleItems();
}
updateVisibleItems() {
const containerHeight = this.container.clientHeight;
this.visibleStart = Math.floor(this.scrollTop / this.itemHeight);
this.visibleEnd = Math.min(
this.visibleStart + Math.ceil(containerHeight / this.itemHeight) + 1,
this.items.length
);
this.render();
}
render() {
const visibleItems = this.items.slice(this.visibleStart, this.visibleEnd);
this.container.innerHTML = '';
visibleItems.forEach((item, index) => {
const element = this.renderItem(item);
element.style.position = 'absolute';
element.style.top = `${(this.visibleStart + index) * this.itemHeight}px`;
element.style.height = `${this.itemHeight}px`;
this.container.appendChild(element);
});
}
}
🎨 渲染性能优化
✅ CSS 性能
避免重排和重绘:
css
/* 使用 transform 替代改变位置 */
.element {
/* ❌ 触发重排 */
/* left: 100px; */
/* ✅ 只触发合成 */
transform: translateX(100px);
}
/* 使用 opacity 替代 visibility */
.element {
/* ❌ 触发重绘 */
/* visibility: hidden; */
/* ✅ 只触发合成 */
opacity: 0;
}
/* 启用硬件加速 */
.accelerated {
transform: translateZ(0); /* 或 will-change: transform; */
}
优化 CSS 选择器:
css
/* ❌ 复杂选择器 */
.container .sidebar .widget .title span.highlight {
color: red;
}
/* ✅ 简单选择器 */
.widget-title-highlight {
color: red;
}
/* ❌ 通用选择器 */
* {
box-sizing: border-box;
}
/* ✅ 具体选择器 */
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
✅ 动画优化
高性能动画:
css
/* 只对 transform 和 opacity 做动画 */
.smooth-animation {
transition: transform 0.3s ease, opacity 0.3s ease;
will-change: transform, opacity;
}
.smooth-animation:hover {
transform: scale(1.05) translateY(-2px);
opacity: 0.9;
}
/* 使用 CSS 动画替代 JavaScript */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in-up {
animation: fadeInUp 0.5s ease-out;
}
JavaScript 动画优化:
javascript
// 使用 requestAnimationFrame
const animateElement = (element, start, end, duration) => {
const startTime = performance.now();
const animate = (currentTime) => {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
const current = start + (end - start) * easeOutCubic(progress);
element.style.transform = `translateX(${current}px)`;
if (progress < 1) {
requestAnimationFrame(animate);
}
};
requestAnimationFrame(animate);
};
// 缓动函数
const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
// 使用 Web Animations API
element.animate([
{ transform: 'translateX(0px)', opacity: 1 },
{ transform: 'translateX(100px)', opacity: 0 }
], {
duration: 1000,
easing: 'ease-out'
});
📱 移动端优化
✅ 触摸优化
触摸响应优化:
css
/* 移除点击延迟 */
.button {
touch-action: manipulation;
}
/* 优化滚动性能 */
.scroll-container {
-webkit-overflow-scrolling: touch;
overflow-scrolling: touch;
}
/* 防止意外缩放 */
.no-zoom {
touch-action: pan-x pan-y;
}
视口优化:
html
<!-- 基础视口设置 -->
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- 防止缩放 -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
✅ 网络优化
离线支持:
javascript
// Service Worker 离线策略
const CACHE_NAME = 'offline-cache-v1';
self.addEventListener('fetch', event => {
if (event.request.destination === 'document') {
event.respondWith(
fetch(event.request)
.catch(() => caches.match('/offline.html'))
);
}
});
// 网络状态检测
const handleNetworkChange = () => {
if (navigator.onLine) {
// 网络恢复,同步离线数据
syncOfflineData();
} else {
// 网络断开,启用离线模式
enableOfflineMode();
}
};
window.addEventListener('online', handleNetworkChange);
window.addEventListener('offline', handleNetworkChange);
🔍 性能监控
✅ 性能指标收集
Core Web Vitals 监控:
javascript
// LCP 监控
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP:', entry.startTime);
// 发送到分析服务
analytics.track('lcp', entry.startTime);
}
}).observe({ entryTypes: ['largest-contentful-paint'] });
// FID 监控
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('FID:', entry.processingStart - entry.startTime);
analytics.track('fid', entry.processingStart - entry.startTime);
}
}).observe({ entryTypes: ['first-input'] });
// CLS 监控
let clsValue = 0;
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
if (!entry.hadRecentInput) {
clsValue += entry.value;
}
}
console.log('CLS:', clsValue);
analytics.track('cls', clsValue);
}).observe({ entryTypes: ['layout-shift'] });
自定义性能监控:
javascript
class PerformanceMonitor {
constructor() {
this.metrics = {};
this.observers = [];
}
// 标记开始时间
mark(name) {
performance.mark(`${name}-start`);
}
// 测量耗时
measure(name) {
performance.mark(`${name}-end`);
performance.measure(name, `${name}-start`, `${name}-end`);
const measure = performance.getEntriesByName(name, 'measure')[0];
this.metrics[name] = measure.duration;
return measure.duration;
}
// 监控资源加载
observeResources() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 1000) {
console.warn('Slow resource:', entry.name, entry.duration);
}
}
});
observer.observe({ entryTypes: ['resource'] });
this.observers.push(observer);
}
// 监控长任务
observeLongTasks() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.warn('Long task detected:', entry.duration);
analytics.track('long-task', {
duration: entry.duration,
startTime: entry.startTime
});
}
});
observer.observe({ entryTypes: ['longtask'] });
this.observers.push(observer);
}
// 获取性能报告
getReport() {
const navigation = performance.getEntriesByType('navigation')[0];
return {
// 页面加载时间
loadTime: navigation.loadEventEnd - navigation.fetchStart,
// DOM 解析时间
domParseTime: navigation.domContentLoadedEventEnd - navigation.domLoading,
// 资源加载时间
resourceLoadTime: navigation.loadEventEnd - navigation.domContentLoadedEventEnd,
// 自定义指标
customMetrics: this.metrics
};
}
}
const monitor = new PerformanceMonitor();
monitor.observeResources();
monitor.observeLongTasks();
🛠️ 性能工具
✅ 开发工具
Chrome DevTools:
- Performance 面板分析运行时性能
- Network 面板优化资源加载
- Lighthouse 综合性能评估
- Coverage 面板找出未使用代码
性能分析工具:
javascript
// 性能分析装饰器
const performanceProfile = (target, propertyKey, descriptor) => {
const originalMethod = descriptor.value;
descriptor.value = function(...args) {
const start = performance.now();
const result = originalMethod.apply(this, args);
const end = performance.now();
console.log(`${propertyKey} 执行时间: ${end - start}ms`);
return result;
};
return descriptor;
};
// 使用示例
class DataProcessor {
@performanceProfile
processLargeDataset(data) {
// 处理大量数据
return data.map(item => transform(item));
}
}
✅ 构建优化
Webpack 优化:
javascript
// webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
optimization: {
// 代码分割
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
}
}
},
// 压缩
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true, // 移除 console
drop_debugger: true // 移除 debugger
}
}
})
]
},
plugins: [
// 分析包大小
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: false
})
]
};
📋 性能检查清单
🎯 加载性能
- [ ] 首屏时间 < 2.5s
- [ ] 资源压缩(Gzip/Brotli)
- [ ] 图片优化(格式、尺寸、懒加载)
- [ ] 字体优化(预加载、font-display)
- [ ] CSS 优化(关键路径、异步加载)
- [ ] JavaScript 优化(代码分割、Tree Shaking)
- [ ] 缓存策略(HTTP 缓存、Service Worker)
⚡ 运行时性能
- [ ] 避免长任务(> 50ms)
- [ ] 内存泄漏检查
- [ ] DOM 操作优化
- [ ] 事件处理优化
- [ ] 动画性能优化
- [ ] 虚拟滚动(长列表)
📱 移动端优化
- [ ] 触摸响应优化
- [ ] 视口配置
- [ ] 网络适配
- [ ] 离线支持
- [ ] 电池优化
🔍 监控与分析
- [ ] Core Web Vitals 监控
- [ ] 错误监控
- [ ] 性能指标收集
- [ ] 用户体验监控
- [ ] A/B 测试
总结
前端性能优化是一个持续的过程,需要:
- 建立性能文化:将性能作为开发的重要考量
- 持续监控:建立完善的性能监控体系
- 数据驱动:基于真实数据进行优化决策
- 用户体验优先:始终以用户体验为核心目标
- 渐进式优化:从影响最大的问题开始优化
通过系统性地应用这些优化策略,可以显著提升应用的性能表现和用户体验。