1419 字
7 分钟
Web 性能优化完全指南:从浏览器原理到性能指标监控
Web 性能优化完全指南:从浏览器原理到性能指标监控
⚡ 前言:性能是用户体验的核心。一个加载缓慢的网站会导致用户流失、转化率下降。这篇文章将从浏览器原理出发,系统讲解 Web 性能优化的方方面面。
一、浏览器渲染原理
1.1 关键渲染路径
浏览器将 HTML、CSS、JavaScript 转换为屏幕上的像素,经历以下步骤:
HTML → DOM Tree ↓CSS → CSSOM Tree ↓ Render Tree ↓ Layout (Reflow) ↓ Paint ↓ Composite详细过程:
-
解析 HTML 构建 DOM
- 遇到
<script>会阻塞解析(除非有 defer/async) - 遇到 CSS 会请求并解析
- 遇到
-
解析 CSS 构建 CSSOM
- CSS 是阻塞渲染的资源
- 内联 CSS 可以避免网络请求
-
合并 DOM 和 CSSOM 形成 Render Tree
- 只包含可见元素(排除 display: none)
-
Layout(布局/回流)
- 计算每个元素的位置和大小
- 几何属性变化会触发回流
-
Paint(绘制)
- 填充像素
- 颜色、背景、阴影等属性变化触发重绘
-
Composite(合成)
- 将图层合并为最终屏幕图像
- transform 和 opacity 触发合成
1.2 回流和重绘
触发回流(Reflow)的属性:
- width、height、padding、margin
- border、display、position
- top、left、right、bottom
- font-size、line-height
- 获取 offsetHeight、scrollTop 等
触发重绘(Repaint)的属性:
- color、background-color
- border-color、box-shadow
- text-decoration
优化策略:
// ❌ 错误:多次读写导致多次回流const height = element.offsetHeight; // 读取element.style.height = (height + 10) + 'px'; // 写入const width = element.offsetWidth; // 读取(触发回流)element.style.width = (width + 10) + 'px'; // 写入
// ✅ 正确:批量读写const height = element.offsetHeight;const width = element.offsetWidth;element.style.cssText = `height: ${height + 10}px; width: ${width + 10}px;`;二、资源加载优化
2.1 图片优化
格式选择:
- 照片:WebP(比 JPEG 小 25-35%)
- 透明背景:WebP 或 PNG
- 动画:WebP 或 AVIF
- 图标:SVG
响应式图片:
<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: 600px) 480px, (max-width: 1000px) 800px, 1200px" src="fallback.jpg" alt="描述">懒加载:
<!-- 原生懒加载 --><img src="placeholder.jpg" data-src="real.jpg" loading="lazy" alt="">
<!-- Intersection Observer --><script>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));</script>2.2 JavaScript 优化
代码分割:
// 路由级别分割const Home = () => import('./views/Home.vue');const About = () => import('./views/About.vue');
// 组件级别分割const HeavyComponent = defineAsyncComponent(() => import('./components/HeavyComponent.vue'));Tree Shaking:
// ✅ 使用具名导入,支持 Tree Shakingimport { map, filter } from 'lodash-es';
// ❌ 避免全量导入import _ from 'lodash';延迟加载非关键 JS:
<!-- async: 并行下载,下载完立即执行 --><script src="analytics.js" async></script>
<!-- defer: 并行下载,HTML解析完执行 --><script src="app.js" defer></script>
<!-- type="module": 自动 defer --><script type="module" src="app.js"></script>2.3 CSS 优化
关键 CSS 内联:
<head> <!-- 关键 CSS 直接内联 --> <style> /* 首屏必需的样式 */ body { margin: 0; font-family: sans-serif; } .hero { height: 100vh; background: #000; } </style>
<!-- 非关键 CSS 异步加载 --> <link rel="preload" href="non-critical.css" as="style" onload="this.onload=null;this.rel='stylesheet'"> <noscript><link rel="stylesheet" href="non-critical.css"></noscript></head>CSS 压缩和 Purge:
module.exports = { purge: ['./src/**/*.{vue,js,ts}'], // 生产环境自动移除未使用的 CSS};三、缓存策略
3.1 HTTP 缓存
# 强缓存(不会发请求到服务器)Cache-Control: max-age=31536000, immutable
# 协商缓存(会发请求验证)Last-Modified: Wed, 21 Oct 2026 07:28:00 GMTETag: "33a64df5"最佳实践:
- HTML:no-cache(总是获取最新)
- JS/CSS:永久缓存,使用内容哈希命名
- 图片/字体:长期缓存
3.2 Service Worker 缓存
const CACHE_NAME = 'v1';const urlsToCache = [ '/', '/styles.css', '/app.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); }) );});四、Core Web Vitals
Google 提出的三个核心性能指标:
4.1 LCP(Largest Contentful Paint)
定义: 最大内容绘制时间,衡量首屏加载速度。
目标: < 2.5 秒
优化方法:
- 优化服务器响应时间
- 预加载关键资源
- 压缩图片
- 使用 CDN
<!-- 预加载 LCP 图片 --><link rel="preload" as="image" href="hero.jpg" fetchpriority="high">4.2 FID(First Input Delay)/ INP
定义: 首次输入延迟 / 交互到下一帧绘制,衡量交互响应性。
目标: < 100 毫秒
优化方法:
- 拆分长任务
- 使用 Web Workers
- 延迟非关键 JS
// 拆分长任务async function processLargeArray(data) { const chunkSize = 100; for (let i = 0; i < data.length; i += chunkSize) { await new Promise(resolve => { requestIdleCallback(() => { processChunk(data.slice(i, i + chunkSize)); resolve(); }); }); }}4.3 CLS(Cumulative Layout Shift)
定义: 累积布局偏移,衡量视觉稳定性。
目标: < 0.1
优化方法:
- 图片设置尺寸
- 广告位预留空间
- 避免插入内容到已有内容上方
<!-- 预留图片空间 --><img src="photo.jpg" width="800" height="600" alt="">
<!-- 预留广告位 --><div style="min-height: 250px;"> <!-- 广告代码 --></div>五、性能监控
5.1 Web Vitals 库
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
function sendToAnalytics(metric) { const body = JSON.stringify(metric); // 发送到分析服务 navigator.sendBeacon('/analytics', body);}
getCLS(sendToAnalytics);getFID(sendToAnalytics);getLCP(sendToAnalytics);5.2 Performance API
// 获取性能数据const perfData = performance.getEntriesByType('navigation')[0];
console.log('DNS 查询:', perfData.domainLookupEnd - perfData.domainLookupStart);console.log('TCP 连接:', perfData.connectEnd - perfData.connectStart);console.log('TTFB:', perfData.responseStart - perfData.requestStart);console.log('DOM 解析:', perfData.domContentLoadedEventEnd - perfData.domLoading);console.log('资源加载:', perfData.loadEventEnd - perfData.domContentLoadedEventEnd);
// 资源加载详情const resources = performance.getEntriesByType('resource');resources.forEach(r => { console.log(`${r.name}: ${r.duration}ms`);});六、性能优化清单
加载优化
- 启用 HTTP/2
- 使用 CDN
- 压缩资源(Gzip/Brotli)
- 图片懒加载
- 使用 WebP 格式
- 内联关键 CSS
- 延迟加载非关键 JS
- 资源预加载/预连接
运行时优化
- 避免强制同步布局
- 使用 transform 和 opacity 动画
- 虚拟列表渲染大量数据
- 使用 requestAnimationFrame
- Web Workers 处理复杂计算
缓存优化
- 静态资源长期缓存
- Service Worker 缓存
- 合理使用 LocalStorage/IndexedDB
七、总结
性能优化是一个持续的过程:
- 测量:使用 Lighthouse、Web Vitals 评估现状
- 优化:针对瓶颈进行优化
- 监控:持续监控真实用户性能数据
- 迭代:不断优化改进
记住:没有银弹,要根据实际情况选择合适的优化策略。
希望这篇文章能帮助你打造极致性能的 Web 应用!
Web 性能优化完全指南:从浏览器原理到性能指标监控
https://www.oferry.com/posts/a87/