从理论到实践,全面掌握JavaScript性能优化的核心技术与最佳实践
📋 目录
引言:为什么需要性能优化第一章:性能测量与分析第二章:代码层面优化第三章:DOM操作优化第四章:事件处理优化第五章:内存管理优化第六章:网络请求优化第七章:渲染性能优化第八章:打包与加载优化第九章:现代框架性能优化第十章:性能监控与持续优化总结与最佳实践
引言:为什么需要性能优化
1.1 性能的重要性
用户体验影响
// 性能对用户体验的影响统计
const performanceImpact = {
loadTime: {
'0-1s': { satisfaction: '95%', conversionRate: 'baseline' },
'1-3s': { satisfaction: '85%', conversionRate: '-12%' },
'3-5s': { satisfaction: '70%', conversionRate: '-32%' },
'5s+': { satisfaction: '50%', conversionRate: '-53%' }
},
// Google研究数据
googleFindings: {
'100ms延迟': '转化率下降1%',
'1s延迟': '用户满意度下降16%',
'3s加载时间': '53%用户会离开'
},
// 业务影响
businessImpact: {
amazon: '每100ms延迟损失1%销售额',
walmart: '每1s改进提升2%转化率',
pinterest: '感知等待时间减少40%,流量增加15%'
}
};
性能优化的收益
| 优化方向 | 性能提升 | 用户体验改善 | 业务价值 |
|---|---|---|---|
| 🚀 首屏加载 | 30-50% | 跳出率降低20% | 转化率提升15% |
| ⚡ 交互响应 | 50-70% | 满意度提升25% | 留存率提升18% |
| 💾 内存优化 | 40-60% | 崩溃率降低30% | 用户投诉减少40% |
| 🌐 网络优化 | 20-40% | 流量节省35% | 成本降低25% |
1.2 性能优化的核心指标
// Core Web Vitals - Google核心性能指标
const coreWebVitals = {
// 最大内容绘制 (Largest Contentful Paint)
LCP: {
good: '< 2.5s',
needsImprovement: '2.5s - 4s',
poor: '> 4s',
description: '衡量加载性能'
},
// 首次输入延迟 (First Input Delay)
FID: {
good: '< 100ms',
needsImprovement: '100ms - 300ms',
poor: '> 300ms',
description: '衡量交互性'
},
// 累积布局偏移 (Cumulative Layout Shift)
CLS: {
good: '< 0.1',
needsImprovement: '0.1 - 0.25',
poor: '> 0.25',
description: '衡量视觉稳定性'
}
};
// 其他重要指标
const otherMetrics = {
FCP: 'First Contentful Paint - 首次内容绘制',
TTI: 'Time to Interactive - 可交互时间',
TBT: 'Total Blocking Time - 总阻塞时间',
SI: 'Speed Index - 速度指数'
};
第一章:性能测量与分析
2.1 浏览器性能API
Performance API基础
// ========== 1. Navigation Timing API ==========
class PerformanceMonitor {
// 获取页面加载性能
static getPageLoadMetrics() {
const perfData = performance.getEntriesByType('navigation')[0];
return {
// DNS查询时间
dnsTime: perfData.domainLookupEnd - perfData.domainLookupStart,
// TCP连接时间
tcpTime: perfData.connectEnd - perfData.connectStart,
// SSL握手时间
sslTime: perfData.secureConnectionStart > 0
? perfData.connectEnd - perfData.secureConnectionStart
: 0,
// 请求响应时间
requestTime: perfData.responseEnd - perfData.requestStart,
// DOM解析时间
domParseTime: perfData.domInteractive - perfData.responseEnd,
// 资源加载时间
resourceLoadTime: perfData.loadEventStart - perfData.domContentLoadedEventEnd,
// 总加载时间
totalTime: perfData.loadEventEnd - perfData.fetchStart,
// 白屏时间
whiteScreenTime: perfData.responseStart - perfData.fetchStart,
// 首屏时间
firstScreenTime: perfData.domContentLoadedEventEnd - perfData.fetchStart
};
}
// 打印性能报告
static printPerformanceReport() {
const metrics = this.getPageLoadMetrics();
console.log('📊 页面性能报告');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log(`⏱️ DNS查询: ${metrics.dnsTime.toFixed(2)}ms`);
console.log(`🔌 TCP连接: ${metrics.tcpTime.toFixed(2)}ms`);
console.log(`🔒 SSL握手: ${metrics.sslTime.toFixed(2)}ms`);
console.log(`📡 请求响应: ${metrics.requestTime.toFixed(2)}ms`);
console.log(`📄 DOM解析: ${metrics.domParseTime.toFixed(2)}ms`);
console.log(`📦 资源加载: ${metrics.resourceLoadTime.toFixed(2)}ms`);
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
console.log(`⚪ 白屏时间: ${metrics.whiteScreenTime.toFixed(2)}ms`);
console.log(`🖼️ 首屏时间: ${metrics.firstScreenTime.toFixed(2)}ms`);
console.log(`✅ 总加载时间: ${metrics.totalTime.toFixed(2)}ms`);
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
}
}
// 使用示例
window.addEventListener('load', () => {
// 等待所有资源加载完成后再测量
setTimeout(() => {
PerformanceMonitor.printPerformanceReport();
}, 0);
});
Resource Timing API
// ========== 2. 资源加载性能分析 ==========
class ResourcePerformanceAnalyzer {
// 分析所有资源加载性能
static analyzeResources() {
const resources = performance.getEntriesByType('resource');
const analysis = {
total: resources.length,
byType: {},
slowest: [],
totalSize: 0,
totalDuration: 0
};
resources.forEach(resource => {
// 按类型分类
const type = this.getResourceType(resource.name);
if (!analysis.byType[type]) {
analysis.byType[type] = {
count: 0,
totalDuration: 0,
totalSize: 0,
items: []
};
}
const duration = resource.responseEnd - resource.startTime;
const size = resource.transferSize || 0;
analysis.byType[type].count++;
analysis.byType[type].totalDuration += duration;
analysis.byType[type].totalSize += size;
analysis.byType[type].items.push({
name: resource.name,
duration,
size
});
analysis.totalDuration += duration;
analysis.totalSize += size;
});
// 找出最慢的10个资源
analysis.slowest = resources
.map(r => ({
name: r.name,
duration: r.responseEnd - r.startTime,
size: r.transferSize || 0
}))
.sort((a, b) => b.duration - a.duration)
.slice(0, 10);
return analysis;
}
// 获取资源类型
static getResourceType(url) {
if (url.match(/.(js)$/)) return 'JavaScript';
if (url.match(/.(css)$/)) return 'CSS';
if (url.match(/.(jpg|jpeg|png|gif|webp|svg)$/)) return 'Image';
if (url.match(/.(woff|woff2|ttf|eot)$/)) return 'Font';
if (url.match(/.(mp4|webm|ogg)$/)) return 'Video';
return 'Other';
}
// 打印资源分析报告
static printResourceReport() {
const analysis = this.analyzeResources();
console.log('📦 资源加载分析报告');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log(`总资源数: ${analysis.total}`);
console.log(`总大小: ${(analysis.totalSize / 1024).toFixed(2)} KB`);
console.log(`总耗时: ${analysis.totalDuration.toFixed(2)} ms`);
console.log('');
console.log('📊 按类型统计:');
Object.entries(analysis.byType).forEach(([type, data]) => {
console.log(` ${type}:`);
console.log(` 数量: ${data.count}`);
console.log(` 大小: ${(data.totalSize / 1024).toFixed(2)} KB`);
console.log(` 耗时: ${data.totalDuration.toFixed(2)} ms`);
});
console.log('');
console.log('🐌 最慢的10个资源:');
analysis.slowest.forEach((item, index) => {
console.log(` ${index + 1}. ${item.name.substring(item.name.lastIndexOf('/') + 1)}`);
console.log(` 耗时: ${item.duration.toFixed(2)} ms`);
console.log(` 大小: ${(item.size / 1024).toFixed(2)} KB`);
});
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
}
}
// 使用示例
window.addEventListener('load', () => {
setTimeout(() => {
ResourcePerformanceAnalyzer.printResourceReport();
}, 0);
});
User Timing API
// ========== 3. 自定义性能标记 ==========
class CustomPerformanceTracker {
// 标记开始
static mark(name) {
performance.mark(name);
}
// 测量两个标记之间的时间
static measure(name, startMark, endMark) {
performance.measure(name, startMark, endMark);
const measure = performance.getEntriesByName(name)[0];
return measure.duration;
}
// 清除标记
static clearMarks(name) {
if (name) {
performance.clearMarks(name);
} else {
performance.clearMarks();
}
}
// 清除测量
static clearMeasures(name) {
if (name) {
performance.clearMeasures(name);
} else {
performance.clearMeasures();
}
}
}
// 使用示例:测量函数执行时间
function measureFunctionPerformance() {
// 标记开始
CustomPerformanceTracker.mark('function-start');
// 执行一些操作
let sum = 0;
for (let i = 0; i < 1000000; i++) {
sum += i;
}
// 标记结束
CustomPerformanceTracker.mark('function-end');
// 测量
const duration = CustomPerformanceTracker.measure(
'function-execution',
'function-start',
'function-end'
);
console.log(`函数执行时间: ${duration.toFixed(2)}ms`);
}
// 使用示例:测量页面渲染阶段
class PageRenderTracker {
static trackPageRender() {
// 标记各个阶段
document.addEventListener('DOMContentLoaded', () => {
performance.mark('dom-ready');
});
window.addEventListener('load', () => {
performance.mark('page-loaded');
// 测量各阶段时间
const domReadyTime = performance.measure(
'dom-ready-time',
'navigationStart',
'dom-ready'
);
const pageLoadTime = performance.measure(
'page-load-time',
'navigationStart',
'page-loaded'
);
console.log('📊 页面渲染阶段:');
console.log(`DOM就绪: ${performance.getEntriesByName('dom-ready-time')[0].duration.toFixed(2)}ms`);
console.log(`页面加载: ${performance.getEntriesByName('page-load-time')[0].duration.toFixed(2)}ms`);
});
}
}
PageRenderTracker.trackPageRender();
2.2 Chrome DevTools性能分析
Performance面板使用
// ========== 性能分析辅助工具 ==========
class PerformanceProfiler {
constructor() {
this.profiles = [];
}
// 开始性能分析
startProfile(name) {
console.profile(name);
performance.mark(`${name}-start`);
}
// 结束性能分析
endProfile(name) {
performance.mark(`${name}-end`);
performance.measure(name, `${name}-start`, `${name}-end`);
console.profileEnd(name);
const measure = performance.getEntriesByName(name)[0];
this.profiles.push({
name,
duration: measure.duration,
timestamp: Date.now()
});
return measure.duration;
}
// 获取所有性能分析结果
getProfiles() {
return this.profiles;
}
// 打印性能分析报告
printReport() {
console.log('⚡ 性能分析报告');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
this.profiles.forEach(profile => {
console.log(`${profile.name}: ${profile.duration.toFixed(2)}ms`);
});
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
}
}
// 使用示例
const profiler = new PerformanceProfiler();
profiler.startProfile('data-processing');
// 执行数据处理
processLargeDataset();
profiler.endProfile('data-processing');
profiler.startProfile('dom-manipulation');
// 执行DOM操作
updateUI();
profiler.endProfile('dom-manipulation');
profiler.printReport();
2.3 Lighthouse性能审计
// ========== Lighthouse性能指标收集 ==========
class LighthouseMetricsCollector {
// 收集Core Web Vitals
static async collectCoreWebVitals() {
return new Promise((resolve) => {
const metrics = {
LCP: null,
FID: null,
CLS: null,
FCP: null,
TTFB: null
};
// LCP - Largest Contentful Paint
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
metrics.LCP = lastEntry.renderTime || lastEntry.loadTime;
}).observe({ entryTypes: ['largest-contentful-paint'] });
// FID - First Input Delay
new PerformanceObserver((list) => {
const entries = list.getEntries();
entries.forEach(entry => {
if (!metrics.FID) {
metrics.FID = entry.processingStart - entry.startTime;
}
});
}).observe({ entryTypes: ['first-input'] });
// CLS - Cumulative Layout Shift
let clsValue = 0;
new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
if (!entry.hadRecentInput) {
clsValue += entry.value;
metrics.CLS = clsValue;
}
});
}).observe({ entryTypes: ['layout-shift'] });
// FCP - First Contentful Paint
new PerformanceObserver((list) => {
const entries = list.getEntries();
entries.forEach(entry => {
if (entry.name === 'first-contentful-paint') {
metrics.FCP = entry.startTime;
}
});
}).observe({ entryTypes: ['paint'] });
// TTFB - Time to First Byte
const navTiming = performance.getEntriesByType('navigation')[0];
metrics.TTFB = navTiming.responseStart - navTiming.requestStart;
// 等待一段时间后返回结果
setTimeout(() => {
resolve(metrics);
}, 5000);
});
}
// 评估性能得分
static evaluateMetrics(metrics) {
const scores = {
LCP: this.scoreLCP(metrics.LCP),
FID: this.scoreFID(metrics.FID),
CLS: this.scoreCLS(metrics.CLS),
FCP: this.scoreFCP(metrics.FCP),
TTFB: this.scoreTTFB(metrics.TTFB)
};
// 计算总分(加权平均)
const weights = {
LCP: 0.25,
FID: 0.25,
CLS: 0.15,
FCP: 0.15,
TTFB: 0.20
};
let totalScore = 0;
Object.entries(scores).forEach(([metric, score]) => {
totalScore += score * weights[metric];
});
return {
scores,
totalScore: Math.round(totalScore),
grade: this.getGrade(totalScore)
};
}
// LCP评分
static scoreLCP(value) {
if (value === null) return 0;
if (value <= 2500) return 100;
if (value <= 4000) return 50;
return 0;
}
// FID评分
static scoreFID(value) {
if (value === null) return 0;
if (value <= 100) return 100;
if (value <= 300) return 50;
return 0;
}
// CLS评分
static scoreCLS(value) {
if (value === null) return 0;
if (value <= 0.1) return 100;
if (value <= 0.25) return 50;
return 0;
}
// FCP评分
static scoreFCP(value) {
if (value === null) return 0;
if (value <= 1800) return 100;
if (value <= 3000) return 50;
return 0;
}
// TTFB评分
static scoreTTFB(value) {
if (value === null) return 0;
if (value <= 600) return 100;
if (value <= 1500) return 50;
return 0;
}
// 获取等级
static getGrade(score) {
if (score >= 90) return 'A';
if (score >= 75) return 'B';
if (score >= 60) return 'C';
if (score >= 45) return 'D';
return 'F';
}
// 打印性能报告
static async printPerformanceReport() {
console.log('🔍 正在收集性能指标...');
const metrics = await this.collectCoreWebVitals();
const evaluation = this.evaluateMetrics(metrics);
console.log('');
console.log('📊 Core Web Vitals 性能报告');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log(`LCP (Largest Contentful Paint): ${metrics.LCP ? metrics.LCP.toFixed(2) + 'ms' : 'N/A'} - 得分: ${evaluation.scores.LCP}`);
console.log(`FID (First Input Delay): ${metrics.FID ? metrics.FID.toFixed(2) + 'ms' : 'N/A'} - 得分: ${evaluation.scores.FID}`);
console.log(`CLS (Cumulative Layout Shift): ${metrics.CLS ? metrics.CLS.toFixed(4) : 'N/A'} - 得分: ${evaluation.scores.CLS}`);
console.log(`FCP (First Contentful Paint): ${metrics.FCP ? metrics.FCP.toFixed(2) + 'ms' : 'N/A'} - 得分: ${evaluation.scores.FCP}`);
console.log(`TTFB (Time to First Byte): ${metrics.TTFB ? metrics.TTFB.toFixed(2) + 'ms' : 'N/A'} - 得分: ${evaluation.scores.TTFB}`);
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log(`总分: ${evaluation.totalScore}/100`);
console.log(`等级: ${evaluation.grade}`);
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
}
}
// 使用示例
window.addEventListener('load', () => {
LighthouseMetricsCollector.printPerformanceReport();
});
第二章:代码层面优化
3.1 算法优化
时间复杂度优化
// ========== 示例1:数组去重优化 ==========
// ❌ 低效方法 - O(n²)
function removeDuplicatesSlow(arr) {
const result = [];
for (let i = 0; i < arr.length; i++) {
if (result.indexOf(arr[i]) === -1) {
result.push(arr[i]);
}
}
return result;
}
// ✅ 高效方法 - O(n)
function removeDuplicatesFast(arr) {
return [...new Set(arr)];
}
// 性能对比
const largeArray = Array.from({ length: 10000 }, () => Math.floor(Math.random() * 1000));
console.time('慢速去重');
removeDuplicatesSlow(largeArray);
console.timeEnd('慢速去重');
// 输出: 慢速去重: ~100ms
console.time('快速去重');
removeDuplicatesFast(largeArray);
console.timeEnd('快速去重');
// 输出: 快速去重: ~2ms
// ========== 示例2:查找优化 ==========
// ❌ 低效方法 - 线性查找 O(n)
function findInArraySlow(arr, target) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === target) {
return i;
}
}
return -1;
}
// ✅ 高效方法 - 使用Map O(1)
class FastLookup {
constructor(arr) {
this.map = new Map();
arr.forEach((item, index) => {
this.map.set(item, index);
});
}
find(target) {
return this.map.has(target) ? this.map.get(target) : -1;
}
}
// 性能对比
const testArray = Array.from({ length: 100000 }, (_, i) => i);
console.time('线性查找');
findInArraySlow(testArray, 99999);
console.timeEnd('线性查找');
// 输出: 线性查找: ~1ms
const fastLookup = new FastLookup(testArray);
console.time('Map查找');
fastLookup.find(99999);
console.timeEnd('Map查找');
// 输出: Map查找: ~0.01ms
空间复杂度优化
// ========== 示例:斐波那契数列优化 ==========
// ❌ 递归方法 - 时间O(2^n),空间O(n)
function fibonacciRecursive(n) {
if (n <= 1) return n;
return fibonacciRecursive(n - 1) + fibonacciRecursive(n - 2);
}
// ✅ 动态规划 - 时间O(n),空间O(n)
function fibonacciDP(n) {
if (n <= 1) return n;
const dp = [0, 1];
for (let i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
// ✅✅ 空间优化 - 时间O(n),空间O(1)
function fibonacciOptimized(n) {
if (n <= 1) return n;
let prev = 0, curr = 1;
for (let i = 2; i <= n; i++) {
[prev, curr] = [curr, prev + curr];
}
return curr;
}
// 性能对比
console.time('递归方法');
fibonacciRecursive(30);
console.timeEnd('递归方法');
// 输出: 递归方法: ~20ms
console.time('动态规划');
fibonacciDP(30);
console.timeEnd('动态规划');
// 输出: 动态规划: ~0.1ms
console.time('空间优化');
fibonacciOptimized(30);
console.timeEnd('空间优化');
// 输出: 空间优化: ~0.05ms
3.2 循环优化
// ========== 循环优化技巧 ==========
// ❌ 低效循环
function inefficientLoop(arr) {
for (let i = 0; i < arr.length; i++) {
// 每次迭代都计算length
console.log(arr[i]);
}
}
// ✅ 缓存length
function efficientLoop(arr) {
const len = arr.length;
for (let i = 0; i < len; i++) {
console.log(arr[i]);
}
}
// ✅✅ 使用for...of(更简洁)
function modernLoop(arr) {
for (const item of arr) {
console.log(item);
}
}
// ========== 循环展开 ==========
// ❌ 普通循环
function sumNormal(arr) {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
// ✅ 循环展开(处理大数组时更快)
function sumUnrolled(arr) {
let sum = 0;
const len = arr.length;
const remainder = len % 4;
// 处理余数
for (let i = 0; i < remainder; i++) {
sum += arr[i];
}
// 4个一组处理
for (let i = remainder; i < len; i += 4) {
sum += arr[i] + arr[i + 1] + arr[i + 2] + arr[i + 3];
}
return sum;
}
// ========== 避免不必要的循环 ==========
// ❌ 多次循环
function multipleLoops(arr) {
// 第一次循环:过滤
const filtered = arr.filter(x => x > 0);
// 第二次循环:映射
const mapped = filtered.map(x => x * 2);
// 第三次循环:求和
const sum = mapped.reduce((acc, x) => acc + x, 0);
return sum;
}
// ✅ 单次循环
function singleLoop(arr) {
return arr.reduce((sum, x) => {
if (x > 0) {
sum += x * 2;
}
return sum;
}, 0);
}
// 性能对比
const largeArray = Array.from({ length: 1000000 }, () => Math.random() * 100 - 50);
console.time('多次循环');
multipleLoops(largeArray);
console.timeEnd('多次循环');
// 输出: 多次循环: ~30ms
console.time('单次循环');
singleLoop(largeArray);
console.timeEnd('单次循环');
// 输出: 单次循环: ~10ms
3.3 函数优化
函数缓存(Memoization)
// ========== 函数缓存实现 ==========
// 通用缓存装饰器
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log('从缓存返回');
return cache.get(key);
}
console.log('计算并缓存');
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
// 使用示例:昂贵的计算
function expensiveCalculation(n) {
console.log(`计算 ${n} 的阶乘...`);
let result = 1;
for (let i = 2; i <= n; i++) {
result *= i;
}
return result;
}
const memoizedCalculation = memoize(expensiveCalculation);
console.log(memoizedCalculation(10)); // 计算并缓存
console.log(memoizedCalculation(10)); // 从缓存返回
console.log(memoizedCalculation(20)); // 计算并缓存
console.log(memoizedCalculation(10)); // 从缓存返回
// ========== 带过期时间的缓存 ==========
function memoizeWithExpiry(fn, expiryTime = 5000) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
const now = Date.now();
if (cache.has(key)) {
const { value, timestamp } = cache.get(key);
if (now - timestamp < expiryTime) {
console.log('从缓存返回');
return value;
} else {
console.log('缓存已过期');
cache.delete(key);
}
}
console.log('计算并缓存');
const result = fn.apply(this, args);
cache.set(key, { value: result, timestamp: now });
return result;
};
}
// ========== LRU缓存实现 ==========
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.cache = new Map();
}
get(key) {
if (!this.cache.has(key)) {
return undefined;
}
// 移到最后(最近使用)
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
set(key, value) {
// 如果已存在,先删除
if (this.cache.has(key)) {
this.cache.delete(key);
}
// 如果超过容量,删除最久未使用的(第一个)
if (this.cache.size >= this.capacity) {
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, value);
}
}
// 使用LRU缓存的函数包装器
function memoizeWithLRU(fn, capacity = 100) {
const cache = new LRUCache(capacity);
return function(...args) {
const key = JSON.stringify(args);
const cached = cache.get(key);
if (cached !== undefined) {
console.log('从LRU缓存返回');
return cached;
}
console.log('计算并缓存到LRU');
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
防抖与节流
// ========== 防抖(Debounce)==========
// 应用场景:搜索框输入、窗口resize
function debounce(fn, delay = 300) {
let timeoutId = null;
return function(...args) {
// 清除之前的定时器
if (timeoutId) {
clearTimeout(timeoutId);
}
// 设置新的定时器
timeoutId = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
// 使用示例:搜索框
const searchInput = document.getElementById('search');
const search = debounce(function(e) {
console.log('执行搜索:', e.target.value);
// 发送API请求
}, 500);
searchInput.addEventListener('input', search);
// ========== 节流(Throttle)==========
// 应用场景:滚动事件、鼠标移动
function throttle(fn, delay = 300) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= delay) {
fn.apply(this, args);
lastTime = now;
}
};
}
// 使用示例:滚动事件
const handleScroll = throttle(function() {
console.log('滚动位置:', window.scrollY);
// 执行滚动相关操作
}, 200);
window.addEventListener('scroll', handleScroll);
// ========== 高级版本:支持立即执行 ==========
function debounceAdvanced(fn, delay = 300, immediate = false) {
let timeoutId = null;
return function(...args) {
const callNow = immediate && !timeoutId;
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(() => {
timeoutId = null;
if (!immediate) {
fn.apply(this, args);
}
}, delay);
if (callNow) {
fn.apply(this, args);
}
};
}
function throttleAdvanced(fn, delay = 300, options = {}) {
let timeoutId = null;
let lastTime = 0;
const { leading = true, trailing = true } = options;
return function(...args) {
const now = Date.now();
if (!lastTime && !leading) {
lastTime = now;
}
const remaining = delay - (now - lastTime);
if (remaining <= 0 || remaining > delay) {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
lastTime = now;
fn.apply(this, args);
} else if (!timeoutId && trailing) {
timeoutId = setTimeout(() => {
lastTime = leading ? Date.now() : 0;
timeoutId = null;
fn.apply(this, args);
}, remaining);
}
};
}
3.4 数据结构选择
// ========== 选择合适的数据结构 ==========
// 场景1:频繁查找 - 使用Map而不是Object
// ❌ 使用Object
const userDataObj = {};
for (let i = 0; i < 10000; i++) {
userDataObj[`user${i}`] = { id: i, name: `User ${i}` };
}
console.time('Object查找');
const user1 = userDataObj['user5000'];
console.timeEnd('Object查找');
// ✅ 使用Map
const userDataMap = new Map();
for (let i = 0; i < 10000; i++) {
userDataMap.set(`user${i}`, { id: i, name: `User ${i}` });
}
console.time('Map查找');
const user2 = userDataMap.get('user5000');
console.timeEnd('Map查找');
// 场景2:唯一值集合 - 使用Set
// ❌ 使用Array
const uniqueArraySlow = [];
function addUniqueToArray(value) {
if (!uniqueArraySlow.includes(value)) {
uniqueArraySlow.push(value);
}
}
console.time('Array添加唯一值');
for (let i = 0; i < 10000; i++) {
addUniqueToArray(i % 100);
}
console.timeEnd('Array添加唯一值');
// ✅ 使用Set
const uniqueSet = new Set();
console.time('Set添加唯一值');
for (let i = 0; i < 10000; i++) {
uniqueSet.add(i % 100);
}
console.timeEnd('Set添加唯一值');
// 场景3:队列操作 - 避免使用Array的shift()
// ❌ 使用Array的shift() - O(n)
class QueueSlow {
constructor() {
this.items = [];
}
enqueue(item) {
this.items.push(item);
}
dequeue() {
return this.items.shift(); // O(n) - 慢!
}
}
// ✅ 使用双指针 - O(1)
class QueueFast {
constructor() {
this.items = {};
this.head = 0;
this.tail = 0;
}
enqueue(item) {
this.items[this.tail] = item;
this.tail++;
}
dequeue() {
if (this.head === this.tail) {
return undefined;
}
const item = this.items[this.head];
delete this.items[this.head];
this.head++;
return item;
}
}
// 性能对比
const slowQueue = new QueueSlow();
const fastQueue = new QueueFast();
console.time('慢速队列');
for (let i = 0; i < 10000; i++) {
slowQueue.enqueue(i);
}
for (let i = 0; i < 10000; i++) {
slowQueue.dequeue();
}
console.timeEnd('慢速队列');
// 输出: 慢速队列: ~100ms
console.time('快速队列');
for (let i = 0; i < 10000; i++) {
fastQueue.enqueue(i);
}
for (let i = 0; i < 10000; i++) {
fastQueue.dequeue();
}
console.timeEnd('快速队列');
// 输出: 快速队列: ~2ms
第三章:DOM操作优化
4.1 减少DOM访问
// ========== DOM访问优化 ==========
// ❌ 频繁访问DOM
function inefficientDOMAccess() {
for (let i = 0; i < 1000; i++) {
document.getElementById('counter').innerHTML = i;
document.getElementById('status').innerHTML = 'Processing...';
}
}
// ✅ 缓存DOM引用
function efficientDOMAccess() {
const counter = document.getElementById('counter');
const status = document.getElementById('status');
for (let i = 0; i < 1000; i++) {
counter.innerHTML = i;
status.innerHTML = 'Processing...';
}
}
// ✅✅ 批量更新
function batchDOMUpdate() {
const counter = document.getElementById('counter');
const status = document.getElementById('status');
// 使用DocumentFragment
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
// 在内存中操作
}
// 一次性更新
counter.innerHTML = 1000;
status.innerHTML = 'Complete';
}
4.2 批量DOM操作
// ========== 批量插入元素 ==========
// ❌ 逐个插入 - 触发1000次重排
function insertOneByOne() {
const container = document.getElementById('container');
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
div.textContent = `Item ${i}`;
container.appendChild(div); // 每次都触发重排
}
}
// ✅ 使用DocumentFragment - 只触发1次重排
function insertWithFragment() {
const container = document.getElementById('container');
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
div.textContent = `Item ${i}`;
fragment.appendChild(div);
}
container.appendChild(fragment); // 只触发一次重排
}
// ✅✅ 使用innerHTML(更快,但要注意XSS)
function insertWithInnerHTML() {
const container = document.getElementById('container');
let html = '';
for (let i = 0; i < 1000; i++) {
html += `<div>Item ${i}</div>`;
}
container.innerHTML = html; // 只触发一次重排
}
// ✅✅✅ 使用模板字符串(最快)
function insertWithTemplate() {
const container = document.getElementById('container');
const html = Array.from({ length: 1000 }, (_, i) =>
`<div>Item ${i}</div>`
).join('');
container.innerHTML = html;
}
// 性能对比
console.time('逐个插入');
insertOneByOne();
console.timeEnd('逐个插入');
// 输出: 逐个插入: ~50ms
console.time('Fragment插入');
insertWithFragment();
console.timeEnd('Fragment插入');
// 输出: Fragment插入: ~10ms
console.time('innerHTML插入');
insertWithInnerHTML();
console.timeEnd('innerHTML插入');
// 输出: innerHTML插入: ~5ms
console.time('模板字符串插入');
insertWithTemplate();
console.timeEnd('模板字符串插入');
// 输出: 模板字符串插入: ~3ms
4.3 虚拟滚动
// ========== 虚拟滚动实现 ==========
// 只渲染可见区域的元素,大幅提升大列表性能
class VirtualScroller {
constructor(options) {
this.container = options.container;
this.items = options.items;
this.itemHeight = options.itemHeight;
this.renderItem = options.renderItem;
this.visibleCount = Math.ceil(this.container.clientHeight / this.itemHeight);
this.startIndex = 0;
this.endIndex = this.visibleCount;
this.init();
}
init() {
// 创建容器
this.scrollContainer = document.createElement('div');
this.scrollContainer.style.height = `${this.items.length * this.itemHeight}px`;
this.scrollContainer.style.position = 'relative';
this.contentContainer = document.createElement('div');
this.contentContainer.style.position = 'absolute';
this.contentContainer.style.top = '0';
this.contentContainer.style.left = '0';
this.contentContainer.style.right = '0';
this.scrollContainer.appendChild(this.contentContainer);
this.container.appendChild(this.scrollContainer);
// 监听滚动
this.container.addEventListener('scroll', () => this.handleScroll());
// 初始渲染
this.render();
}
handleScroll() {
const scrollTop = this.container.scrollTop;
this.startIndex = Math.floor(scrollTop / this.itemHeight);
this.endIndex = this.startIndex + this.visibleCount + 1;
this.render();
}
render() {
const visibleItems = this.items.slice(this.startIndex, this.endIndex);
// 清空内容
this.contentContainer.innerHTML = '';
// 设置偏移
this.contentContainer.style.transform = `translateY(${this.startIndex * this.itemHeight}px)`;
// 渲染可见项
visibleItems.forEach((item, index) => {
const element = this.renderItem(item, this.startIndex + index);
element.style.height = `${this.itemHeight}px`;
this.contentContainer.appendChild(element);
});
}
}
// 使用示例
const container = document.getElementById('scroll-container');
const items = Array.from({ length: 100000 }, (_, i) => ({
id: i,
text: `Item ${i}`
}));
new VirtualScroller({
container: container,
items: items,
itemHeight: 50,
renderItem: (item, index) => {
const div = document.createElement('div');
div.className = 'list-item';
div.textContent = item.text;
div.style.borderBottom = '1px solid #ccc';
div.style.padding = '15px';
return div;
}
});
4.4 离屏渲染
// ========== 离屏渲染技术 ==========
// 使用requestAnimationFrame优化动画
class AnimationOptimizer {
constructor() {
this.animations = [];
this.isRunning = false;
}
add(fn) {
this.animations.push(fn);
if (!this.isRunning) {
this.start();
}
}
start() {
this.isRunning = true;
this.tick();
}
tick() {
// 批量执行所有动画
this.animations.forEach(fn => fn());
if (this.animations.length > 0) {
requestAnimationFrame(() => this.tick());
} else {
this.isRunning = false;
}
}
remove(fn) {
const index = this.animations.indexOf(fn);
if (index > -1) {
this.animations.splice(index, 1);
}
}
}
// 使用示例
const animator = new AnimationOptimizer();
const box = document.getElementById('box');
let position = 0;
const animate = () => {
position += 1;
box.style.transform = `translateX(${position}px)`;
if (position < 500) {
animator.add(animate);
}
};
animator.add(animate);
// ========== 使用Web Worker进行离屏计算 ==========
// worker.js
self.addEventListener('message', (e) => {
const { data } = e;
// 执行复杂计算
const result = heavyCalculation(data);
// 返回结果
self.postMessage(result);
});
function heavyCalculation(data) {
// 复杂计算逻辑
let sum = 0;
for (let i = 0; i < data.length; i++) {
sum += Math.sqrt(data[i]) * Math.sin(data[i]);
}
return sum;
}
// main.js
const worker = new Worker('worker.js');
worker.addEventListener('message', (e) => {
console.log('计算结果:', e.data);
// 更新UI
});
// 发送数据到Worker
const largeDataset = Array.from({ length: 1000000 }, () => Math.random());
worker.postMessage(largeDataset);
第四章:事件处理优化
5.1 事件委托
// ========== 事件委托优化 ==========
// ❌ 为每个元素绑定事件
function inefficientEventBinding() {
const buttons = document.querySelectorAll('.button');
buttons.forEach(button => {
button.addEventListener('click', function() {
console.log('Button clicked:', this.textContent);
});
});
}
// 问题:1000个按钮 = 1000个事件监听器
// ✅ 使用事件委托
function efficientEventDelegation() {
const container = document.getElementById('button-container');
container.addEventListener('click', function(e) {
if (e.target.classList.contains('button')) {
console.log('Button clicked:', e.target.textContent);
}
});
}
// 优点:只有1个事件监听器,无论多少按钮
// ========== 高级事件委托 ==========
class EventDelegator {
constructor(container) {
this.container = container;
this.handlers = new Map();
}
on(selector, eventType, handler) {
if (!this.handlers.has(eventType)) {
this.handlers.set(eventType, []);
this.container.addEventListener(eventType, (e) => {
this.handleEvent(e, eventType);
});
}
this.handlers.get(eventType).push({ selector, handler });
}
handleEvent(e, eventType) {
const handlers = this.handlers.get(eventType);
handlers.forEach(({ selector, handler }) => {
if (e.target.matches(selector)) {
handler.call(e.target, e);
}
});
}
off(selector, eventType) {
if (this.handlers.has(eventType)) {
const handlers = this.handlers.get(eventType);
const index = handlers.findIndex(h => h.selector === selector);
if (index > -1) {
handlers.splice(index, 1);
}
}
}
}
// 使用示例
const delegator = new EventDelegator(document.body);
delegator.on('.button', 'click', function(e) {
console.log('Button clicked:', this.textContent);
});
delegator.on('.link', 'click', function(e) {
e.preventDefault();
console.log('Link clicked:', this.href);
});
delegator.on('.input', 'input', function(e) {
console.log('Input changed:', this.value);
});
5.2 被动事件监听器
// ========== 被动事件监听器 ==========
// 提升滚动性能
// ❌ 默认事件监听器(可能阻塞滚动)
document.addEventListener('touchstart', function(e) {
// 处理触摸事件
console.log('Touch started');
}, false);
// ✅ 被动事件监听器(不阻塞滚动)
document.addEventListener('touchstart', function(e) {
// 处理触摸事件
console.log('Touch started');
// 注意:不能调用 e.preventDefault()
}, { passive: true });
// ========== 智能事件监听器管理 ==========
class SmartEventManager {
constructor() {
this.listeners = new Map();
}
// 添加事件监听器(自动优化)
add(element, eventType, handler, options = {}) {
const key = `${element}-${eventType}`;
// 自动判断是否应该使用passive
const shouldBePassive = this.shouldUsePassive(eventType);
const finalOptions = {
...options,
passive: shouldBePassive
};
element.addEventListener(eventType, handler, finalOptions);
this.listeners.set(key, { element, eventType, handler, options: finalOptions });
}
// 移除事件监听器
remove(element, eventType) {
const key = `${element}-${eventType}`;
const listener = this.listeners.get(key);
if (listener) {
listener.element.removeEventListener(
listener.eventType,
listener.handler,
listener.options
);
this.listeners.delete(key);
}
}
// 判断是否应该使用passive
shouldUsePassive(eventType) {
const passiveEvents = [
'scroll',
'wheel',
'touchstart',
'touchmove',
'touchend',
'mousewheel'
];
return passiveEvents.includes(eventType);
}
// 移除所有监听器
removeAll() {
this.listeners.forEach(listener => {
listener.element.removeEventListener(
listener.eventType,
listener.handler,
listener.options
);
});
this.listeners.clear();
}
}
// 使用示例
const eventManager = new SmartEventManager();
eventManager.add(window, 'scroll', function() {
console.log('Scrolling...');
});
// 自动使用 { passive: true }
eventManager.add(document, 'click', function(e) {
console.log('Clicked');
});
// 自动使用 { passive: false }(因为可能需要preventDefault)
5.3 事件节流与防抖应用
// ========== 实际应用场景 ==========
// 场景1:搜索框自动完成
class SearchAutocomplete {
constructor(input, searchFn) {
this.input = input;
this.searchFn = searchFn;
// 使用防抖,避免频繁请求
this.debouncedSearch = debounce(this.search.bind(this), 300);
this.input.addEventListener('input', this.debouncedSearch);
}
search(e) {
const query = e.target.value;
if (query.length >= 2) {
this.searchFn(query);
}
}
}
// 使用示例
const searchInput = document.getElementById('search');
new SearchAutocomplete(searchInput, (query) => {
console.log('搜索:', query);
// 发送API请求
fetch(`/api/search?q=${query}`)
.then(res => res.json())
.then(data => {
// 显示搜索结果
});
});
// 场景2:无限滚动加载
class InfiniteScroll {
constructor(container, loadMoreFn) {
this.container = container;
this.loadMoreFn = loadMoreFn;
this.loading = false;
// 使用节流,避免频繁检查
this.throttledCheck = throttle(this.checkScroll.bind(this), 200);
this.container.addEventListener('scroll', this.throttledCheck);
}
checkScroll() {
if (this.loading) return;
const { scrollTop, scrollHeight, clientHeight } = this.container;
// 距离底部100px时加载更多
if (scrollTop + clientHeight >= scrollHeight - 100) {
this.loading = true;
this.loadMoreFn().then(() => {
this.loading = false;
});
}
}
}
// 使用示例
const scrollContainer = document.getElementById('scroll-container');
new InfiniteScroll(scrollContainer, async () => {
console.log('加载更多...');
// 加载更多数据
const response = await fetch('/api/more-data');
const data = await response.json();
// 添加到容器
data.forEach(item => {
const div = document.createElement('div');
div.textContent = item.title;
scrollContainer.appendChild(div);
});
});
// 场景3:窗口resize处理
class ResponsiveHandler {
constructor() {
this.handlers = [];
// 使用防抖,resize结束后才执行
this.debouncedResize = debounce(this.handleResize.bind(this), 250);
window.addEventListener('resize', this.debouncedResize);
}
addHandler(handler) {
this.handlers.push(handler);
}
handleResize() {
const width = window.innerWidth;
const height = window.innerHeight;
this.handlers.forEach(handler => {
handler(width, height);
});
}
}
// 使用示例
const responsiveHandler = new ResponsiveHandler();
responsiveHandler.addHandler((width, height) => {
console.log('窗口大小:', width, 'x', height);
// 调整布局
});
responsiveHandler.addHandler((width, height) => {
// 根据宽度加载不同的资源
if (width < 768) {
console.log('移动端视图');
} else {
console.log('桌面端视图');
}
});
第五章:内存管理优化
6.1 避免内存泄漏
// ========== 常见内存泄漏场景与解决方案 ==========
// 场景1:未清理的事件监听器
// ❌ 内存泄漏
class LeakyComponent {
constructor(element) {
this.element = element;
this.handleClick = this.onClick.bind(this);
this.element.addEventListener('click', this.handleClick);
}
onClick() {
console.log('Clicked');
}
// 没有清理事件监听器!
}
// ✅ 正确做法
class ProperComponent {
constructor(element) {
this.element = element;
this.handleClick = this.onClick.bind(this);
this.element.addEventListener('click', this.handleClick);
}
onClick() {
console.log('Clicked');
}
destroy() {
this.element.removeEventListener('click', this.handleClick);
this.element = null;
this.handleClick = null;
}
}
// 场景2:定时器未清理
// ❌ 内存泄漏
class LeakyTimer {
constructor() {
this.data = new Array(1000000).fill('data');
this.intervalId = setInterval(() => {
console.log('Timer running');
}, 1000);
}
}
// ✅ 正确做法
class ProperTimer {
constructor() {
this.data = new Array(1000000).fill('data');
this.intervalId = setInterval(() => {
console.log('Timer running');
}, 1000);
}
destroy() {
clearInterval(this.intervalId);
this.data = null;
}
}
// 场景3:闭包引用
// ❌ 内存泄漏
function createLeakyClosure() {
const largeData = new Array(1000000).fill('data');
return function() {
// 即使不使用largeData,它也会被保留在内存中
console.log('Closure function');
};
}
// ✅ 正确做法
function createProperClosure() {
const largeData = new Array(1000000).fill('data');
// 只保留需要的数据
const summary = largeData.length;
return function() {
console.log('Data length:', summary);
};
}
// 场景4:DOM引用
// ❌ 内存泄漏
class LeakyDOMReference {
constructor() {
this.elements = [];
const container = document.getElementById('container');
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
container.appendChild(div);
this.elements.push(div); // 保留了DOM引用
}
}
clear() {
const container = document.getElementById('container');
container.innerHTML = ''; // DOM被清除,但this.elements仍然引用
}
}
// ✅ 正确做法
class ProperDOMReference {
constructor() {
this.elements = [];
const container = document.getElementById('container');
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
container.appendChild(div);
this.elements.push(div);
}
}
clear() {
const container = document.getElementById('container');
container.innerHTML = '';
this.elements = []; // 清除引用
}
}
// ========== 内存泄漏检测工具 ==========
class MemoryLeakDetector {
constructor() {
this.snapshots = [];
}
// 拍摄内存快照
takeSnapshot(label) {
if (performance.memory) {
this.snapshots.push({
label,
timestamp: Date.now(),
usedJSHeapSize: performance.memory.usedJSHeapSize,
totalJSHeapSize: performance.memory.totalJSHeapSize,
jsHeapSizeLimit: performance.memory.jsHeapSizeLimit
});
}
}
// 分析内存增长
analyze() {
if (this.snapshots.length < 2) {
console.log('需要至少2个快照');
return;
}
console.log('📊 内存分析报告');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
for (let i = 1; i < this.snapshots.length; i++) {
const prev = this.snapshots[i - 1];
const curr = this.snapshots[i];
const growth = curr.usedJSHeapSize - prev.usedJSHeapSize;
const growthMB = (growth / 1024 / 1024).toFixed(2);
console.log(`${prev.label} -> ${curr.label}:`);
console.log(` 内存增长: ${growthMB} MB`);
console.log(` 当前使用: ${(curr.usedJSHeapSize / 1024 / 1024).toFixed(2)} MB`);
}
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
}
// 清除快照
clear() {
this.snapshots = [];
}
}
// 使用示例
const detector = new MemoryLeakDetector();
detector.takeSnapshot('初始状态');
// 执行一些操作
for (let i = 0; i < 10000; i++) {
const div = document.createElement('div');
document.body.appendChild(div);
}
detector.takeSnapshot('创建10000个div后');
// 清理
document.body.innerHTML = '';
detector.takeSnapshot('清理后');
detector.analyze();
6.2 对象池技术
// ========== 对象池实现 ==========
// 复用对象,减少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() {
if (this.pool.length > 0) {
return this.pool.pop();
}
return this.createFn();
}
// 归还对象
release(obj) {
this.resetFn(obj);
this.pool.push(obj);
}
// 获取池大小
size() {
return this.pool.length;
}
}
// 使用示例:粒子系统
class Particle {
constructor() {
this.x = 0;
this.y = 0;
this.vx = 0;
this.vy = 0;
this.life = 0;
}
update() {
this.x += this.vx;
this.y += this.vy;
this.life--;
}
isAlive() {
return this.life > 0;
}
}
// 创建粒子池
const particlePool = new ObjectPool(
() => new Particle(),
(particle) => {
particle.x = 0;
particle.y = 0;
particle.vx = 0;
particle.vy = 0;
particle.life = 0;
},
100
);
// 粒子系统
class ParticleSystem {
constructor() {
this.particles = [];
}
emit(x, y) {
const particle = particlePool.acquire();
particle.x = x;
particle.y = y;
particle.vx = (Math.random() - 0.5) * 5;
particle.vy = (Math.random() - 0.5) * 5;
particle.life = 100;
this.particles.push(particle);
}
update() {
for (let i = this.particles.length - 1; i >= 0; i--) {
const particle = this.particles[i];
particle.update();
if (!particle.isAlive()) {
this.particles.splice(i, 1);
particlePool.release(particle); // 归还到对象池
}
}
}
}
// 性能对比
console.time('不使用对象池');
for (let i = 0; i < 10000; i++) {
const p = new Particle();
// 使用后直接丢弃,等待GC
}
console.timeEnd('不使用对象池');
// 输出: 不使用对象池: ~5ms
console.time('使用对象池');
for (let i = 0; i < 10000; i++) {
const p = particlePool.acquire();
particlePool.release(p);
}
console.timeEnd('使用对象池');
// 输出: 使用对象池: ~1ms
6.3 WeakMap和WeakSet
// ========== WeakMap使用场景 ==========
// 场景1:私有数据存储
const privateData = new WeakMap();
class User {
constructor(name, password) {
this.name = name;
privateData.set(this, { password });
}
checkPassword(password) {
return privateData.get(this).password === password;
}
}
const user = new User('Alice', 'secret123');
console.log(user.name); // 'Alice'
console.log(user.password); // undefined(无法访问)
console.log(user.checkPassword('secret123')); // true
// 场景2:DOM元素关联数据
const elementData = new WeakMap();
function attachData(element, data) {
elementData.set(element, data);
}
function getData(element) {
return elementData.get(element);
}
const div = document.createElement('div');
attachData(div, { id: 1, name: 'Test' });
console.log(getData(div)); // { id: 1, name: 'Test' }
// 当div被移除时,关联的数据会自动被垃圾回收
// ========== WeakSet使用场景 ==========
// 场景:标记对象
const processedObjects = new WeakSet();
function processObject(obj) {
if (processedObjects.has(obj)) {
console.log('对象已处理过');
return;
}
// 处理对象
console.log('处理对象:', obj);
processedObjects.add(obj);
}
const obj1 = { id: 1 };
const obj2 = { id: 2 };
processObject(obj1); // 处理对象: { id: 1 }
processObject(obj1); // 对象已处理过
processObject(obj2); // 处理对象: { id: 2 }
// ========== Map vs WeakMap 对比 ==========
// Map - 会阻止垃圾回收
const regularMap = new Map();
let obj = { data: 'large data' };
regularMap.set(obj, 'value');
obj = null; // obj仍然在Map中,不会被回收
// WeakMap - 不会阻止垃圾回收
const weakMap = new WeakMap();
let obj2 = { data: 'large data' };
weakMap.set(obj2, 'value');
obj2 = null; // obj2会被垃圾回收,WeakMap中的条目也会被清除
第六章:网络请求优化
7.1 请求合并与批处理
// ========== 请求批处理器 ==========
class RequestBatcher {
constructor(batchFn, delay = 50) {
this.batchFn = batchFn;
this.delay = delay;
this.queue = [];
this.timeoutId = null;
}
add(request) {
return new Promise((resolve, reject) => {
this.queue.push({ request, resolve, reject });
if (this.timeoutId) {
clearTimeout(this.timeoutId);
}
this.timeoutId = setTimeout(() => {
this.flush();
}, this.delay);
});
}
async flush() {
if (this.queue.length === 0) return;
const batch = this.queue.splice(0);
const requests = batch.map(item => item.request);
try {
const results = await this.batchFn(requests);
batch.forEach((item, index) => {
item.resolve(results[index]);
});
} catch (error) {
batch.forEach(item => {
item.reject(error);
});
}
}
}
// 使用示例:批量获取用户信息
const userBatcher = new RequestBatcher(async (userIds) => {
console.log('批量请求用户信息:', userIds);
const response = await fetch('/api/users/batch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ids: userIds })
});
return response.json();
}, 50);
// 多个组件同时请求用户信息
async function loadUserProfile(userId) {
const user = await userBatcher.add(userId);
console.log('用户信息:', user);
}
// 这些请求会被合并成一个批量请求
loadUserProfile(1);
loadUserProfile(2);
loadUserProfile(3);
loadUserProfile(4);
loadUserProfile(5);
// 实际只发送一次请求:POST /api/users/batch { ids: [1,2,3,4,5] }
7.2 请求缓存
// ========== HTTP缓存管理器 ==========
class CacheManager {
constructor() {
this.cache = new Map();
this.pendingRequests = new Map();
}
// 生成缓存键
getCacheKey(url, options = {}) {
return `${url}:${JSON.stringify(options)}`;
}
// 获取缓存
get(url, options = {}) {
const key = this.getCacheKey(url, options);
const cached = this.cache.get(key);
if (cached && Date.now() < cached.expiry) {
console.log('从缓存返回:', url);
return Promise.resolve(cached.data);
}
return null;
}
// 设置缓存
set(url, data, ttl = 60000, options = {}) {
const key = this.getCacheKey(url, options);
this.cache.set(key, {
data,
expiry: Date.now() + ttl
});
}
// 清除缓存
clear(url, options = {}) {
if (url) {
const key = this.getCacheKey(url, options);
this.cache.delete(key);
} else {
this.cache.clear();
}
}
// 带缓存的fetch
async fetch(url, options = {}, ttl = 60000) {
// 检查缓存
const cached = this.get(url, options);
if (cached) {
return cached;
}
// 检查是否有相同的请求正在进行
const key = this.getCacheKey(url, options);
if (this.pendingRequests.has(key)) {
console.log('等待进行中的请求:', url);
return this.pendingRequests.get(key);
}
// 发起新请求
console.log('发起新请求:', url);
const promise = fetch(url, options)
.then(response => response.json())
.then(data => {
this.set(url, data, ttl, options);
this.pendingRequests.delete(key);
return data;
})
.catch(error => {
this.pendingRequests.delete(key);
throw error;
});
this.pendingRequests.set(key, promise);
return promise;
}
}
// 使用示例
const cacheManager = new CacheManager();
// 第一次请求 - 发起网络请求
cacheManager.fetch('/api/users/1', {}, 60000)
.then(data => console.log('数据1:', data));
// 第二次请求(立即) - 等待第一次请求
cacheManager.fetch('/api/users/1', {}, 60000)
.then(data => console.log('数据2:', data));
// 第三次请求(1秒后) - 从缓存返回
setTimeout(() => {
cacheManager.fetch('/api/users/1', {}, 60000)
.then(data => console.log('数据3:', data));
}, 1000);
// ========== Service Worker缓存 ==========
// sw.js
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open('v1').then((cache) => {
return cache.addAll([
'/',
'/styles/main.css',
'/scripts/main.js',
'/images/logo.png'
]);
})
);
});
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('v1').then((cache) => {
cache.put(event.request, responseToCache);
});
return response;
});
})
);
});
7.3 预加载与懒加载
// ========== 资源预加载 ==========
// 1. DNS预解析
function prefetchDNS(domains) {
domains.forEach(domain => {
const link = document.createElement('link');
link.rel = 'dns-prefetch';
link.href = `//${domain}`;
document.head.appendChild(link);
});
}
prefetchDNS(['api.example.com', 'cdn.example.com']);
// 2. 预连接
function preconnect(urls) {
urls.forEach(url => {
const link = document.createElement('link');
link.rel = 'preconnect';
link.href = url;
document.head.appendChild(link);
});
}
preconnect(['https://api.example.com', 'https://cdn.example.com']);
// 3. 预加载资源
function preloadResource(url, as) {
const link = document.createElement('link');
link.rel = 'preload';
link.href = url;
link.as = as;
document.head.appendChild(link);
}
preloadResource('/fonts/main.woff2', 'font');
preloadResource('/styles/critical.css', 'style');
preloadResource('/scripts/main.js', 'script');
// 4. 预获取资源(低优先级)
function prefetchResource(url) {
const link = document.createElement('link');
link.rel = 'prefetch';
link.href = url;
document.head.appendChild(link);
}
prefetchResource('/next-page.html');
prefetchResource('/images/next-page-hero.jpg');
// ========== 图片懒加载 ==========
class LazyImageLoader {
constructor(options = {}) {
this.options = {
root: null,
rootMargin: '50px',
threshold: 0.01,
...options
};
this.observer = new IntersectionObserver(
this.handleIntersection.bind(this),
this.options
);
this.loadImage = this.loadImage.bind(this);
}
observe(images) {
images.forEach(img => {
this.observer.observe(img);
});
}
handleIntersection(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.loadImage(entry.target);
this.observer.unobserve(entry.target);
}
});
}
loadImage(img) {
const src = img.dataset.src;
const srcset = img.dataset.srcset;
if (!src) return;
// 创建新图片对象预加载
const tempImg = new Image();
tempImg.onload = () => {
img.src = src;
if (srcset) {
img.srcset = srcset;
}
img.classList.add('loaded');
};
tempImg.onerror = () => {
console.error('图片加载失败:', src);
img.classList.add('error');
};
tempImg.src = src;
}
disconnect() {
this.observer.disconnect();
}
}
// 使用示例
const lazyLoader = new LazyImageLoader({
rootMargin: '100px' // 提前100px开始加载
});
const lazyImages = document.querySelectorAll('img[data-src]');
lazyLoader.observe(lazyImages);
// HTML示例:
// <img data-src="image.jpg" data-srcset="image-2x.jpg 2x" alt="Lazy loaded image">
// ========== 路由懒加载 ==========
class RouteLoader {
constructor() {
this.routes = new Map();
this.cache = new Map();
}
// 注册路由
register(path, loader) {
this.routes.set(path, loader);
}
// 加载路由
async load(path) {
// 检查缓存
if (this.cache.has(path)) {
console.log('从缓存加载路由:', path);
return this.cache.get(path);
}
// 获取加载器
const loader = this.routes.get(path);
if (!loader) {
throw new Error(`路由未找到: ${path}`);
}
// 加载模块
console.log('加载路由:', path);
const module = await loader();
// 缓存模块
this.cache.set(path, module);
return module;
}
// 预加载路由
async prefetch(path) {
if (!this.cache.has(path)) {
await this.load(path);
}
}
}
// 使用示例
const routeLoader = new RouteLoader();
// 注册路由
routeLoader.register('/home', () => import('./pages/Home.js'));
routeLoader.register('/about', () => import('./pages/About.js'));
routeLoader.register('/contact', () => import('./pages/Contact.js'));
// 加载路由
async function navigateTo(path) {
const module = await routeLoader.load(path);
module.render();
}
// 预加载可能访问的路由
routeLoader.prefetch('/about');
// ========== 组件懒加载(React示例)==========
import React, { lazy, Suspense } from 'react';
// 懒加载组件
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
</div>
);
}
第七章:渲染性能优化
8.1 重排与重绘优化
// ========== 避免强制同步布局 ==========
// ❌ 强制同步布局(Layout Thrashing)
function inefficientLayout() {
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => {
// 读取布局属性
const width = box.offsetWidth;
// 立即修改样式
box.style.width = width + 10 + 'px';
// 浏览器被迫重新计算布局
});
}
// ✅ 批量读取,批量写入
function efficientLayout() {
const boxes = document.querySelectorAll('.box');
// 第一步:批量读取
const widths = Array.from(boxes).map(box => box.offsetWidth);
// 第二步:批量写入
boxes.forEach((box, index) => {
box.style.width = widths[index] + 10 + 'px';
});
}
// ========== 使用requestAnimationFrame ==========
class LayoutOptimizer {
constructor() {
this.readQueue = [];
this.writeQueue = [];
this.scheduled = false;
}
// 添加读操作
read(fn) {
this.readQueue.push(fn);
this.schedule();
}
// 添加写操作
write(fn) {
this.writeQueue.push(fn);
this.schedule();
}
// 调度执行
schedule() {
if (this.scheduled) return;
this.scheduled = true;
requestAnimationFrame(() => {
this.flush();
});
}
// 执行队列
flush() {
// 先执行所有读操作
this.readQueue.forEach(fn => fn());
this.readQueue = [];
// 再执行所有写操作
this.writeQueue.forEach(fn => fn());
this.writeQueue = [];
this.scheduled = false;
}
}
// 使用示例
const optimizer = new LayoutOptimizer();
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => {
let width;
// 添加读操作
optimizer.read(() => {
width = box.offsetWidth;
});
// 添加写操作
optimizer.write(() => {
box.style.width = width + 10 + 'px';
});
});
// ========== CSS优化 ==========
// 1. 使用transform代替top/left
// ❌ 触发重排
element.style.left = '100px';
element.style.top = '100px';
// ✅ 只触发合成
element.style.transform = 'translate(100px, 100px)';
// 2. 使用opacity代替visibility
// ❌ 触发重绘
element.style.visibility = 'hidden';
// ✅ 只触发合成
element.style.opacity = '0';
// 3. 使用will-change提示浏览器
element.style.willChange = 'transform, opacity';
// 4. 避免复杂的CSS选择器
// ❌ 复杂选择器
// .container .list .item:nth-child(odd) > .content > .title
// ✅ 简单选择器
// .item-title
// ========== 批量DOM操作 ==========
class DOMBatcher {
constructor() {
this.mutations = [];
this.scheduled = false;
}
// 添加变更
add(fn) {
this.mutations.push(fn);
this.schedule();
}
// 调度执行
schedule() {
if (this.scheduled) return;
this.scheduled = true;
requestAnimationFrame(() => {
this.flush();
});
}
// 执行变更
flush() {
// 使用DocumentFragment批量操作
const fragment = document.createDocumentFragment();
this.mutations.forEach(fn => fn(fragment));
this.mutations = [];
// 一次性应用所有变更
document.body.appendChild(fragment);
this.scheduled = false;
}
}
// 使用示例
const batcher = new DOMBatcher();
for (let i = 0; i < 1000; i++) {
batcher.add((fragment) => {
const div = document.createElement('div');
div.textContent = `Item ${i}`;
fragment.appendChild(div);
});
}
8.2 CSS动画优化
// ========== GPU加速动画 ==========
// ❌ CPU动画(慢)
function cpuAnimation(element) {
let position = 0;
function animate() {
position += 1;
element.style.left = position + 'px'; // 触发重排
if (position < 500) {
requestAnimationFrame(animate);
}
}
animate();
}
// ✅ GPU动画(快)
function gpuAnimation(element) {
let position = 0;
// 启用GPU加速
element.style.transform = 'translateZ(0)';
function animate() {
position += 1;
element.style.transform = `translate3d(${position}px, 0, 0)`; // 只触发合成
if (position < 500) {
requestAnimationFrame(animate);
}
}
animate();
}
// ========== Web Animations API ==========
class AnimationController {
constructor(element) {
this.element = element;
this.animations = [];
}
// 创建动画
animate(keyframes, options) {
const animation = this.element.animate(keyframes, options);
this.animations.push(animation);
return animation;
}
// 淡入动画
fadeIn(duration = 300) {
return this.animate(
[
{ opacity: 0 },
{ opacity: 1 }
],
{
duration,
easing: 'ease-in-out',
fill: 'forwards'
}
);
}
// 滑入动画
slideIn(direction = 'left', duration = 300) {
const transforms = {
left: ['translateX(-100%)', 'translateX(0)'],
right: ['translateX(100%)', 'translateX(0)'],
top: ['translateY(-100%)', 'translateY(0)'],
bottom: ['translateY(100%)', 'translateY(0)']
};
return this.animate(
[
{ transform: transforms[direction][0], opacity: 0 },
{ transform: transforms[direction][1], opacity: 1 }
],
{
duration,
easing: 'ease-out',
fill: 'forwards'
}
);
}
// 缩放动画
scale(from = 0, to = 1, duration = 300) {
return this.animate(
[
{ transform: `scale(${from})`, opacity: 0 },
{ transform: `scale(${to})`, opacity: 1 }
],
{
duration,
easing: 'ease-out',
fill: 'forwards'
}
);
}
// 暂停所有动画
pauseAll() {
this.animations.forEach(anim => anim.pause());
}
// 播放所有动画
playAll() {
this.animations.forEach(anim => anim.play());
}
// 取消所有动画
cancelAll() {
this.animations.forEach(anim => anim.cancel());
this.animations = [];
}
}
// 使用示例
const element = document.getElementById('animated-element');
const controller = new AnimationController(element);
// 淡入
controller.fadeIn(500);
// 滑入
controller.slideIn('left', 500);
// 缩放
controller.scale(0.5, 1, 500);
// ========== CSS动画性能监控 ==========
class AnimationPerformanceMonitor {
constructor() {
this.metrics = [];
}
// 监控动画性能
monitor(animationName, callback) {
const startTime = performance.now();
let frameCount = 0;
let lastTime = startTime;
const element = document.querySelector(`[data-animation="${animationName}"]`);
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if (entry.name === animationName) {
frameCount++;
const currentTime = performance.now();
const fps = 1000 / (currentTime - lastTime);
lastTime = currentTime;
this.metrics.push({
time: currentTime - startTime,
fps: fps
});
}
});
});
observer.observe({ entryTypes: ['measure'] });
element.addEventListener('animationend', () => {
observer.disconnect();
const duration = performance.now() - startTime;
const avgFPS = (frameCount / duration) * 1000;
callback({
duration,
frameCount,
avgFPS,
metrics: this.metrics
});
});
}
// 生成报告
generateReport() {
const avgFPS = this.metrics.reduce((sum, m) => sum + m.fps, 0) / this.metrics.length;
const minFPS = Math.min(...this.metrics.map(m => m.fps));
const maxFPS = Math.max(...this.metrics.map(m => m.fps));
return {
avgFPS: avgFPS.toFixed(2),
minFPS: minFPS.toFixed(2),
maxFPS: maxFPS.toFixed(2),
frameDrops: this.metrics.filter(m => m.fps < 30).length
};
}
}
// 使用示例
const monitor = new AnimationPerformanceMonitor();
monitor.monitor('slide-in', (result) => {
console.log('动画性能报告:');
console.log(`持续时间: ${result.duration.toFixed(2)}ms`);
console.log(`帧数: ${result.frameCount}`);
console.log(`平均FPS: ${result.avgFPS.toFixed(2)}`);
const report = monitor.generateReport();
console.log('详细报告:', report);
});
8.3 Canvas优化
// ========== Canvas性能优化技巧 ==========
// 1. 离屏Canvas
class OffscreenCanvasRenderer {
constructor(width, height) {
// 主Canvas
this.canvas = document.getElementById('main-canvas');
this.ctx = this.canvas.getContext('2d');
// 离屏Canvas
this.offscreenCanvas = document.createElement('canvas');
this.offscreenCanvas.width = width;
this.offscreenCanvas.height = height;
this.offscreenCtx = this.offscreenCanvas.getContext('2d');
}
// 在离屏Canvas上绘制
drawOffscreen() {
this.offscreenCtx.clearRect(0, 0, this.offscreenCanvas.width, this.offscreenCanvas.height);
// 复杂的绘制操作
for (let i = 0; i < 1000; i++) {
this.offscreenCtx.fillStyle = `hsl(${i % 360}, 50%, 50%)`;
this.offscreenCtx.fillRect(
Math.random() * this.offscreenCanvas.width,
Math.random() * this.offscreenCanvas.height,
10, 10
);
}
}
// 将离屏Canvas绘制到主Canvas
render() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.drawImage(this.offscreenCanvas, 0, 0);
}
}
// 2. 分层Canvas
class LayeredCanvasRenderer {
constructor() {
this.layers = {
background: this.createLayer('background'),
objects: this.createLayer('objects'),
ui: this.createLayer('ui')
};
}
createLayer(name) {
const canvas = document.createElement('canvas');
canvas.width = 800;
canvas.height = 600;
canvas.style.position = 'absolute';
canvas.style.left = '0';
canvas.style.top = '0';
canvas.id = `layer-${name}`;
document.getElementById('canvas-container').appendChild(canvas);
return {
canvas,
ctx: canvas.getContext('2d'),
dirty: true
};
}
// 只重绘变化的层
render() {
Object.values(this.layers).forEach(layer => {
if (layer.dirty) {
layer.ctx.clearRect(0, 0, layer.canvas.width, layer.canvas.height);
// 绘制该层内容
layer.dirty = false;
}
});
}
// 标记层为脏
markDirty(layerName) {
if (this.layers[layerName]) {
this.layers[layerName].dirty = true;
}
}
}
// 3. 批量绘制
class BatchRenderer {
constructor(ctx) {
this.ctx = ctx;
this.drawCalls = [];
}
// 添加绘制调用
add(type, ...args) {
this.drawCalls.push({ type, args });
}
// 批量执行
flush() {
this.ctx.save();
// 按类型分组
const groups = this.groupByType();
// 批量绘制相同类型
Object.entries(groups).forEach(([type, calls]) => {
this.ctx.beginPath();
calls.forEach(call => {
switch (type) {
case 'rect':
this.ctx.rect(...call.args);
break;
case 'circle':
const [x, y, r] = call.args;
this.ctx.arc(x, y, r, 0, Math.PI * 2);
break;
}
});
this.ctx.fill();
});
this.ctx.restore();
this.drawCalls = [];
}
groupByType() {
return this.drawCalls.reduce((groups, call) => {
if (!groups[call.type]) {
groups[call.type] = [];
}
groups[call.type].push(call);
return groups;
}, {});
}
}
// 使用示例
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const batchRenderer = new BatchRenderer(ctx);
// 添加大量绘制调用
for (let i = 0; i < 1000; i++) {
batchRenderer.add('rect', i * 10, 0, 8, 8);
}
// 批量绘制
batchRenderer.flush();
// 4. 使用WebGL(通过Three.js)
class WebGLRenderer {
constructor(container) {
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(this.renderer.domElement);
this.objects = [];
}
addObject(geometry, material) {
const mesh = new THREE.Mesh(geometry, material);
this.scene.add(mesh);
this.objects.push(mesh);
return mesh;
}
animate() {
requestAnimationFrame(() => this.animate());
// 更新对象
this.objects.forEach(obj => {
obj.rotation.x += 0.01;
obj.rotation.y += 0.01;
});
this.renderer.render(this.scene, this.camera);
}
}
第八章:打包与加载优化
8.1 代码分割
// ========== Webpack代码分割配置 ==========
// webpack.config.js
module.exports = {
entry: {
main: './src/index.js',
vendor: './src/vendor.js'
},
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
chunkFilename: '[name].[contenthash].chunk.js'
},
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
// 第三方库单独打包
vendor: {
test: /[\/]node_modules[\/]/,
name: 'vendors',
priority: 10
},
// 公共代码单独打包
common: {
minChunks: 2,
priority: 5,
reuseExistingChunk: true
},
// React相关单独打包
react: {
test: /[\/]node_modules[\/](react|react-dom)[\/]/,
name: 'react',
priority: 20
}
}
},
// 运行时代码单独提取
runtimeChunk: {
name: 'runtime'
}
}
};
// ========== 动态导入(Dynamic Import)==========
// 1. 路由级别的代码分割
class Router {
constructor() {
this.routes = new Map();
this.currentRoute = null;
}
// 注册路由(使用动态导入)
register(path, loader) {
this.routes.set(path, loader);
}
// 导航到路由
async navigate(path) {
const loader = this.routes.get(path);
if (!loader) {
console.error('路由不存在:', path);
return;
}
// 显示加载状态
this.showLoading();
try {
// 动态导入模块
const module = await loader();
// 卸载当前路由
if (this.currentRoute && this.currentRoute.unmount) {
this.currentRoute.unmount();
}
// 挂载新路由
this.currentRoute = module.default;
this.currentRoute.mount();
// 更新URL
window.history.pushState({ path }, '', path);
} catch (error) {
console.error('路由加载失败:', error);
this.showError();
} finally {
this.hideLoading();
}
}
showLoading() {
document.getElementById('loading').style.display = 'block';
}
hideLoading() {
document.getElementById('loading').style.display = 'none';
}
showError() {
document.getElementById('error').style.display = 'block';
}
}
// 使用示例
const router = new Router();
// 注册路由(每个路由都是独立的chunk)
router.register('/', () => import('./pages/Home'));
router.register('/about', () => import('./pages/About'));
router.register('/products', () => import('./pages/Products'));
router.register('/contact', () => import('./pages/Contact'));
// 导航
document.getElementById('nav-home').addEventListener('click', () => {
router.navigate('/');
});
document.getElementById('nav-about').addEventListener('click', () => {
router.navigate('/about');
});
// 2. 组件级别的代码分割
class ComponentLoader {
constructor() {
this.cache = new Map();
}
// 懒加载组件
async load(componentName, container) {
// 检查缓存
if (this.cache.has(componentName)) {
const Component = this.cache.get(componentName);
return new Component(container);
}
// 动态导入组件
const module = await import(`./components/${componentName}.js`);
const Component = module.default;
// 缓存组件
this.cache.set(componentName, Component);
// 实例化并返回
return new Component(container);
}
// 预加载组件
async preload(componentNames) {
const promises = componentNames.map(name =>
import(`./components/${name}.js`).then(module => {
this.cache.set(name, module.default);
})
);
await Promise.all(promises);
}
}
// 使用示例
const loader = new ComponentLoader();
// 懒加载组件
document.getElementById('load-chart').addEventListener('click', async () => {
const container = document.getElementById('chart-container');
const chart = await loader.load('Chart', container);
chart.render();
});
// 预加载可能需要的组件
loader.preload(['Chart', 'Table', 'Modal']);
// 3. 条件加载
class ConditionalLoader {
// 根据条件动态加载
static async loadIfNeeded(condition, loader) {
if (condition) {
return await loader();
}
return null;
}
// 根据用户权限加载
static async loadByPermission(permission, loader) {
const hasPermission = await this.checkPermission(permission);
if (hasPermission) {
return await loader();
}
return null;
}
// 根据设备类型加载
static async loadByDevice(loader) {
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
if (isMobile) {
return await import('./mobile-version.js');
} else {
return await import('./desktop-version.js');
}
}
// 根据功能支持加载
static async loadByFeatureSupport(feature, loader) {
const isSupported = this.checkFeatureSupport(feature);
if (isSupported) {
return await loader();
} else {
// 加载polyfill
return await import('./polyfills.js');
}
}
static async checkPermission(permission) {
// 模拟权限检查
return fetch('/api/check-permission', {
method: 'POST',
body: JSON.stringify({ permission })
}).then(res => res.json());
}
static checkFeatureSupport(feature) {
const features = {
'webgl': () => {
const canvas = document.createElement('canvas');
return !!(canvas.getContext('webgl') || canvas.getContext('experimental-webgl'));
},
'webworker': () => typeof Worker !== 'undefined',
'serviceworker': () => 'serviceWorker' in navigator,
'intersectionobserver': () => 'IntersectionObserver' in window
};
return features[feature] ? features[feature]() : false;
}
}
// 使用示例
// 仅在需要时加载3D渲染器
const load3DRenderer = async () => {
const renderer = await ConditionalLoader.loadIfNeeded(
document.getElementById('enable-3d').checked,
() => import('./renderers/3D.js')
);
if (renderer) {
renderer.default.init();
}
};
// 根据权限加载管理面板
const loadAdminPanel = async () => {
const panel = await ConditionalLoader.loadByPermission(
'admin',
() => import('./components/AdminPanel.js')
);
if (panel) {
panel.default.render();
}
};
// 根据设备加载不同版本
const loadAppropriateVersion = async () => {
const module = await ConditionalLoader.loadByDevice();
module.default.init();
};
// 根据功能支持加载
const loadWithPolyfill = async () => {
const module = await ConditionalLoader.loadByFeatureSupport(
'intersectionobserver',
() => import('./features/LazyLoad.js')
);
module.default.init();
};
8.2 Tree Shaking
// ========== Tree Shaking优化 ==========
// ❌ 不利于Tree Shaking的写法
// utils.js
export default {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => a / b
};
// main.js
import utils from './utils';
console.log(utils.add(1, 2)); // 整个对象都会被打包
// ✅ 利于Tree Shaking的写法
// utils.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export const multiply = (a, b) => a * b;
export const divide = (a, b) => a / b;
// main.js
import { add } from './utils'; // 只打包add函数
console.log(add(1, 2));
// ========== 副作用标记 ==========
// package.json
{
"name": "my-library",
"version": "1.0.0",
"sideEffects": false // 标记为无副作用,可以安全地Tree Shaking
}
// 或者指定有副作用的文件
{
"sideEffects": [
"*.css",
"*.scss",
"./src/polyfills.js"
]
}
// ========== 优化第三方库导入 ==========
// ❌ 导入整个库
import _ from 'lodash';
const result = _.debounce(fn, 300);
// ✅ 只导入需要的函数
import debounce from 'lodash/debounce';
const result = debounce(fn, 300);
// 或使用lodash-es(ES模块版本)
import { debounce } from 'lodash-es';
const result = debounce(fn, 300);
// ========== Webpack配置优化 ==========
// webpack.config.js
module.exports = {
mode: 'production', // 生产模式自动启用Tree Shaking
optimization: {
usedExports: true, // 标记未使用的导出
minimize: true, // 压缩代码
sideEffects: true // 识别副作用
},
module: {
rules: [
{
test: /.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', {
modules: false // 保留ES模块,不转换为CommonJS
}]
]
}
}
}
]
}
};
// ========== 分析打包结果 ==========
// 使用webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'static',
reportFilename: 'bundle-report.html',
openAnalyzer: false
})
]
};
// 运行后会生成可视化报告,显示每个模块的大小
8.3 资源压缩
// ========== Webpack压缩配置 ==========
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = {
optimization: {
minimize: true,
minimizer: [
// JavaScript压缩
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true, // 删除console
drop_debugger: true, // 删除debugger
pure_funcs: ['console.log'] // 删除特定函数调用
},
format: {
comments: false // 删除注释
}
},
extractComments: false,
parallel: true // 多线程压缩
}),
// CSS压缩
new CssMinimizerPlugin({
minimizerOptions: {
preset: [
'default',
{
discardComments: { removeAll: true },
normalizeWhitespace: true
}
]
}
})
]
},
plugins: [
// 图片压缩
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminMinify,
options: {
plugins: [
['gifsicle', { interlaced: true }],
['jpegtran', { progressive: true }],
['optipng', { optimizationLevel: 5 }],
['svgo', {
plugins: [
{
name: 'removeViewBox',
active: false
}
]
}]
]
}
}
}),
// Gzip压缩
new CompressionPlugin({
algorithm: 'gzip',
test: /.(js|css|html|svg)$/,
threshold: 10240, // 只压缩大于10KB的文件
minRatio: 0.8
}),
// Brotli压缩(更高压缩率)
new CompressionPlugin({
filename: '[path][base].br',
algorithm: 'brotliCompress',
test: /.(js|css|html|svg)$/,
compressionOptions: {
level: 11
},
threshold: 10240,
minRatio: 0.8
})
]
};
// ========== 运行时压缩 ==========
// 服务端配置示例(Express)
const express = require('express');
const compression = require('compression');
const app = express();
// 启用压缩中间件
app.use(compression({
level: 6, // 压缩级别(0-9)
threshold: 1024, // 只压缩大于1KB的响应
filter: (req, res) => {
// 自定义过滤器
if (req.headers['x-no-compression']) {
return false;
}
return compression.filter(req, res);
}
}));
// ========== 图片优化 ==========
class ImageOptimizer {
// 响应式图片
static generateResponsiveImages(src, sizes) {
const srcset = sizes.map(size =>
`${src.replace(/.(jpg|png)$/, `-${size}w.$1`)} ${size}w`
).join(', ');
return {
src,
srcset,
sizes: '(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 33vw'
};
}
// WebP支持检测
static async supportsWebP() {
if (!self.createImageBitmap) return false;
const webpData = 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA=';
const blob = await fetch(webpData).then(r => r.blob());
return createImageBitmap(blob).then(() => true, () => false);
}
// 动态选择图片格式
static async getOptimalImageSrc(baseSrc) {
const supportsWebP = await this.supportsWebP();
if (supportsWebP) {
return baseSrc.replace(/.(jpg|png)$/, '.webp');
}
return baseSrc;
}
}
// 使用示例
const imageData = ImageOptimizer.generateResponsiveImages(
'/images/hero.jpg',
[320, 640, 960, 1280, 1920]
);
const img = document.createElement('img');
img.src = imageData.src;
img.srcset = imageData.srcset;
img.sizes = imageData.sizes;
// ========== 字体优化 ==========
// CSS字体优化
/*
@font-face {
font-family: 'MyFont';
src: url('/fonts/myfont.woff2') format('woff2'),
url('/fonts/myfont.woff') format('woff');
font-display: swap; // 使用系统字体直到自定义字体加载完成
unicode-range: U+0000-00FF; // 只加载需要的字符范围
}
*/
class FontLoader {
// 预加载字体
static preload(fontUrl) {
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'font';
link.type = 'font/woff2';
link.href = fontUrl;
link.crossOrigin = 'anonymous';
document.head.appendChild(link);
}
// 使用Font Loading API
static async load(fontFamily, fontUrl) {
const font = new FontFace(fontFamily, `url(${fontUrl})`);
try {
await font.load();
document.fonts.add(font);
console.log(`字体 ${fontFamily} 加载成功`);
} catch (error) {
console.error(`字体 ${fontFamily} 加载失败:`, error);
}
}
// 字体子集化(只包含需要的字符)
static generateSubset(text) {
// 获取唯一字符
const chars = [...new Set(text)].sort().join('');
// 生成unicode范围
const ranges = [];
let start = chars.charCodeAt(0);
let end = start;
for (let i = 1; i < chars.length; i++) {
const code = chars.charCodeAt(i);
if (code === end + 1) {
end = code;
} else {
ranges.push(start === end ?
`U+${start.toString(16).toUpperCase()}` :
`U+${start.toString(16).toUpperCase()}-${end.toString(16).toUpperCase()}`
);
start = end = code;
}
}
ranges.push(start === end ?
`U+${start.toString(16).toUpperCase()}` :
`U+${start.toString(16).toUpperCase()}-${end.toString(16).toUpperCase()}`
);
return ranges.join(', ');
}
}
// 使用示例
FontLoader.preload('/fonts/myfont.woff2');
FontLoader.load('MyFont', '/fonts/myfont.woff2');
// 生成字体子集
const pageText = document.body.textContent;
const unicodeRange = FontLoader.generateSubset(pageText);
console.log('Unicode范围:', unicodeRange);
8.4 CDN优化
// ========== CDN配置与优化 ==========
class CDNManager {
constructor(config) {
this.config = {
primary: config.primary,
fallbacks: config.fallbacks || [],
timeout: config.timeout || 3000
};
}
// 加载资源(带降级)
async loadResource(path, type = 'script') {
const urls = [this.config.primary + path, ...this.config.fallbacks.map(cdn => cdn + path)];
for (let i = 0; i < urls.length; i++) {
try {
await this.load(urls[i], type);
console.log(`从CDN加载成功: ${urls[i]}`);
return;
} catch (error) {
console.warn(`CDN加载失败: ${urls[i]}`, error);
if (i === urls.length - 1) {
throw new Error('所有CDN都加载失败');
}
}
}
}
// 加载单个资源
load(url, type) {
return new Promise((resolve, reject) => {
const element = type === 'script' ?
document.createElement('script') :
document.createElement('link');
const timeout = setTimeout(() => {
reject(new Error('加载超时'));
}, this.config.timeout);
element.onload = () => {
clearTimeout(timeout);
resolve();
};
element.onerror = () => {
clearTimeout(timeout);
reject(new Error('加载失败'));
};
if (type === 'script') {
element.src = url;
element.async = true;
} else {
element.rel = 'stylesheet';
element.href = url;
}
document.head.appendChild(element);
});
}
// 预连接到CDN
preconnect() {
const domains = [this.config.primary, ...this.config.fallbacks];
domains.forEach(domain => {
const link = document.createElement('link');
link.rel = 'preconnect';
link.href = domain;
document.head.appendChild(link);
});
}
// DNS预解析
dnsPrefetch() {
const domains = [this.config.primary, ...this.config.fallbacks];
domains.forEach(domain => {
const link = document.createElement('link');
link.rel = 'dns-prefetch';
link.href = domain;
document.head.appendChild(link);
});
}
}
// 使用示例
const cdnManager = new CDNManager({
primary: 'https://cdn1.example.com',
fallbacks: [
'https://cdn2.example.com',
'https://cdn3.example.com'
],
timeout: 5000
});
// 预连接
cdnManager.preconnect();
// 加载资源
cdnManager.loadResource('/js/vendor.js', 'script')
.then(() => console.log('资源加载成功'))
.catch(error => console.error('资源加载失败', error));
// ========== 智能CDN选择 ==========
class SmartCDNSelector {
constructor(cdns) {
this.cdns = cdns;
this.performanceData = new Map();
}
// 测试CDN速度
async testCDN(cdn) {
const testUrl = `${cdn}/test.png?t=${Date.now()}`;
const startTime = performance.now();
try {
await fetch(testUrl, { method: 'HEAD' });
const endTime = performance.now();
const latency = endTime - startTime;
this.performanceData.set(cdn, {
latency,
timestamp: Date.now(),
available: true
});
return latency;
} catch (error) {
this.performanceData.set(cdn, {
latency: Infinity,
timestamp: Date.now(),
available: false
});
return Infinity;
}
}
// 选择最快的CDN
async selectFastest() {
const tests = this.cdns.map(cdn => this.testCDN(cdn));
await Promise.all(tests);
let fastest = null;
let minLatency = Infinity;
this.performanceData.forEach((data, cdn) => {
if (data.available && data.latency < minLatency) {
minLatency = data.latency;
fastest = cdn;
}
});
return fastest;
}
// 获取性能报告
getPerformanceReport() {
const report = [];
this.performanceData.forEach((data, cdn) => {
report.push({
cdn,
latency: data.latency,
available: data.available,
status: data.available ?
`${data.latency.toFixed(2)}ms` :
'不可用'
});
});
return report.sort((a, b) => a.latency - b.latency);
}
}
// 使用示例
const cdnSelector = new SmartCDNSelector([
'https://cdn1.example.com',
'https://cdn2.example.com',
'https://cdn3.example.com'
]);
// 选择最快的CDN
cdnSelector.selectFastest().then(fastest => {
console.log('最快的CDN:', fastest);
console.log('性能报告:', cdnSelector.getPerformanceReport());
});
第九章:现代框架性能优化
9.1 React性能优化
// ========== React性能优化技巧 ==========
import React, {
memo,
useMemo,
useCallback,
lazy,
Suspense,
useState,
useEffect,
useRef
} from 'react';
// 1. 使用React.memo避免不必要的重渲染
// ❌ 未优化的组件
function ExpensiveComponent({ data, onClick }) {
console.log('ExpensiveComponent渲染');
return (
<div onClick={onClick}>
{data.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
}
// ✅ 使用memo优化
const OptimizedComponent = memo(function ExpensiveComponent({ data, onClick }) {
console.log('OptimizedComponent渲染');
return (
<div onClick={onClick}>
{data.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
}, (prevProps, nextProps) => {
// 自定义比较函数
return prevProps.data === nextProps.data &&
prevProps.onClick === nextProps.onClick;
});
// 2. 使用useMemo缓存计算结果
function DataProcessor({ items }) {
// ❌ 每次渲染都重新计算
const processedData = items.map(item => ({
...item,
processed: expensiveOperation(item)
}));
// ✅ 使用useMemo缓存
const optimizedData = useMemo(() => {
return items.map(item => ({
...item,
processed: expensiveOperation(item)
}));
}, [items]); // 只在items改变时重新计算
return <div>{/* 使用optimizedData */}</div>;
}
// 3. 使用useCallback缓存回调函数
function ParentComponent() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// ❌ 每次渲染都创建新函数
const handleClick = () => {
setCount(c => c + 1);
};
// ✅ 使用useCallback缓存
const optimizedHandleClick = useCallback(() => {
setCount(c => c + 1);
}, []); // 依赖为空,函数永远不变
return (
<div>
<input value={text} onChange={e => setText(e.target.value)} />
<OptimizedComponent
data={[]}
onClick={optimizedHandleClick}
/>
</div>
);
}
// 4. 列表渲染优化
function ListComponent({ items }) {
return (
<div>
{items.map(item => (
// ✅ 使用稳定的key
<ListItem key={item.id} item={item} />
))}
</div>
);
}
const ListItem = memo(function ListItem({ item }) {
return <div>{item.name}</div>;
});
// 5. 虚拟滚动(使用react-window)
import { FixedSizeList } from 'react-window';
function VirtualList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>
{items[index].name}
</div>
);
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={50}
width="100%"
>
{Row}
</FixedSizeList>
);
}
// 6. 代码分割与懒加载
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}
// 7. 避免内联对象和数组
function BadComponent() {
return (
// ❌ 每次渲染都创建新对象
<ChildComponent style={{ color: 'red' }} data={[1, 2, 3]} />
);
}
function GoodComponent() {
// ✅ 在组件外部定义
const style = useMemo(() => ({ color: 'red' }), []);
const data = useMemo(() => [1, 2, 3], []);
return <ChildComponent style={style} data={data} />;
}
// 8. 使用Profiler分析性能
import { Profiler } from 'react';
function onRenderCallback(
id, // 发生提交的 Profiler 树的 "id"
phase, // "mount" (首次挂载)或 "update" (重渲染)
actualDuration, // 本次更新花费的时间
baseDuration, // 估计不使用 memoization 的情况下渲染整颗子树需要的时间
startTime, // 本次更新开始渲染的时间
commitTime, // 本次更新提交的时间
interactions // 属于本次更新的 interactions 的集合
) {
console.log(`${id} (${phase}) 渲染耗时: ${actualDuration}ms`);
}
function ProfiledApp() {
return (
<Profiler id="App" onRender={onRenderCallback}>
<App />
</Profiler>
);
}
// 9. 状态管理优化
// 使用Context时避免不必要的重渲染
const DataContext = React.createContext();
const UpdateContext = React.createContext();
function DataProvider({ children }) {
const [data, setData] = useState({});
// 分离数据和更新函数
return (
<DataContext.Provider value={data}>
<UpdateContext.Provider value={setData}>
{children}
</UpdateContext.Provider>
</DataContext.Provider>
);
}
// 10. 使用useTransition处理非紧急更新
import { useTransition } from 'react';
function SearchComponent() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
const value = e.target.value;
setQuery(value);
// 将搜索结果更新标记为非紧急
startTransition(() => {
const filtered = expensiveSearch(value);
setResults(filtered);
});
};
return (
<div>
<input value={query} onChange={handleChange} />
{isPending && <div>搜索中...</div>}
<ResultsList results={results} />
</div>
);
}
// ========== React性能监控 ==========
class ReactPerformanceMonitor {
constructor() {
this.measurements = [];
}
// 测量组件渲染时间
measureComponent(componentName, callback) {
const startTime = performance.now();
const result = callback();
const endTime = performance.now();
this.measurements.push({
component: componentName,
duration: endTime - startTime,
timestamp: Date.now()
});
return result;
}
// 获取性能报告
getReport() {
const grouped = this.measurements.reduce((acc, m) => {
if (!acc[m.component]) {
acc[m.component] = {
count: 0,
totalDuration: 0,
avgDuration: 0
};
}
acc[m.component].count++;
acc[m.component].totalDuration += m.duration;
acc[m.component].avgDuration =
acc[m.component].totalDuration / acc[m.component].count;
return acc;
}, {});
return Object.entries(grouped)
.map(([component, stats]) => ({
component,
...stats
}))
.sort((a, b) => b.avgDuration - a.avgDuration);
}
// 打印报告
printReport() {
const report = this.getReport();
console.log('📊 React性能报告');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
report.forEach(item => {
console.log(`${item.component}:`);
console.log(` 渲染次数: ${item.count}`);
console.log(` 平均耗时: ${item.avgDuration.toFixed(2)}ms`);
console.log(` 总耗时: ${item.totalDuration.toFixed(2)}ms`);
});
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
}
}
// 使用示例
const monitor = new ReactPerformanceMonitor();
function MonitoredComponent() {
return monitor.measureComponent('MonitoredComponent', () => {
// 组件逻辑
return <div>Hello</div>;
});
}
9.2 Vue性能优化
// ========== Vue 3性能优化技巧 ==========
import {
ref,
computed,
watch,
watchEffect,
shallowRef,
shallowReactive,
markRaw,
defineAsyncComponent,
KeepAlive,
Teleport
} from 'vue';
// 1. 使用shallowRef和shallowReactive
// ❌ 深度响应式(性能开销大)
const deepState = ref({
nested: {
deep: {
value: 1
}
}
});
// ✅ 浅层响应式(性能更好)
const shallowState = shallowRef({
nested: {
deep: {
value: 1
}
}
});
// 更新时需要替换整个对象
shallowState.value = {
nested: {
deep: {
value: 2
}
}
};
// 2. 使用computed缓存计算结果
export default {
setup() {
const items = ref([]);
// ❌ 每次访问都重新计算
const getFilteredItems = () => {
return items.value.filter(item => item.active);
};
// ✅ 使用computed缓存
const filteredItems = computed(() => {
return items.value.filter(item => item.active);
});
return { filteredItems };
}
};
// 3. v-once和v-memo优化
// template:
/*
<!-- 只渲染一次 -->
<div v-once>{{ staticContent }}</div>
<!-- 根据依赖决定是否更新 -->
<div v-memo="[item.id, item.name]">
{{ item.name }} - {{ item.description }}
</div>
*/
// 4. 虚拟滚动
import { ref, computed } from 'vue';
export default {
setup() {
const items = ref(Array.from({ length: 10000 }, (_, i) => ({
id: i,
name: `Item ${i}`
})));
const scrollTop = ref(0);
const itemHeight = 50;
const visibleCount = 20;
const visibleItems = computed(() => {
const start = Math.floor(scrollTop.value / itemHeight);
const end = start + visibleCount;
return items.value.slice(start, end);
});
const offsetY = computed(() => {
return Math.floor(scrollTop.value / itemHeight) * itemHeight;
});
const totalHeight = computed(() => {
return items.value.length * itemHeight;
});
const handleScroll = (e) => {
scrollTop.value = e.target.scrollTop;
};
return {
visibleItems,
offsetY,
totalHeight,
handleScroll
};
}
};
// 5. 异步组件
const AsyncComponent = defineAsyncComponent({
loader: () => import('./HeavyComponent.vue'),
loadingComponent: LoadingComponent,
errorComponent: ErrorComponent,
delay: 200,
timeout: 3000
});
// 6. KeepAlive缓存组件
// template:
/*
<KeepAlive :max="10">
<component :is="currentComponent" />
</KeepAlive>
*/
// 7. 避免不必要的响应式
export default {
setup() {
// ❌ 大型数据结构不需要响应式
const largeData = ref({
// 10000条数据
});
// ✅ 使用markRaw标记为非响应式
const optimizedData = markRaw({
// 10000条数据
});
return { optimizedData };
}
};
// 8. 函数式组件
import { h } from 'vue';
// 函数式组件没有实例,性能更好
export default function FunctionalComponent(props) {
return h('div', props.text);
}
// 9. 优化watch
export default {
setup() {
const data = ref({});
// ❌ 深度监听(性能开销大)
watch(data, () => {
console.log('changed');
}, { deep: true });
// ✅ 监听特定属性
watch(() => data.value.specificProp, () => {
console.log('changed');
});
// ✅ 使用watchEffect(自动追踪依赖)
watchEffect(() => {
console.log(data.value.specificProp);
});
}
};
// 10. 延迟加载路由
const routes = [
{
path: '/',
component: () => import('./views/Home.vue')
},
{
path: '/about',
component: () => import('./views/About.vue')
}
];
// ========== Vue性能监控 ==========
class VuePerformanceMonitor {
constructor(app) {
this.app = app;
this.measurements = new Map();
this.setupHooks();
}
setupHooks() {
// 监听组件挂载
this.app.mixin({
beforeMount() {
const name = this.$options.name || 'Anonymous';
performance.mark(`${name}-mount-start`);
},
mounted() {
const name = this.$options.name || 'Anonymous';
performance.mark(`${name}-mount-end`);
performance.measure(
`${name}-mount`,
`${name}-mount-start`,
`${name}-mount-end`
);
const measure = performance.getEntriesByName(`${name}-mount`)[0];
this.recordMeasurement(name, 'mount', measure.duration);
},
beforeUpdate() {
const name = this.$options.name || 'Anonymous';
performance.mark(`${name}-update-start`);
},
updated() {
const name = this.$options.name || 'Anonymous';
performance.mark(`${name}-update-end`);
performance.measure(
`${name}-update`,
`${name}-update-start`,
`${name}-update-end`
);
const measure = performance.getEntriesByName(`${name}-update`)[0];
this.recordMeasurement(name, 'update', measure.duration);
}
});
}
recordMeasurement(component, type, duration) {
if (!this.measurements.has(component)) {
this.measurements.set(component, {
mount: [],
update: []
});
}
this.measurements.get(component)[type].push(duration);
}
getReport() {
const report = [];
this.measurements.forEach((data, component) => {
const mountAvg = data.mount.length > 0 ?
data.mount.reduce((a, b) => a + b, 0) / data.mount.length : 0;
const updateAvg = data.update.length > 0 ?
data.update.reduce((a, b) => a + b, 0) / data.update.length : 0;
report.push({
component,
mountCount: data.mount.length,
updateCount: data.update.length,
avgMountTime: mountAvg.toFixed(2),
avgUpdateTime: updateAvg.toFixed(2)
});
});
return report.sort((a, b) =>
parseFloat(b.avgUpdateTime) - parseFloat(a.avgUpdateTime)
);
}
printReport() {
const report = this.getReport();
console.log('📊 Vue性能报告');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
report.forEach(item => {
console.log(`${item.component}:`);
console.log(` 挂载次数: ${item.mountCount}`);
console.log(` 更新次数: ${item.updateCount}`);
console.log(` 平均挂载时间: ${item.avgMountTime}ms`);
console.log(` 平均更新时间: ${item.avgUpdateTime}ms`);
});
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
}
}
// 使用示例
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
const monitor = new VuePerformanceMonitor(app);
app.mount('#app');
// 在开发环境打印性能报告
if (process.env.NODE_ENV === 'development') {
setTimeout(() => {
monitor.printReport();
}, 5000);
}
第十章:性能监控与持续优化
10.1 性能监控系统
// ========== 综合性能监控系统 ==========
class PerformanceMonitoringSystem {
constructor(config = {}) {
this.config = {
reportInterval: config.reportInterval || 60000, // 1分钟
sampleRate: config.sampleRate || 1, // 100%采样
endpoint: config.endpoint || '/api/performance',
...config
};
this.metrics = {
navigation: [],
resources: [],
paint: [],
interactions: [],
errors: [],
customMetrics: []
};
this.init();
}
init() {
// 监听页面加载性能
this.observeNavigation();
// 监听资源加载性能
this.observeResources();
// 监听绘制性能
this.observePaint();
// 监听用户交互性能
this.observeInteractions();
// 监听错误
this.observeErrors();
// 定期上报
this.startReporting();
}
// 监听导航性能
observeNavigation() {
window.addEventListener('load', () => {
setTimeout(() => {
const navTiming = performance.getEntriesByType('navigation')[0];
if (navTiming) {
this.metrics.navigation.push({
timestamp: Date.now(),
url: window.location.href,
dns: navTiming.domainLookupEnd - navTiming.domainLookupStart,
tcp: navTiming.connectEnd - navTiming.connectStart,
request: navTiming.responseEnd - navTiming.requestStart,
response: navTiming.responseEnd - navTiming.responseStart,
dom: navTiming.domComplete - navTiming.domInteractive,
load: navTiming.loadEventEnd - navTiming.fetchStart,
ttfb: navTiming.responseStart - navTiming.requestStart
});
}
}, 0);
});
}
// 监听资源加载性能
observeResources() {
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if (Math.random() <= this.config.sampleRate) {
this.metrics.resources.push({
timestamp: Date.now(),
name: entry.name,
type: this.getResourceType(entry.name),
duration: entry.duration,
size: entry.transferSize || 0,
cached: entry.transferSize === 0 && entry.decodedBodySize > 0
});
}
});
});
observer.observe({ entryTypes: ['resource'] });
}
// 监听绘制性能
observePaint() {
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
this.metrics.paint.push({
timestamp: Date.now(),
name: entry.name,
startTime: entry.startTime
});
});
});
observer.observe({ entryTypes: ['paint'] });
// 监听LCP
const lcpObserver = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
this.metrics.paint.push({
timestamp: Date.now(),
name: 'largest-contentful-paint',
startTime: lastEntry.renderTime || lastEntry.loadTime
});
});
lcpObserver.observe({ entryTypes: ['largest-contentful-paint'] });
}
// 监听用户交互性能
observeInteractions() {
// FID
const fidObserver = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
this.metrics.interactions.push({
timestamp: Date.now(),
type: 'first-input-delay',
delay: entry.processingStart - entry.startTime,
eventType: entry.name
});
});
});
fidObserver.observe({ entryTypes: ['first-input'] });
// CLS
let clsValue = 0;
const clsObserver = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if (!entry.hadRecentInput) {
clsValue += entry.value;
}
});
});
clsObserver.observe({ entryTypes: ['layout-shift'] });
// 页面卸载时记录CLS
window.addEventListener('beforeunload', () => {
this.metrics.interactions.push({
timestamp: Date.now(),
type: 'cumulative-layout-shift',
value: clsValue
});
});
}
// 监听错误
observeErrors() {
// JavaScript错误
window.addEventListener('error', (event) => {
this.metrics.errors.push({
timestamp: Date.now(),
type: 'javascript',
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
stack: event.error?.stack
});
});
// Promise错误
window.addEventListener('unhandledrejection', (event) => {
this.metrics.errors.push({
timestamp: Date.now(),
type: 'promise',
reason: event.reason,
promise: event.promise
});
});
// 资源加载错误
window.addEventListener('error', (event) => {
if (event.target !== window) {
this.metrics.errors.push({
timestamp: Date.now(),
type: 'resource',
target: event.target.tagName,
src: event.target.src || event.target.href
});
}
}, true);
}
// 记录自定义指标
recordCustomMetric(name, value, tags = {}) {
this.metrics.customMetrics.push({
timestamp: Date.now(),
name,
value,
tags
});
}
// 获取资源类型
getResourceType(url) {
if (url.match(/.(js)$/)) return 'script';
if (url.match(/.(css)$/)) return 'stylesheet';
if (url.match(/.(jpg|jpeg|png|gif|webp|svg)$/)) return 'image';
if (url.match(/.(woff|woff2|ttf|eot)$/)) return 'font';
return 'other';
}
// 生成报告
generateReport() {
return {
timestamp: Date.now(),
url: window.location.href,
userAgent: navigator.userAgent,
metrics: {
navigation: this.aggregateNavigation(),
resources: this.aggregateResources(),
paint: this.aggregatePaint(),
interactions: this.aggregateInteractions(),
errors: this.metrics.errors,
custom: this.metrics.customMetrics
}
};
}
// 聚合导航数据
aggregateNavigation() {
if (this.metrics.navigation.length === 0) return null;
const latest = this.metrics.navigation[this.metrics.navigation.length - 1];
return latest;
}
// 聚合资源数据
aggregateResources() {
const byType = {};
this.metrics.resources.forEach(resource => {
if (!byType[resource.type]) {
byType[resource.type] = {
count: 0,
totalSize: 0,
totalDuration: 0,
cached: 0
};
}
byType[resource.type].count++;
byType[resource.type].totalSize += resource.size;
byType[resource.type].totalDuration += resource.duration;
if (resource.cached) byType[resource.type].cached++;
});
return byType;
}
// 聚合绘制数据
aggregatePaint() {
const paintMetrics = {};
this.metrics.paint.forEach(paint => {
paintMetrics[paint.name] = paint.startTime;
});
return paintMetrics;
}
// 聚合交互数据
aggregateInteractions() {
const interactions = {};
this.metrics.interactions.forEach(interaction => {
if (interaction.type === 'first-input-delay') {
interactions.fid = interaction.delay;
} else if (interaction.type === 'cumulative-layout-shift') {
interactions.cls = interaction.value;
}
});
return interactions;
}
// 上报数据
async report() {
const report = this.generateReport();
try {
// 使用sendBeacon确保数据发送
if (navigator.sendBeacon) {
const blob = new Blob([JSON.stringify(report)], {
type: 'application/json'
});
navigator.sendBeacon(this.config.endpoint, blob);
} else {
// 降级到fetch
await fetch(this.config.endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(report),
keepalive: true
});
}
// 清空已上报的数据
this.clearMetrics();
} catch (error) {
console.error('性能数据上报失败:', error);
}
}
// 清空指标
clearMetrics() {
this.metrics.resources = [];
this.metrics.errors = [];
this.metrics.customMetrics = [];
}
// 开始定期上报
startReporting() {
setInterval(() => {
this.report();
}, this.config.reportInterval);
// 页面卸载时上报
window.addEventListener('beforeunload', () => {
this.report();
});
// 页面隐藏时上报
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
this.report();
}
});
}
// 打印本地报告
printReport() {
const report = this.generateReport();
console.log('📊 性能监控报告');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('URL:', report.url);
console.log('时间:', new Date(report.timestamp).toLocaleString());
console.log('');
if (report.metrics.navigation) {
console.log('📡 导航性能:');
console.log(` DNS: ${report.metrics.navigation.dns.toFixed(2)}ms`);
console.log(` TCP: ${report.metrics.navigation.tcp.toFixed(2)}ms`);
console.log(` 请求: ${report.metrics.navigation.request.toFixed(2)}ms`);
console.log(` DOM: ${report.metrics.navigation.dom.toFixed(2)}ms`);
console.log(` 加载: ${report.metrics.navigation.load.toFixed(2)}ms`);
console.log(` TTFB: ${report.metrics.navigation.ttfb.toFixed(2)}ms`);
console.log('');
}
if (Object.keys(report.metrics.resources).length > 0) {
console.log('📦 资源加载:');
Object.entries(report.metrics.resources).forEach(([type, data]) => {
console.log(` ${type}:`);
console.log(` 数量: ${data.count}`);
console.log(` 大小: ${(data.totalSize / 1024).toFixed(2)} KB`);
console.log(` 耗时: ${data.totalDuration.toFixed(2)}ms`);
console.log(` 缓存: ${data.cached}/${data.count}`);
});
console.log('');
}
if (Object.keys(report.metrics.paint).length > 0) {
console.log('🎨 绘制性能:');
Object.entries(report.metrics.paint).forEach(([name, time]) => {
console.log(` ${name}: ${time.toFixed(2)}ms`);
});
console.log('');
}
if (Object.keys(report.metrics.interactions).length > 0) {
console.log('👆 交互性能:');
if (report.metrics.interactions.fid !== undefined) {
console.log(` FID: ${report.metrics.interactions.fid.toFixed(2)}ms`);
}
if (report.metrics.interactions.cls !== undefined) {
console.log(` CLS: ${report.metrics.interactions.cls.toFixed(4)}`);
}
console.log('');
}
if (report.metrics.errors.length > 0) {
console.log('❌ 错误统计:');
console.log(` 总数: ${report.metrics.errors.length}`);
const errorsByType = report.metrics.errors.reduce((acc, err) => {
acc[err.type] = (acc[err.type] || 0) + 1;
return acc;
}, {});
Object.entries(errorsByType).forEach(([type, count]) => {
console.log(` ${type}: ${count}`);
});
}
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
}
}
// 使用示例
const monitor = new PerformanceMonitoringSystem({
reportInterval: 30000, // 30秒上报一次
sampleRate: 0.1, // 10%采样率
endpoint: '/api/performance'
});
// 记录自定义指标
monitor.recordCustomMetric('api-call', 234, {
endpoint: '/api/users',
method: 'GET'
});
// 打印本地报告
setTimeout(() => {
monitor.printReport();
}, 5000);
10.2 性能预算
// ========== 性能预算系统 ==========
class PerformanceBudget {
constructor(budgets) {
this.budgets = {
// 页面加载预算
pageLoad: {
ttfb: budgets.ttfb || 600, // Time to First Byte
fcp: budgets.fcp || 1800, // First Contentful Paint
lcp: budgets.lcp || 2500, // Largest Contentful Paint
tti: budgets.tti || 3800, // Time to Interactive
totalLoad: budgets.totalLoad || 5000 // Total Load Time
},
// 资源预算
resources: {
javascript: budgets.javascript || 200, // KB
css: budgets.css || 100, // KB
images: budgets.images || 500, // KB
fonts: budgets.fonts || 100, // KB
total: budgets.total || 1000 // KB
},
// 交互预算
interactions: {
fid: budgets.fid || 100, // First Input Delay (ms)
cls: budgets.cls || 0.1 // Cumulative Layout Shift
},
// 请求数量预算
requests: {
total: budgets.requests || 50,
javascript: budgets.jsRequests || 10,
css: budgets.cssRequests || 5,
images: budgets.imageRequests || 30
}
};
this.violations = [];
}
// 检查页面加载性能
checkPageLoad() {
const navTiming = performance.getEntriesByType('navigation')[0];
if (!navTiming) return;
const metrics = {
ttfb: navTiming.responseStart - navTiming.requestStart,
totalLoad: navTiming.loadEventEnd - navTiming.fetchStart
};
// 检查TTFB
if (metrics.ttfb > this.budgets.pageLoad.ttfb) {
this.violations.push({
type: 'pageLoad',
metric: 'TTFB',
actual: metrics.ttfb.toFixed(2),
budget: this.budgets.pageLoad.ttfb,
severity: this.calculateSeverity(metrics.ttfb, this.budgets.pageLoad.ttfb)
});
}
// 检查总加载时间
if (metrics.totalLoad > this.budgets.pageLoad.totalLoad) {
this.violations.push({
type: 'pageLoad',
metric: 'Total Load',
actual: metrics.totalLoad.toFixed(2),
budget: this.budgets.pageLoad.totalLoad,
severity: this.calculateSeverity(metrics.totalLoad, this.budgets.pageLoad.totalLoad)
});
}
}
// 检查资源大小
checkResources() {
const resources = performance.getEntriesByType('resource');
const sizes = {
javascript: 0,
css: 0,
images: 0,
fonts: 0,
total: 0
};
const counts = {
total: resources.length,
javascript: 0,
css: 0,
images: 0
};
resources.forEach(resource => {
const size = (resource.transferSize || 0) / 1024; // 转换为KB
sizes.total += size;
if (resource.name.match(/.js$/)) {
sizes.javascript += size;
counts.javascript++;
} else if (resource.name.match(/.css$/)) {
sizes.css += size;
counts.css++;
} else if (resource.name.match(/.(jpg|jpeg|png|gif|webp|svg)$/)) {
sizes.images += size;
counts.images++;
} else if (resource.name.match(/.(woff|woff2|ttf|eot)$/)) {
sizes.fonts += size;
}
});
// 检查各类资源大小
Object.entries(sizes).forEach(([type, size]) => {
if (size > this.budgets.resources[type]) {
this.violations.push({
type: 'resources',
metric: `${type} size`,
actual: size.toFixed(2) + ' KB',
budget: this.budgets.resources[type] + ' KB',
severity: this.calculateSeverity(size, this.budgets.resources[type])
});
}
});
// 检查请求数量
Object.entries(counts).forEach(([type, count]) => {
if (count > this.budgets.requests[type]) {
this.violations.push({
type: 'requests',
metric: `${type} requests`,
actual: count,
budget: this.budgets.requests[type],
severity: this.calculateSeverity(count, this.budgets.requests[type])
});
}
});
}
// 检查Core Web Vitals
async checkCoreWebVitals() {
return new Promise((resolve) => {
const metrics = {
lcp: null,
fid: null,
cls: null
};
// LCP
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
metrics.lcp = lastEntry.renderTime || lastEntry.loadTime;
if (metrics.lcp > this.budgets.pageLoad.lcp) {
this.violations.push({
type: 'coreWebVitals',
metric: 'LCP',
actual: metrics.lcp.toFixed(2) + ' ms',
budget: this.budgets.pageLoad.lcp + ' ms',
severity: this.calculateSeverity(metrics.lcp, this.budgets.pageLoad.lcp)
});
}
}).observe({ entryTypes: ['largest-contentful-paint'] });
// FID
new PerformanceObserver((list) => {
const entries = list.getEntries();
entries.forEach(entry => {
if (!metrics.fid) {
metrics.fid = entry.processingStart - entry.startTime;
if (metrics.fid > this.budgets.interactions.fid) {
this.violations.push({
type: 'coreWebVitals',
metric: 'FID',
actual: metrics.fid.toFixed(2) + ' ms',
budget: this.budgets.interactions.fid + ' ms',
severity: this.calculateSeverity(metrics.fid, this.budgets.interactions.fid)
});
}
}
});
}).observe({ entryTypes: ['first-input'] });
// CLS
let clsValue = 0;
new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
if (!entry.hadRecentInput) {
clsValue += entry.value;
metrics.cls = clsValue;
}
});
}).observe({ entryTypes: ['layout-shift'] });
// 5秒后检查CLS
setTimeout(() => {
if (metrics.cls > this.budgets.interactions.cls) {
this.violations.push({
type: 'coreWebVitals',
metric: 'CLS',
actual: metrics.cls.toFixed(4),
budget: this.budgets.interactions.cls,
severity: this.calculateSeverity(metrics.cls, this.budgets.interactions.cls)
});
}
resolve(metrics);
}, 5000);
});
}
// 计算严重程度
calculateSeverity(actual, budget) {
const ratio = actual / budget;
if (ratio >= 2) return 'critical';
if (ratio >= 1.5) return 'high';
if (ratio >= 1.2) return 'medium';
return 'low';
}
// 执行所有检查
async runAllChecks() {
this.violations = [];
this.checkPageLoad();
this.checkResources();
await this.checkCoreWebVitals();
return this.violations;
}
// 生成报告
generateReport() {
const report = {
timestamp: Date.now(),
url: window.location.href,
totalViolations: this.violations.length,
violationsBySeverity: {
critical: 0,
high: 0,
medium: 0,
low: 0
},
violations: this.violations
};
this.violations.forEach(v => {
report.violationsBySeverity[v.severity]++;
});
return report;
}
// 打印报告
printReport() {
const report = this.generateReport();
console.log('💰 性能预算报告');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log(`URL: ${report.url}`);
console.log(`时间: ${new Date(report.timestamp).toLocaleString()}`);
console.log(`总违规数: ${report.totalViolations}`);
console.log('');
console.log('按严重程度统计:');
console.log(` 🔴 严重: ${report.violationsBySeverity.critical}`);
console.log(` 🟠 高: ${report.violationsBySeverity.high}`);
console.log(` 🟡 中: ${report.violationsBySeverity.medium}`);
console.log(` 🟢 低: ${report.violationsBySeverity.low}`);
console.log('');
if (report.violations.length > 0) {
console.log('违规详情:');
report.violations.forEach((v, index) => {
const icon = {
critical: '🔴',
high: '🟠',
medium: '🟡',
low: '🟢'
}[v.severity];
console.log(`${index + 1}. ${icon} ${v.metric}`);
console.log(` 实际值: ${v.actual}`);
console.log(` 预算值: ${v.budget}`);
console.log(` 严重程度: ${v.severity}`);
});
} else {
console.log('✅ 所有指标都在预算范围内!');
}
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
}
}
// 使用示例
const budget = new PerformanceBudget({
// 页面加载预算(毫秒)
ttfb: 600,
fcp: 1800,
lcp: 2500,
tti: 3800,
totalLoad: 5000,
// 资源预算(KB)
javascript: 200,
css: 100,
images: 500,
fonts: 100,
total: 1000,
// 交互预算
fid: 100,
cls: 0.1,
// 请求数量预算
requests: 50,
jsRequests: 10,
cssRequests: 5,
imageRequests: 30
});
// 页面加载完成后检查
window.addEventListener('load', async () => {
await budget.runAllChecks();
budget.printReport();
// 如果有严重违规,发出警告
const report = budget.generateReport();
if (report.violationsBySeverity.critical > 0) {
console.error('⚠️ 检测到严重的性能预算违规!');
}
});
10.3 持续优化流程
// ========== 持续优化流程管理 ==========
class ContinuousOptimizationPipeline {
constructor() {
this.optimizations = new Map();
this.history = [];
this.baseline = null;
}
// 设置性能基线
async setBaseline() {
const metrics = await this.measurePerformance();
this.baseline = {
timestamp: Date.now(),
metrics
};
console.log('📊 性能基线已设置');
this.printMetrics(metrics);
}
// 测量性能
async measurePerformance() {
return new Promise((resolve) => {
const metrics = {
navigation: null,
resources: null,
paint: null,
interactions: null
};
// 导航性能
const navTiming = performance.getEntriesByType('navigation')[0];
if (navTiming) {
metrics.navigation = {
ttfb: navTiming.responseStart - navTiming.requestStart,
domLoad: navTiming.domContentLoadedEventEnd - navTiming.fetchStart,
totalLoad: navTiming.loadEventEnd - navTiming.fetchStart
};
}
// 资源性能
const resources = performance.getEntriesByType('resource');
const totalSize = resources.reduce((sum, r) => sum + (r.transferSize || 0), 0);
metrics.resources = {
count: resources.length,
totalSize: totalSize / 1024, // KB
avgDuration: resources.reduce((sum, r) => sum + r.duration, 0) / resources.length
};
// 绘制性能
const paintEntries = performance.getEntriesByType('paint');
metrics.paint = {};
paintEntries.forEach(entry => {
metrics.paint[entry.name] = entry.startTime;
});
// Core Web Vitals
let lcp = null;
let fid = null;
let cls = 0;
new PerformanceObserver((list) => {
const entries = list.getEntries();
lcp = entries[entries.length - 1].renderTime || entries[entries.length - 1].loadTime;
}).observe({ entryTypes: ['largest-contentful-paint'] });
new PerformanceObserver((list) => {
const entries = list.getEntries();
if (entries.length > 0 && !fid) {
fid = entries[0].processingStart - entries[0].startTime;
}
}).observe({ entryTypes: ['first-input'] });
new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
if (!entry.hadRecentInput) {
cls += entry.value;
}
});
}).observe({ entryTypes: ['layout-shift'] });
setTimeout(() => {
metrics.interactions = { lcp, fid, cls };
resolve(metrics);
}, 3000);
});
}
// 注册优化
registerOptimization(name, implementation, expectedImprovement) {
this.optimizations.set(name, {
name,
implementation,
expectedImprovement,
applied: false,
actualImprovement: null
});
}
// 应用优化
async applyOptimization(name) {
const optimization = this.optimizations.get(name);
if (!optimization) {
console.error(`优化 ${name} 不存在`);
return;
}
if (optimization.applied) {
console.warn(`优化 ${name} 已经应用过`);
return;
}
console.log(`🔧 应用优化: ${name}`);
// 应用前测量
const before = await this.measurePerformance();
// 应用优化
await optimization.implementation();
// 等待一段时间让优化生效
await new Promise(resolve => setTimeout(resolve, 2000));
// 应用后测量
const after = await this.measurePerformance();
// 计算改进
const improvement = this.calculateImprovement(before, after);
optimization.applied = true;
optimization.actualImprovement = improvement;
// 记录历史
this.history.push({
timestamp: Date.now(),
optimization: name,
before,
after,
improvement
});
console.log(`✅ 优化 ${name} 已应用`);
this.printImprovement(improvement);
}
// 计算改进
calculateImprovement(before, after) {
const improvement = {};
// 导航性能改进
if (before.navigation && after.navigation) {
improvement.navigation = {
ttfb: this.percentChange(before.navigation.ttfb, after.navigation.ttfb),
domLoad: this.percentChange(before.navigation.domLoad, after.navigation.domLoad),
totalLoad: this.percentChange(before.navigation.totalLoad, after.navigation.totalLoad)
};
}
// 资源改进
if (before.resources && after.resources) {
improvement.resources = {
count: this.percentChange(before.resources.count, after.resources.count),
totalSize: this.percentChange(before.resources.totalSize, after.resources.totalSize),
avgDuration: this.percentChange(before.resources.avgDuration, after.resources.avgDuration)
};
}
// Core Web Vitals改进
if (before.interactions && after.interactions) {
improvement.interactions = {
lcp: this.percentChange(before.interactions.lcp, after.interactions.lcp),
fid: before.interactions.fid && after.interactions.fid ?
this.percentChange(before.interactions.fid, after.interactions.fid) : null,
cls: this.percentChange(before.interactions.cls, after.interactions.cls)
};
}
return improvement;
}
// 计算百分比变化
percentChange(before, after) {
if (!before || !after) return null;
return ((before - after) / before * 100).toFixed(2);
}
// 生成优化报告
generateOptimizationReport() {
const report = {
baseline: this.baseline,
optimizations: [],
totalImprovement: null
};
this.optimizations.forEach((opt, name) => {
if (opt.applied) {
report.optimizations.push({
name: opt.name,
expectedImprovement: opt.expectedImprovement,
actualImprovement: opt.actualImprovement
});
}
});
// 计算总体改进
if (this.baseline && this.history.length > 0) {
const latest = this.history[this.history.length - 1];
report.totalImprovement = this.calculateImprovement(
this.baseline.metrics,
latest.after
);
}
return report;
}
// 打印指标
printMetrics(metrics) {
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
if (metrics.navigation) {
console.log('导航性能:');
console.log(` TTFB: ${metrics.navigation.ttfb.toFixed(2)}ms`);
console.log(` DOM加载: ${metrics.navigation.domLoad.toFixed(2)}ms`);
console.log(` 总加载: ${metrics.navigation.totalLoad.toFixed(2)}ms`);
}
if (metrics.resources) {
console.log('资源性能:');
console.log(` 数量: ${metrics.resources.count}`);
console.log(` 总大小: ${metrics.resources.totalSize.toFixed(2)} KB`);
console.log(` 平均耗时: ${metrics.resources.avgDuration.toFixed(2)}ms`);
}
if (metrics.interactions) {
console.log('Core Web Vitals:');
if (metrics.interactions.lcp) {
console.log(` LCP: ${metrics.interactions.lcp.toFixed(2)}ms`);
}
if (metrics.interactions.fid) {
console.log(` FID: ${metrics.interactions.fid.toFixed(2)}ms`);
}
console.log(` CLS: ${metrics.interactions.cls.toFixed(4)}`);
}
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
}
// 打印改进
printImprovement(improvement) {
console.log('📈 性能改进:');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
if (improvement.navigation) {
console.log('导航性能改进:');
Object.entries(improvement.navigation).forEach(([key, value]) => {
if (value !== null) {
const icon = parseFloat(value) > 0 ? '✅' : '❌';
console.log(` ${icon} ${key}: ${value}%`);
}
});
}
if (improvement.resources) {
console.log('资源性能改进:');
Object.entries(improvement.resources).forEach(([key, value]) => {
if (value !== null) {
const icon = parseFloat(value) > 0 ? '✅' : '❌';
console.log(` ${icon} ${key}: ${value}%`);
}
});
}
if (improvement.interactions) {
console.log('Core Web Vitals改进:');
Object.entries(improvement.interactions).forEach(([key, value]) => {
if (value !== null) {
const icon = parseFloat(value) > 0 ? '✅' : '❌';
console.log(` ${icon} ${key.toUpperCase()}: ${value}%`);
}
});
}
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
}
// 打印完整报告
printFullReport() {
const report = this.generateOptimizationReport();
console.log('📊 持续优化完整报告');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
if (report.baseline) {
console.log('基线指标:');
this.printMetrics(report.baseline.metrics);
}
console.log('已应用的优化:');
report.optimizations.forEach((opt, index) => {
console.log(`${index + 1}. ${opt.name}`);
console.log(` 预期改进: ${opt.expectedImprovement}`);
});
if (report.totalImprovement) {
console.log('');
console.log('总体改进:');
this.printImprovement(report.totalImprovement);
}
}
}
// 使用示例
const pipeline = new ContinuousOptimizationPipeline();
// 设置基线
window.addEventListener('load', async () => {
await pipeline.setBaseline();
// 注册优化措施
pipeline.registerOptimization(
'图片懒加载',
async () => {
// 实现图片懒加载
const images = document.querySelectorAll('img[data-src]');
images.forEach(img => {
img.src = img.dataset.src;
});
},
'预计减少初始加载时间30%'
);
pipeline.registerOptimization(
'代码分割',
async () => {
// 实现代码分割
// 这里只是示例,实际需要配置webpack
console.log('应用代码分割...');
},
'预计减少JavaScript大小40%'
);
pipeline.registerOptimization(
'启用Gzip压缩',
async () => {
// 这需要服务端配置,这里只是示例
console.log('启用Gzip压缩...');
},
'预计减少传输大小60%'
);
// 逐个应用优化
await pipeline.applyOptimization('图片懒加载');
await pipeline.applyOptimization('代码分割');
await pipeline.applyOptimization('启用Gzip压缩');
// 打印完整报告
pipeline.printFullReport();
});
总结与最佳实践
性能优化核心原则
// ========== JavaScript性能优化最佳实践总结 ==========
const PERFORMANCE_BEST_PRACTICES = {
// 1. 测量优先
measurement: {
principle: "先测量,再优化",
practices: [
"使用Performance API测量关键指标",
"设置性能基线",
"持续监控Core Web Vitals",
"使用Lighthouse进行定期审计",
"建立性能预算"
],
tools: [
"Chrome DevTools Performance",
"Lighthouse",
"WebPageTest",
"Performance Observer API",
"Real User Monitoring (RUM)"
]
},
// 2. 代码优化
codeOptimization: {
principle: "写高效的代码",
practices: [
"选择合适的算法和数据结构",
"避免不必要的循环和计算",
"使用函数缓存(memoization)",
"实施防抖和节流",
"避免内存泄漏"
],
antiPatterns: [
"过早优化",
"忽略可读性",
"过度优化微小细节",
"不测量就优化"
]
},
// 3. DOM优化
domOptimization: {
principle: "最小化DOM操作",
practices: [
"批量DOM操作",
"使用DocumentFragment",
"避免强制同步布局",
"使用事件委托",
"实施虚拟滚动"
],
cssOptimization: [
"使用transform代替position",
"使用opacity代替visibility",
"避免复杂的CSS选择器",
"使用will-change提示浏览器"
]
},
// 4. 资源优化
resourceOptimization: {
principle: "减少资源大小和数量",
practices: [
"代码分割和懒加载",
"Tree Shaking移除未使用代码",
"压缩JavaScript、CSS和图片",
"使用现代图片格式(WebP、AVIF)",
"实施资源缓存策略"
],
loading: [
"关键资源预加载",
"非关键资源延迟加载",
"使用CDN加速",
"启用HTTP/2或HTTP/3",
"实施Service Worker缓存"
]
},
// 5. 渲染优化
renderingOptimization: {
principle: "优化渲染性能",
practices: [
"减少重排和重绘",
"使用CSS动画代替JavaScript动画",
"启用GPU加速",
"使用requestAnimationFrame",
"实施离屏渲染"
],
frameworks: {
react: [
"使用React.memo避免不必要渲染",
"使用useMemo和useCallback",
"实施代码分割",
"使用虚拟滚动",
"优化列表渲染的key"
],
vue: [
"使用shallowRef和shallowReactive",
"使用computed缓存计算",
"使用v-once和v-memo",
"实施异步组件",
"使用KeepAlive缓存组件"
]
}
},
// 6. 网络优化
networkOptimization: {
principle: "减少网络延迟",
practices: [
"请求合并和批处理",
"实施HTTP缓存",
"使用预连接和DNS预解析",
"启用压缩(Gzip/Brotli)",
"优化API响应大小"
],
strategies: [
"使用CDN",
"实施智能CDN选择",
"优化首屏加载",
"实施渐进式渲染",
"使用Service Worker离线缓存"
]
},
// 7. 监控与持续优化
monitoring: {
principle: "持续监控和优化",
practices: [
"实施性能监控系统",
"设置性能预算",
"建立持续优化流程",
"定期性能审计",
"A/B测试优化效果"
],
metrics: [
"TTFB (Time to First Byte)",
"FCP (First Contentful Paint)",
"LCP (Largest Contentful Paint)",
"FID (First Input Delay)",
"CLS (Cumulative Layout Shift)",
"TTI (Time to Interactive)"
]
}
};
// 打印最佳实践总结
console.log('📚 JavaScript性能优化最佳实践');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
Object.entries(PERFORMANCE_BEST_PRACTICES).forEach(([category, data]) => {
console.log(`
${category.toUpperCase()}`);
console.log(`原则: ${data.principle}`);
console.log('实践:');
data.practices.forEach((practice, index) => {
console.log(` ${index + 1}. ${practice}`);
});
});
console.log('
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('记住:性能优化是一个持续的过程,不是一次性的任务!');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
性能优化检查清单
// ========== 性能优化检查清单 ==========
const PERFORMANCE_CHECKLIST = {
// 初始加载
initialLoad: {
category: "初始加载优化",
items: [
{ task: "实施代码分割", priority: "高", status: "pending" },
{ task: "启用Tree Shaking", priority: "高", status: "pending" },
{ task: "压缩JavaScript和CSS", priority: "高", status: "pending" },
{ task: "优化图片(WebP、压缩)", priority: "高", status: "pending" },
{ task: "实施懒加载", priority: "中", status: "pending" },
{ task: "使用CDN", priority: "中", status: "pending" },
{ task: "启用Gzip/Brotli压缩", priority: "高", status: "pending" },
{ task: "实施HTTP缓存", priority: "高", status: "pending" }
]
},
// 运行时性能
runtime: {
category: "运行时性能优化",
items: [
{ task: "优化循环和算法", priority: "中", status: "pending" },
{ task: "实施函数缓存", priority: "中", status: "pending" },
{ task: "使用防抖和节流", priority: "高", status: "pending" },
{ task: "避免内存泄漏", priority: "高", status: "pending" },
{ task: "优化事件处理", priority: "中", status: "pending" },
{ task: "使用Web Worker", priority: "低", status: "pending" }
]
},
// 渲染性能
rendering: {
category: "渲染性能优化",
items: [
{ task: "减少DOM操作", priority: "高", status: "pending" },
{ task: "避免强制同步布局", priority: "高", status: "pending" },
{ task: "使用CSS动画", priority: "中", status: "pending" },
{ task: "启用GPU加速", priority: "中", status: "pending" },
{ task: "实施虚拟滚动", priority: "中", status: "pending" },
{ task: "优化CSS选择器", priority: "低", status: "pending" }
]
},
// 网络性能
network: {
category: "网络性能优化",
items: [
{ task: "请求合并", priority: "中", status: "pending" },
{ task: "实施预加载", priority: "中", status: "pending" },
{ task: "使用Service Worker", priority: "低", status: "pending" },
{ task: "优化API响应", priority: "高", status: "pending" },
{ task: "实施请求缓存", priority: "高", status: "pending" }
]
},
// 监控
monitoring: {
category: "性能监控",
items: [
{ task: "设置性能监控", priority: "高", status: "pending" },
{ task: "建立性能预算", priority: "高", status: "pending" },
{ task: "实施错误监控", priority: "高", status: "pending" },
{ task: "定期性能审计", priority: "中", status: "pending" }
]
}
};
// 打印检查清单
function printChecklist() {
console.log('✅ JavaScript性能优化检查清单');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
Object.values(PERFORMANCE_CHECKLIST).forEach(section => {
console.log(`
📋 ${section.category}`);
section.items.forEach((item, index) => {
const icon = item.status === 'done' ? '✅' : '⬜';
const priority = {
'高': '🔴',
'中': '🟡',
'低': '🟢'
}[item.priority];
console.log(`${icon} ${index + 1}. ${item.task} ${priority}`);
});
});
console.log('
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
}
printChecklist();
结语
性能优化是一个持续的过程,需要:
测量优先 – 先测量,再优化,用数据说话用户体验 – 始终以用户体验为中心平衡取舍 – 在性能、可维护性和开发效率之间找到平衡持续监控 – 建立监控系统,及时发现性能问题团队协作 – 性能优化需要整个团队的共同努力
记住:过早优化是万恶之源,但忽视性能同样危险。在正确的时机做正确的优化,才是最佳实践。
相关资源:
Web.dev – PerformanceMDN – PerformanceChrome DevTools – PerformanceLighthouseWebPageTest
💡 提示: 本文涵盖了JavaScript性能优化的方方面面,建议收藏后分章节学习,并在实际项目中逐步应用这些优化技巧。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
暂无评论...


