性能优化是Web开发中的重要环节,直接影响用户体验和业务指标。
东巴文(db-w.cn) 认为:性能优化不是一次性工作,而是持续的过程。掌握性能分析工具、优化策略和最佳实践,能显著提升Web应用的加载速度和运行效率,为用户提供更好的体验。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>性能优化概述 - 东巴文</title>
<style>
body {
font-family: 'Segoe UI', sans-serif;
padding: 20px;
background: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 10px;
}
h1 {
color: #667eea;
margin-bottom: 20px;
}
h2 {
color: #764ba2;
margin: 30px 0 15px;
border-left: 4px solid #764ba2;
padding-left: 15px;
}
.perf-demo {
background: #f8f9fa;
padding: 20px;
border: 2px solid #ddd;
margin: 15px 0;
border-radius: 10px;
}
.perf-demo h3 {
color: #667eea;
margin-bottom: 10px;
}
.metrics-table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
.metrics-table th,
.metrics-table td {
padding: 12px;
text-align: left;
border: 1px solid #ddd;
}
.metrics-table th {
background: #667eea;
color: white;
}
</style>
</head>
<body>
<div class="container">
<h1>性能优化概述</h1>
<h2>核心性能指标</h2>
<table class="metrics-table">
<thead>
<tr>
<th>指标</th>
<th>全称</th>
<th>含义</th>
<th>目标值</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>FCP</strong></td>
<td>First Contentful Paint</td>
<td>首次内容绘制</td>
<td>< 1.8s</td>
</tr>
<tr>
<td><strong>LCP</strong></td>
<td>Largest Contentful Paint</td>
<td>最大内容绘制</td>
<td>< 2.5s</td>
</tr>
<tr>
<td><strong>FID</strong></td>
<td>First Input Delay</td>
<td>首次输入延迟</td>
<td>< 100ms</td>
</tr>
<tr>
<td><strong>CLS</strong></td>
<td>Cumulative Layout Shift</td>
<td>累积布局偏移</td>
<td>< 0.1</td>
</tr>
<tr>
<td><strong>TTFB</strong></td>
<td>Time to First Byte</td>
<td>首字节时间</td>
<td>< 600ms</td>
</tr>
<tr>
<td><strong>TTI</strong></td>
<td>Time to Interactive</td>
<td>可交互时间</td>
<td>< 3.8s</td>
</tr>
<tr>
<td><strong>TBT</strong></td>
<td>Total Blocking Time</td>
<td>总阻塞时间</td>
<td>< 200ms</td>
</tr>
<tr>
<td><strong>SI</strong></td>
<td>Speed Index</td>
<td>速度指数</td>
<td>< 3.4s</td>
</tr>
</tbody>
</table>
<h2>性能测量工具</h2>
<div class="perf-demo">
<h3>常用工具</h3>
<div style="background: #2d2d2d; color: #f8f8f2; padding: 15px; border-radius: 5px; margin: 10px 0;">
<pre>
1. Chrome DevTools
- Performance面板:记录和分析运行时性能
- Network面板:分析网络请求
- Lighthouse:综合性能审计
- Coverage:代码覆盖率分析
2. WebPageTest
- 多地点测试
- 瀑布流分析
- 视频录制
- 性能评分
3. Lighthouse
- 性能评分
- 最佳实践检查
- SEO检查
- 可访问性检查
4. PageSpeed Insights
- Core Web Vitals评估
- 优化建议
- 移动端和桌面端分析
5. Web Vitals库
- 实时性能监控
- 真实用户数据(RUM)
- 自动上报
</pre>
</div>
</div>
<h2>性能测量API</h2>
<div class="perf-demo">
<h3>Performance API</h3>
<div style="background: #2d2d2d; color: #f8f8f2; padding: 15px; border-radius: 5px; margin: 10px 0;">
<pre>
// 1. Performance Timeline API
// 测量代码执行时间
performance.mark('start');
// ... 执行代码
performance.mark('end');
performance.measure('My Measure', 'start', 'end');
const measures = performance.getEntriesByName('My Measure');
console.log('执行时间:', measures[0].duration, 'ms');
// 2. Navigation Timing API
// 页面加载性能
const timing = performance.timing;
const loadTime = timing.loadEventEnd - timing.navigationStart;
const domReady = timing.domContentLoadedEventEnd - timing.navigationStart;
const ttfb = timing.responseStart - timing.navigationStart;
console.log('页面加载时间:', loadTime, 'ms');
console.log('DOM Ready:', domReady, 'ms');
console.log('TTFB:', ttfb, 'ms');
// 3. Resource Timing API
// 资源加载性能
const resources = performance.getEntriesByType('resource');
resources.forEach(resource => {
console.log(resource.name, resource.duration, 'ms');
});
// 4. User Timing API
// 自定义性能标记
performance.mark('feature-start');
// ... 功能代码
performance.mark('feature-end');
performance.measure('feature', 'feature-start', 'feature-end');
// 5. Performance Observer
// 监听性能事件
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
console.log(entry.name, entry.entryType, entry.startTime, entry.duration);
});
});
observer.observe({ entryTypes: ['measure', 'resource', 'paint'] });
</pre>
</div>
</div>
<h2>Web Vitals监控</h2>
<div class="perf-demo">
<h3>使用web-vitals库</h3>
<div style="background: #2d2d2d; color: #f8f8f2; padding: 15px; border-radius: 5px; margin: 10px 0;">
<pre>
// 安装: npm install web-vitals
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
// 监控CLS
getCLS(console.log);
// 监控FID
getFID(console.log);
// 监控FCP
getFCP(console.log);
// 监控LCP
getLCP(console.log);
// 监控TTFB
getTTFB(console.log);
// 发送到分析服务
function sendToAnalytics(metric) {
const body = JSON.stringify({
name: metric.name,
value: metric.value,
delta: metric.delta,
id: metric.id,
});
// 使用navigator.sendBeacon确保发送
if (navigator.sendBeacon) {
navigator.sendBeacon('/analytics', body);
} else {
fetch('/analytics', {
body,
method: 'POST',
keepalive: true
});
}
}
// 应用到所有指标
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);
</pre>
</div>
</div>
<div style="background: #fff3cd; padding: 15px; border-radius: 5px; margin-top: 20px;">
<strong>东巴文提示:</strong>性能优化的第一步是测量。掌握Core Web Vitals指标、性能测量工具和API,能准确识别性能瓶颈,为优化提供数据支持。
</div>
</div>
</body>
</html>
加载性能优化关注如何让页面更快地显示内容。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>加载性能优化 - 东巴文</title>
<style>
body {
font-family: 'Segoe UI', sans-serif;
padding: 20px;
background: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 10px;
}
h1 {
color: #667eea;
margin-bottom: 20px;
}
h2 {
color: #764ba2;
margin: 30px 0 15px;
border-left: 4px solid #764ba2;
padding-left: 15px;
}
.loading-demo {
background: #f8f9fa;
padding: 20px;
border: 2px solid #ddd;
margin: 15px 0;
border-radius: 10px;
}
.loading-demo h3 {
color: #667eea;
margin-bottom: 10px;
}
.code-block {
background: #2d2d2d;
color: #f8f8f2;
padding: 15px;
border-radius: 5px;
overflow-x: auto;
margin: 10px 0;
}
</style>
</head>
<body>
<div class="container">
<h1>加载性能优化</h1>
<h2>资源压缩</h2>
<div class="loading-demo">
<h3>代码压缩</h3>
<div class="code-block">
<pre>
// JavaScript压缩(使用Terser)
// 安装: npm install terser -D
// terser.config.js
module.exports = {
compress: {
drop_console: true, // 移除console
drop_debugger: true, // 移除debugger
pure_funcs: ['console.log'] // 移除特定函数
},
mangle: {
toplevel: true // 顶级作用域变量混淆
},
format: {
comments: false // 移除注释
}
};
// CSS压缩(使用cssnano)
// 安装: npm install cssnano -D
// postcss.config.js
module.exports = {
plugins: [
require('cssnano')({
preset: 'advanced'
})
]
};
// HTML压缩(使用html-minifier)
// 安装: npm install html-minifier -D
const htmlMinifier = require('html-minifier');
const minified = htmlMinifier.minify(html, {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true,
minifyCSS: true,
minifyJS: true
});
</pre>
</div>
</div>
<h2>代码分割</h2>
<div class="loading-demo">
<h3>动态导入</h3>
<div class="code-block">
<pre>
// 1. 动态import()
// 路由懒加载
const routes = [
{
path: '/home',
component: () => import('./views/Home.vue')
},
{
path: '/about',
component: () => import('./views/About.vue')
}
];
// 条件加载
async function loadModule() {
if (condition) {
const module = await import('./heavy-module.js');
module.doSomething();
}
}
// 2. Webpack代码分割
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
},
common: {
name: 'common',
minChunks: 2,
chunks: 'initial',
priority: -10
}
}
}
}
};
// 3. React.lazy
import React, { Suspense, lazy } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}
// 4. Vue异步组件
const AsyncComponent = () => ({
component: import('./AsyncComponent.vue'),
loading: LoadingComponent,
error: ErrorComponent,
delay: 200,
timeout: 3000
});
</pre>
</div>
</div>
<h2>资源预加载</h2>
<div class="loading-demo">
<h3>预加载策略</h3>
<div class="code-block">
<pre>
<!-- 1. preload:预加载当前页面必需资源 -->
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="main.js" as="script">
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="hero.jpg" as="image">
<!-- 2. prefetch:预取未来可能需要的资源 -->
<link rel="prefetch" href="next-page.js" as="script">
<link rel="prefetch" href="detail.html" as="document">
<!-- 3. preconnect:预连接域名 -->
<link rel="preconnect" href="https://cdn.example.com">
<link rel="dns-prefetch" href="https://cdn.example.com">
<!-- 4. modulepreload:预加载ES模块 -->
<link rel="modulepreload" href="module.js">
<!-- 5. 预加载关键字体 -->
<style>
@font-face {
font-family: 'MyFont';
src: url('font.woff2') format('woff2');
font-display: swap; /* 字体加载策略 */
}
</style>
<!-- 6. 预加载关键CSS -->
<style>
/* 内联关键CSS */
body { margin: 0; font-family: sans-serif; }
.header { background: #667eea; }
</style>
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
</pre>
</div>
</div>
<h2>图片优化</h2>
<div class="loading-demo">
<h3>图片加载优化</h3>
<div class="code-block">
<pre>
<!-- 1. 响应式图片 -->
<img
src="image.jpg"
srcset="image-320.jpg 320w,
image-640.jpg 640w,
image-1280.jpg 1280w"
sizes="(max-width: 640px) 100vw,
(max-width: 1024px) 50vw,
33vw"
alt="响应式图片"
>
<!-- 2. 懒加载 -->
<img src="placeholder.jpg" data-src="image.jpg" loading="lazy" alt="懒加载图片">
<!-- 3. 现代图片格式 -->
<picture>
<source srcset="image.avif" type="image/avif">
<source srcset="image.webp" type="image/webp">
<img src="image.jpg" alt="降级图片">
</picture>
<!-- 4. 占位符 -->
<img
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 800 600'%3E%3Crect fill='%23f0f0f0' width='800' height='600'/%3E%3C/svg%3E"
data-src="image.jpg"
class="lazy"
alt="占位符图片"
>
<!-- 5. 低质量图片占位符(LQIP) -->
<img
src="image-tiny.jpg"
data-src="image-full.jpg"
class="blur-up"
alt="渐进式图片"
>
<!-- 6. 懒加载JavaScript实现 -->
<script>
const images = document.querySelectorAll('img[data-src]');
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.removeAttribute('data-src');
observer.unobserve(img);
}
});
}, {
rootMargin: '50px 0px',
threshold: 0.01
});
images.forEach(img => imageObserver.observe(img));
</script>
</pre>
</div>
</div>
<h2>缓存策略</h2>
<div class="loading-demo">
<h3>HTTP缓存</h3>
<div class="code-block">
<pre>
// 服务端缓存配置(Nginx)
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location ~* \.(html)$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
// Service Worker缓存
const CACHE_NAME = 'v1';
const STATIC_ASSETS = [
'/',
'/styles/main.css',
'/scripts/main.js',
'/images/logo.png'
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(STATIC_ASSETS))
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => response || fetch(event.request))
);
});
// 版本化资源
// 使用内容哈希命名
// main.a1b2c3d4.js
// styles.e5f6g7h8.css
</pre>
</div>
</div>
<div style="background: #fff3cd; padding: 15px; border-radius: 5px; margin-top: 20px;">
<strong>东巴文提示:</strong>加载性能优化是用户体验的第一道关卡。通过资源压缩、代码分割、预加载、图片优化和缓存策略,能显著提升页面加载速度,改善FCP、LCP等关键指标。
</div>
</div>
</body>
</html>
运行时性能优化关注页面运行时的流畅度和响应速度。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>运行时性能优化 - 东巴文</title>
<style>
body {
font-family: 'Segoe UI', sans-serif;
padding: 20px;
background: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 10px;
}
h1 {
color: #667eea;
margin-bottom: 20px;
}
h2 {
color: #764ba2;
margin: 30px 0 15px;
border-left: 4px solid #764ba2;
padding-left: 15px;
}
.runtime-demo {
background: #f8f9fa;
padding: 20px;
border: 2px solid #ddd;
margin: 15px 0;
border-radius: 10px;
}
.runtime-demo h3 {
color: #667eea;
margin-bottom: 10px;
}
.code-block {
background: #2d2d2d;
color: #f8f8f2;
padding: 15px;
border-radius: 5px;
overflow-x: auto;
margin: 10px 0;
}
</style>
</head>
<body>
<div class="container">
<h1>运行时性能优化</h1>
<h2>避免长任务</h2>
<div class="runtime-demo">
<h3>任务分片</h3>
<div class="code-block">
<pre>
// ❌ 长任务阻塞主线程
function processLargeArray(array) {
for (let i = 0; i < array.length; i++) {
// 处理每个元素
processItem(array[i]);
}
}
// ✅ 使用requestIdleCallback分片处理
function processLargeArrayAsync(array, callback) {
let i = 0;
function processChunk(deadline) {
while (i < array.length && deadline.timeRemaining() > 0) {
processItem(array[i]);
i++;
}
if (i < array.length) {
requestIdleCallback(processChunk);
} else {
callback();
}
}
requestIdleCallback(processChunk);
}
// ✅ 使用setTimeout分片
function processLargeArrayChunked(array, chunkSize = 100) {
let i = 0;
function processChunk() {
const end = Math.min(i + chunkSize, array.length);
while (i < end) {
processItem(array[i]);
i++;
}
if (i < array.length) {
setTimeout(processChunk, 0);
}
}
processChunk();
}
// ✅ 使用Web Worker
const worker = new Worker('processor.js');
worker.postMessage({ type: 'process', data: largeArray });
worker.onmessage = (e) => {
console.log('处理完成:', e.data);
};
// processor.js
self.onmessage = (e) => {
if (e.data.type === 'process') {
const result = processArray(e.data.data);
self.postMessage({ type: 'result', data: result });
}
};
</pre>
</div>
</div>
<h2>虚拟列表</h2>
<div class="runtime-demo">
<h3>大列表渲染优化</h3>
<div class="code-block">
<pre>
class VirtualList {
constructor(options) {
this.container = options.container;
this.itemHeight = options.itemHeight;
this.renderItem = options.renderItem;
this.data = options.data || [];
this.visibleCount = Math.ceil(this.container.clientHeight / this.itemHeight);
this.startIndex = 0;
this.endIndex = this.visibleCount;
this.init();
}
init() {
// 创建容器
this.wrapper = document.createElement('div');
this.wrapper.style.cssText = `
position: relative;
height: ${this.data.length * this.itemHeight}px;
`;
this.content = document.createElement('div');
this.content.style.cssText = `
position: absolute;
top: 0;
left: 0;
width: 100%;
`;
this.wrapper.appendChild(this.content);
this.container.appendChild(this.wrapper);
// 监听滚动
this.container.addEventListener('scroll', this.handleScroll.bind(this));
// 初始渲染
this.render();
}
handleScroll() {
const scrollTop = this.container.scrollTop;
this.startIndex = Math.floor(scrollTop / this.itemHeight);
this.endIndex = this.startIndex + this.visibleCount + 2; // 缓冲2个
this.render();
}
render() {
const visibleData = this.data.slice(
Math.max(0, this.startIndex - 2),
Math.min(this.data.length, this.endIndex + 2)
);
this.content.style.transform = `translateY(${this.startIndex * this.itemHeight}px)`;
this.content.innerHTML = visibleData.map((item, index) =>
this.renderItem(item, this.startIndex + index)
).join('');
}
}
// 使用示例
const virtualList = new VirtualList({
container: document.getElementById('list-container'),
itemHeight: 50,
data: Array.from({ length: 10000 }, (_, i) => ({ id: i, name: `Item ${i}` })),
renderItem: (item, index) => `
<div style="height: 50px; border-bottom: 1px solid #eee; padding: 10px;">
${item.name}
</div>
`
});
</pre>
</div>
</div>
<h2>防抖与节流</h2>
<div class="runtime-demo">
<h3>函数优化</h3>
<div class="code-block">
<pre>
// 防抖(Debounce)
function debounce(func, wait, immediate = false) {
let timeout;
return function(...args) {
const context = this;
const later = () => {
timeout = null;
if (!immediate) func.apply(context, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
// 使用
const debouncedSearch = debounce((query) => {
fetchSearchResults(query);
}, 300);
input.addEventListener('input', (e) => {
debouncedSearch(e.target.value);
});
// 节流(Throttle)
function throttle(func, limit) {
let inThrottle;
return function(...args) {
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// 使用
const throttledScroll = throttle(() => {
console.log('Scroll position:', window.scrollY);
}, 100);
window.addEventListener('scroll', throttledScroll);
// requestAnimationFrame节流
function rafThrottle(func) {
let ticking = false;
return function(...args) {
if (!ticking) {
requestAnimationFrame(() => {
func.apply(this, args);
ticking = false;
});
ticking = true;
}
};
}
// 使用
const throttledResize = rafThrottle(() => {
console.log('Window size:', window.innerWidth, window.innerHeight);
});
window.addEventListener('resize', throttledResize);
</pre>
</div>
</div>
<h2>内存管理</h2>
<div class="runtime-demo">
<h3>避免内存泄漏</h3>
<div class="code-block">
<pre>
// ❌ 未清理的事件监听器
class Component {
constructor() {
window.addEventListener('resize', this.handleResize);
}
handleResize = () => {
// 处理resize
}
}
// ✅ 清理事件监听器
class Component {
constructor() {
window.addEventListener('resize', this.handleResize);
}
handleResize = () => {
// 处理resize
}
destroy() {
window.removeEventListener('resize', this.handleResize);
}
}
// ❌ 未清理的定时器
class Timer {
constructor() {
this.timer = setInterval(() => {
this.update();
}, 1000);
}
update() {
// 更新
}
}
// ✅ 清理定时器
class Timer {
constructor() {
this.timer = setInterval(() => {
this.update();
}, 1000);
}
update() {
// 更新
}
destroy() {
clearInterval(this.timer);
}
}
// ❌ 闭包引用
function createClosure() {
const largeData = new Array(1000000).fill('data');
return function() {
console.log(largeData.length); // 持有largeData引用
};
}
// ✅ 避免不必要的引用
function createClosure() {
const largeData = new Array(1000000).fill('data');
const length = largeData.length;
return function() {
console.log(length); // 只持有需要的值
};
}
// ✅ 使用WeakMap避免强引用
const cache = new WeakMap();
function cacheData(obj, data) {
cache.set(obj, data);
}
function getData(obj) {
return cache.get(obj);
}
// 当obj被垃圾回收时,cache中的数据也会被清理
</pre>
</div>
</div>
<div style="background: #fff3cd; padding: 15px; border-radius: 5px; margin-top: 20px;">
<strong>东巴文提示:</strong>运行时性能优化关注页面的流畅度和响应速度。通过避免长任务、虚拟列表、防抖节流、内存管理等技术,能显著改善FID、TBT等指标,提升用户体验。
</div>
</div>
</body>
</html>
完成本章学习后,请尝试回答以下问题:
选择题: Core Web Vitals包括哪些指标?
填空题: ____用于预加载当前页面必需的资源,____用于预取未来可能需要的资源。
简答题: 什么是虚拟列表?它解决了什么问题?
实践题: 使用Lighthouse分析一个网页的性能,找出性能瓶颈并提出优化建议。
应用题: 分析一个性能优化的实际案例(如淘宝、京东),总结其优化策略和效果。