为什么性能优化很重要
性能优化不仅关乎用户体验,还直接影响业务指标:
- 📱 移动用户占比增加,慢速网络普遍
- 💰 性能每提升 100ms,转化率可能提升 1%
- 🔍 Google 将性能作为 SEO 排名因素
- 😊 快速的加载速度提升用户满意度
加载性能优化
1. 资源压缩与合并
HTML 压缩
<!-- 压缩前 -->
<!DOCTYPE html>
<html>
<head>
<title>我的网站</title>
</head>
</html>
<!-- 压缩后 -->
<!DOCTYPE html><html><head><title>我的网站</title></head></html>
CSS 压缩
/* 压缩前 */
.header {
background-color: #ffffff;
padding: 20px;
}
.nav {
background-color: #ffffff;
padding: 20px;
}
/* 压缩后 */
.header,.nav{background-color:#fff;padding:20px}
JavaScript 压缩
使用工具如 Terser、UglifyJS:
// 压缩前
function hello(name) {
return 'Hello, ' + name;
}
// 压缩后
function hello(n){return'Hello, '+n}
2. 图片优化
选择合适的格式
- JPEG:适合照片和大尺寸图片
- PNG:适合透明背景和图标
- WebP:现代格式,体积更小(推荐)
- AVIF:最新格式,压缩率更高
<picture>
<source srcset="image.avif" type="image/avif">
<source srcset="image.webp" type="image/webp">
<img src="image.jpg" alt="图片描述">
</picture>
响应式图片
<img
src="image-800.jpg"
srcset="image-400.jpg 400w, image-800.jpg 800w, image-1200.jpg 1200w"
sizes="(max-width: 600px) 400px, 800px"
alt="响应式图片"
loading="lazy"
>
懒加载
<img src="placeholder.jpg" data-src="actual-image.jpg" loading="lazy" alt="图片">
// Intersection Observer API
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
document.querySelectorAll('img[data-src]').forEach(img => {
observer.observe(img);
});
3. 代码分割
动态导入
// 路由懒加载
const Home = () => import('./views/Home.vue');
const About = () => import('./views/About.vue');
// 组件懒加载
const HeavyComponent = defineAsyncComponent(() =>
import('./components/HeavyComponent.vue')
);
Webpack 代码分割
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
},
},
},
},
};
4. CDN 加速
使用 CDN 分发静态资源:
<!-- 使用 unpkg CDN -->
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
运行性能优化
1. 减少 DOM 操作
// ❌ 错误示例:频繁操作 DOM
for (let i = 0; i < 1000; i++) {
document.body.innerHTML += `<div>Item ${i}</div>`;
}
// ✅ 正确示例:批量操作
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
div.textContent = `Item ${i}`;
fragment.appendChild(div);
}
document.body.appendChild(fragment);
2. 事件委托
// ❌ 为每个元素添加事件监听器
items.forEach(item => {
item.addEventListener('click', handleClick);
});
// ✅ 使用事件委托
container.addEventListener('click', (event) => {
if (event.target.matches('.item')) {
handleClick(event);
}
});
3. 虚拟滚动
对于长列表,使用虚拟滚动只渲染可见项:
<template>
<div class="virtual-scroll" @scroll="handleScroll">
<div :style="{ height: totalHeight + 'px' }">
<div
v-for="item in visibleItems"
:key="item.id"
:style="{ transform: `translateY(${item.offset}px)` }"
class="item"
>
{{ item.content }}
</div>
</div>
</div>
</template>
4. 防抖和节流
// 防抖:延迟执行
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
// 节流:固定时间执行
function throttle(fn, delay) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= delay) {
fn.apply(this, args);
lastTime = now;
}
};
}
// 使用场景
input.addEventListener('input', debounce(handleInput, 300));
window.addEventListener('scroll', throttle(handleScroll, 100));
缓存策略
1. 浏览器缓存
Cache-Control: max-age=3600
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
2. Service Worker 缓存
// 注册 Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js');
});
}
// sw.js
const CACHE_NAME = 'v1';
const ASSETS = [
'/',
'/styles/main.css',
'/scripts/main.js',
'/images/logo.png'
];
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))
);
});
性能监控
1. Performance API
// 页面加载时间
window.addEventListener('load', () => {
const perfData = performance.getEntriesByType('navigation')[0];
console.log('页面加载时间:', perfData.loadEventEnd - perfData.loadEventStart);
});
// 资源加载时间
performance.getEntriesByType('resource').forEach(resource => {
console.log(`${resource.name}: ${resource.duration}ms`);
});
2. Web Vitals
import { getCLS, getFID, getLCP } from 'web-vitals';
// 最大内容绘制 (LCP)
getLCP(console.log);
// 首次输入延迟 (FID)
getFID(console.log);
// 累积布局偏移 (CLS)
getCLS(console.log);
性能优化检查清单
✅ 加载性能
- 压缩 HTML、CSS、JavaScript
- 优化图片格式和大小
- 实现代码分割和懒加载
- 使用 CDN 分发静态资源
- 启用浏览器缓存
✅ 运行性能
- 减少 DOM 操作
- 使用事件委托
- 实现虚拟滚动
- 应用防抖和节流
- 避免内存泄漏
✅ 监控和分析
- 使用 Performance API
- 监控 Web Vitals
- 定期进行性能审计
- 分析真实用户数据
总结
前端性能优化是一个持续的过程:
- 🚀 加载性能:从请求到渲染的整个流程
- ⚡ 运行性能:减少计算和渲染开销
- 📊 性能监控:持续追踪和分析
- 🔧 工具辅助:使用 Lighthouse、WebPageTest 等工具
记住:优化无止境,但要平衡性能、功能和开发成本!
希望这篇指南能帮助你构建更快的 Web 应用!