在现代Web开发中,性能优化不仅影响用户体验,更直接关系到业务转化率。根据Google的研究,页面加载时间每增加1秒,转化率就会下降20%。本文将从实际项目经验出发,分享全面的性能优化策略。
Google提出的Core Web Vitals是衡量用户体验的三个关键指标:
指标 | 含义 | 目标值 | 影响因素 |
---|---|---|---|
LCP | 最大内容绘制时间 | < 2.5秒 | 服务器响应、资源加载 |
FID | 首次输入延迟 | < 100毫秒 | JavaScript执行时间 |
CLS | 累积布局偏移 | < 0.1 | 动态内容加载 |
// Performance Observer API 监控
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === 'largest-contentful-paint') {
console.log('LCP:', entry.startTime);
// 发送到分析服务
analytics.track('performance', {
metric: 'LCP',
value: entry.startTime,
url: window.location.href
});
}
}
});
observer.observe({entryTypes: ['largest-contentful-paint']});
// Web Vitals 库的使用
import {getCLS, getFID, getFCP, getLCP, getTTFB} from 'web-vitals';
function sendToAnalytics(metric) {
const body = JSON.stringify(metric);
fetch('/analytics', {
method: 'POST',
body,
keepalive: true
});
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getLCP(sendToAnalytics);
// 响应式图片实现
<picture>
<source media="(min-width: 800px)"
srcset="hero-large.webp 1200w, hero-large.jpg 1200w">
<source media="(min-width: 400px)"
srcset="hero-medium.webp 800w, hero-medium.jpg 800w">
<img src="hero-small.jpg"
srcset="hero-small.webp 400w, hero-small.jpg 400w"
alt="Hero image"
loading="lazy">
</picture>
// 渐进式图片加载
class ProgressiveImage {
constructor(img) {
this.img = img;
this.placeholder = img.dataset.placeholder;
this.src = img.dataset.src;
this.loadPlaceholder();
}
loadPlaceholder() {
if (this.placeholder) {
this.img.src = this.placeholder;
this.img.classList.add('loaded');
}
this.loadFullImage();
}
loadFullImage() {
const fullImg = new Image();
fullImg.onload = () => {
this.img.src = this.src;
this.img.classList.add('full-loaded');
};
fullImg.src = this.src;
}
}
/* 字体加载优化 */
@font-face {
font-family: 'CustomFont';
src: url('font.woff2') format('woff2'),
url('font.woff') format('woff');
font-display: swap; /* 关键:避免不可见文本闪烁 */
font-weight: 400;
font-style: normal;
}
/* 字体预加载 */
<link rel="preload" href="/fonts/custom-font.woff2"
as="font" type="font/woff2" crossorigin>
// JavaScript字体加载API
const font = new FontFace('CustomFont', 'url(/fonts/custom-font.woff2)');
font.load().then(() => {
document.fonts.add(font);
document.body.classList.add('font-loaded');
});
// 路由级别的代码分割
import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./Dashboard'));
const Profile = lazy(() =>
import('./Profile').then(module => ({
default: module.Profile
}))
);
// 动态导入
async function loadFeature() {
if (shouldLoadFeature()) {
const { AdvancedFeature } = await import('./AdvancedFeature');
return new AdvancedFeature();
}
}
// Webpack魔法注释
const ChartComponent = lazy(() =>
import(
/* webpackChunkName: "chart" */
/* webpackPrefetch: true */
'./Chart'
)
);
普通列表(10000项): 初始渲染 ~3000ms
虚拟滚动(10000项): 初始渲染 ~50ms
性能提升: 60倍
class VirtualList {
constructor(container, items, itemHeight = 50) {
this.container = container;
this.items = items;
this.itemHeight = itemHeight;
this.visibleItems = Math.ceil(container.clientHeight / itemHeight) + 1;
this.startIndex = 0;
this.init();
}
init() {
this.container.style.height = `${this.items.length * this.itemHeight}px`;
this.container.style.position = 'relative';
this.viewport = document.createElement('div');
this.viewport.style.position = 'absolute';
this.viewport.style.top = '0';
this.viewport.style.width = '100%';
this.container.appendChild(this.viewport);
this.container.addEventListener('scroll', this.handleScroll.bind(this));
this.render();
}
handleScroll() {
const scrollTop = this.container.scrollTop;
const newStartIndex = Math.floor(scrollTop / this.itemHeight);
if (newStartIndex !== this.startIndex) {
this.startIndex = newStartIndex;
this.render();
}
}
render() {
const endIndex = Math.min(
this.startIndex + this.visibleItems,
this.items.length
);
this.viewport.innerHTML = '';
this.viewport.style.top = `${this.startIndex * this.itemHeight}px`;
for (let i = this.startIndex; i < endIndex; i++) {
const item = this.createItem(this.items[i], i);
this.viewport.appendChild(item);
}
}
createItem(data, index) {
const item = document.createElement('div');
item.style.height = `${this.itemHeight}px`;
item.style.padding = '10px';
item.style.borderBottom = '1px solid #eee';
item.textContent = `Item ${index}: ${data.title}`;
return item;
}
}
// Service Worker 缓存策略
const CACHE_NAME = 'app-cache-v1';
const urlsToCache = [
'/',
'/static/css/main.css',
'/static/js/main.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => cache.addAll(urlsToCache))
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then((response) => {
// 缓存命中,返回缓存版本
if (response) {
return response;
}
// 网络请求
return fetch(event.request).then((response) => {
// 检查响应是否有效
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// 克隆响应用于缓存
const responseToCache = response.clone();
caches.open(CACHE_NAME)
.then((cache) => {
cache.put(event.request, responseToCache);
});
return response;
});
})
);
});
// 关键资源预加载
<link rel="preload" href="/api/critical-data" as="fetch" crossorigin>
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/images/hero.jpg" as="image">
// 预连接到第三方域名
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://api.analytics.com">
// 智能预加载实现
class IntelligentPreloader {
constructor() {
this.observer = new IntersectionObserver(this.handleIntersection.bind(this));
this.preloadedUrls = new Set();
}
observe(element) {
this.observer.observe(element);
}
handleIntersection(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
const url = entry.target.dataset.preload;
if (url && !this.preloadedUrls.has(url)) {
this.preloadUrl(url);
this.preloadedUrls.add(url);
}
}
});
}
preloadUrl(url) {
const link = document.createElement('link');
link.rel = 'prefetch';
link.href = url;
document.head.appendChild(link);
}
}
// CSS 关键路径提取
const criticalCSS = `
/* 首屏必需的样式 */
body { font-family: system-ui; margin: 0; }
.header { height: 60px; background: #fff; }
.hero { height: 400px; background: linear-gradient(...); }
`;
// 内联关键CSS
document.head.insertAdjacentHTML('beforeend',
`<style>${criticalCSS}</style>`
);
// 异步加载非关键CSS
function loadCSS(href) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = href;
link.media = 'print';
link.onload = () => { link.media = 'all'; };
document.head.appendChild(link);
}
loadCSS('/styles/non-critical.css');
// 批量DOM操作
function optimizedDOMUpdate(items) {
// 使用 DocumentFragment 减少重排
const fragment = document.createDocumentFragment();
items.forEach(item => {
const element = document.createElement('div');
element.textContent = item.text;
element.className = item.className;
fragment.appendChild(element);
});
// 一次性添加到DOM
container.appendChild(fragment);
}
// 使用 requestAnimationFrame 优化动画
function smoothScroll(element, targetScrollTop, duration = 300) {
const startScrollTop = element.scrollTop;
const distance = targetScrollTop - startScrollTop;
const startTime = performance.now();
function animate(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// 缓动函数
const easing = 1 - Math.pow(1 - progress, 3);
element.scrollTop = startScrollTop + distance * easing;
if (progress < 1) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
}
// WeakMap 避免内存泄漏
class ComponentManager {
constructor() {
this.componentData = new WeakMap();
this.eventListeners = new WeakMap();
}
addComponent(element, data) {
this.componentData.set(element, data);
// 使用 AbortController 管理事件监听器
const controller = new AbortController();
const { signal } = controller;
element.addEventListener('click', this.handleClick.bind(this), { signal });
element.addEventListener('resize', this.handleResize.bind(this), { signal });
this.eventListeners.set(element, controller);
}
removeComponent(element) {
// 自动清理事件监听器
const controller = this.eventListeners.get(element);
if (controller) {
controller.abort();
this.eventListeners.delete(element);
}
// WeakMap 会自动清理引用
this.componentData.delete(element);
}
}
// 对象池模式减少垃圾回收
class ObjectPool {
constructor(createFn, resetFn, maxSize = 100) {
this.createFn = createFn;
this.resetFn = resetFn;
this.maxSize = maxSize;
this.pool = [];
}
acquire() {
if (this.pool.length > 0) {
return this.pool.pop();
}
return this.createFn();
}
release(obj) {
if (this.pool.length < this.maxSize) {
this.resetFn(obj);
this.pool.push(obj);
}
}
}
优化前: LCP 4.2s, FID 180ms, CLS 0.25
优化后: LCP 1.8s, FID 45ms, CLS 0.08
转化率提升: 23%
// package.json 脚本配置
{
"scripts": {
"perf:audit": "lighthouse --chrome-flags='--headless' --output=json --output-path=./reports/lighthouse.json",
"perf:bundle": "webpack-bundle-analyzer dist/static/js/*.js",
"perf:monitor": "node scripts/performance-monitor.js"
}
}
// CI/CD 集成
// .github/workflows/performance.yml
name: Performance Audit
on: [pull_request]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Audit URLs using Lighthouse
uses: treosh/lighthouse-ci-action@v7
with:
uploadDir: './lighthouse-reports'
temporaryPublicStorage: true
Web性能优化是一个系统性工程,需要从多个维度协同优化: