大家好,我是老十三,一名前端开发工程师。性能优化如同唐僧的九道心经,是前端修行的精髓所在。在本文中,我将为你揭示从网络传输到渲染优化的九大关键技术,涵盖HTTP协议、资源加载策略、缓存控制等核心难题。通过这些实战技巧,你将能打造出如行云流水般顺滑的用户体验,让你的应用脱胎换骨,达到性能优化的"大乘境界"。
工程化之道修行完毕,我们继续西行,迎来前端取经第六关——性能优化。唐僧虽不像孙悟空那般神通广大,但他的九道心经却是取经团队的精神支柱。同样,前端性能优化虽不如框架技术那般光鲜,却是用户体验的根本保障,值得每位前端修行者深入探索。
🚀 第一难:网络优化 - HTTP/2与HTTP/3的"缩地术"
问题:为什么我的网站在弱网环境下体验差?如何利用现代网络协议提升加载速度?
深度技术:
网络传输是前端性能的第一道关卡,而HTTP协议的演进极大改变了资源加载效率。从HTTP/1.1到HTTP/2再到HTTP/3,每一代协议都带来了性能突破。
HTTP/2引入了多路复用、头部压缩、服务器推送等特性,彻底改变了我们对"减少HTTP请求"的传统优化思路;而基于UDP的HTTP/3(QUIC)进一步解决了队头阻塞问题,提供更可靠的弱网环境表现。理解这些协议的核心机制,才能设计出真正高效的前端资源加载策略。
代码示例:
// 1. HTTP/2服务器推送配置
// Nginx配置示例
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
// HTTP/2服务器推送
location / {
root /var/www/html;
http2_push /styles/main.css;
http2_push /scripts/main.js;
http2_push /images/logo.png;
}
}
// 2. Link预加载标头(替代服务器推送)
// Express.js中间件示例
app.get('/', (req, res) => {
// 使用Link头预告关键资源
res.set('Link', [
'</styles/main.css>; rel=preload; as=style',
'</scripts/main.js>; rel=preload; as=script',
'</fonts/awesome.woff2>; rel=preload; as=font; crossorigin'
].join(', '));
res.render('index');
});
// 3. 域名分片(HTTP/1.1时代的优化,HTTP/2下反而有害)
// bad.js - HTTP/2下不推荐
function loadImagesFromMultipleDomains() {
const domains = [
'assets1.example.com',
'assets2.example.com',
'assets3.example.com'
];
return images.map((image, index) => {
const domain = domains[index % domains.length];
return `https://${domain}/${image.path}`;
});
}
// good.js - HTTP/2下推荐
function loadImagesFromSingleDomain() {
// 使用单一域名,利用HTTP/2多路复用
return images.map(image => `https://assets.example.com/${image.path}`);
}
// 4. 资源合并(HTTP/1.1时代的优化,HTTP/2下需重新评估)
// webpack.config.js
module.exports = {
// HTTP/1.1环境: 推荐合并文件减少请求数
// HTTP/2环境: 适度拆分,利用并行加载
optimization: {
splitChunks: {
chunks: 'all',
maxInitialRequests: 10, // HTTP/2下可以设置更高
maxAsyncRequests: 10,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
};
// 5. 使用CDN加速
// index.html中引用CDN资源
<script src="https://cdn.example.com/libraries/react.production.min.js"></script>
// 配置CDN回源
// 使用源站防护,控制CDN回源流量
const config = {
cdnDomain: 'cdn.example.com',
// 避免直接访问源站
preventDirectAccess: process.env.NODE_ENV === 'production',
generateUrl(path) {
return `https://${this.cdnDomain}/${path}`;
}
};
// 6. 分析并优化第三方资源
// 第三方资源审计脚本
function auditThirdPartyResources() {
const resources = performance.getEntriesByType('resource');
// 按域名分组统计
const domainStats = {};
resources.forEach(resource => {
const url = new URL(resource.name);
const domain = url.hostname;
if (!domainStats[domain]) {
domainStats[domain] = {
count: 0,
totalSize: 0,
resources: []
};
}
domainStats[domain].count++;
domainStats[domain].totalSize += resource.encodedBodySize || 0;
domainStats[domain].resources.push({
url: resource.name,
size: resource.encodedBodySize || 0,
duration: resource.duration,
initiatorType: resource.initiatorType
});
});
console.table(
Object.entries(domainStats)
.map(([domain, stats]) => ({
Domain: domain,
Requests: stats.count,
'Total Size (KB)': (stats.totalSize / 1024).toFixed(2),
'Is Third Party': !domain.includes(window.location.hostname)
}))
);
}
// 7. 实现HTTP/3检测和回退
function detectHttp3Support() {
return new Promise(resolve => {
const img = new Image();
const start = performance.now();
let http3Supported = false;
// 尝试通过HTTP/3加载1x1像素图片
img.src = 'https://http3-test.example.com/pixel.png?cachebust=' + Math.random();
// 如果加载时间短,可能支持HTTP/3
img.onload = () => {
const loadTime = performance.now() - start;
http3Supported = loadTime < 50; // 假设50ms是阈值
resolve(http3Supported);
};
img.onerror = () => {
resolve(false);
};
// 如果超过1秒还没加载完,认为不支持
setTimeout(() => {
if (!http3Supported) {
resolve(false);
}
}, 1000);
});
}
📊 第二难:加载策略 - 资源优先级的"火眼金睛"
问题:加载顺序如何影响用户体验?如何只加载当前页面真正需要的资源?
深度技术:
网页加载是一场与时间赛跑的过程,而资源优先级策略决定了这场比赛的成败。关键在于理解渲染路径和资源加载对页面展示的影响,确保首屏关键内容最先加载完成。
现代浏览器提供了多种资源提示(Resource Hints)机制:preload(预加载)、prefetch(预获取)、preconnect(预连接)等,正确使用这些技术可以精确控制资源加载顺序和时机。同时,按需加载和代码分割(Code Splitting)策略也是优化大型应用加载性能的关键手段。
代码示例:
<!-- 1. 资源提示示例 -->
<!-- Preload - 当前页面必需的高优先级资源 -->
<link rel="preload" href="/fonts/awesome.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/critical.css" as="style">
<link rel="preload" href="/hero-image.jpg" as="image" media="(min-width: 600px)">
<!-- Prefetch - 可能在未来页面用到的资源 -->
<link rel="prefetch" href="/next-page.js">
<link rel="prefetch" href="/articles/popular.json">
<!-- Preconnect - 提前建立连接 -->
<link rel="preconnect" href="https://api.example.com">
<link rel="dns-prefetch" href="https://analytics.example.com">
<!-- 预渲染下一页 -->
<link rel="prerender" href="https://example.com/next-page">
<!-- 2. 关键CSS内联 -->
<style>
/* 首屏关键样式内联 */
.hero {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-color: #f0f0f0;
}
.hero-title {
font-size: 2.5rem;
font-weight: bold;
color: #333;
}
/* 其他首屏必需样式... */
</style>
<!-- 异步加载非关键CSS -->
<link rel="preload" href="/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/main.css"></noscript>
<!-- 3. 脚本加载策略 -->
<!-- 延迟执行,不阻塞解析 -->
<script src="/analytics.js" defer></script>
<!-- 异步加载,不阻塞解析,下载完立即执行 -->
<script src="/widget.js" async></script>
<!-- 模块脚本,自动延迟 -->
<script type="module" src="/app.js"></script>
<script nomodule src="/app-legacy.js"></script>
<!-- 内联关键脚本 -->
<script>
// 初始化首屏必需功能
document.addEventListener('DOMContentLoaded', () => {
initCriticalFeatures();
});
</script>
<!-- 4. 图片资源策略 -->
<!-- 响应式图片 -->
<picture>
<source srcset="/hero-large.webp 1200w, /hero-medium.webp 800w" type="image/webp">
<source srcset="/hero-large.jpg 1200w, /hero-medium.jpg 800w" type="image/jpeg">
<img src="/hero-fallback.jpg" alt="Hero image" loading="eager" width="1200" height="600">
</picture>
<!-- 延迟加载非首屏图片 -->
<img src="placeholder.svg" data-src="product.jpg" alt="Product" loading="lazy" class="lazyload">
<!-- 5. JavaScript实现资源优先级管理 -->
<script>
// 动态插入预加载
function preloadCriticalAssets() {
const assets = [
{ href: '/app.js', as: 'script' },
{ href: '/logo.svg', as: 'image' }
];
assets.forEach(asset => {
const link = document.createElement('link');
link.rel = 'preload';
link.href = asset.href;
link.as = asset.as;
if (asset.as === 'font') {
link.crossOrigin = 'anonymous';
}
document.head.appendChild(link);
});
}
// 根据路由预测和预加载
function predictNextNavigation() {
// 分析用户行为,预测下一个导航目标
const links = Array.from(document.querySelectorAll('a'));
const visibleLinks = links.filter(link => {
const rect = link.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= window.innerHeight &&
rect.right <= window.innerWidth
);
});
// 为可视区域内的链接添加prefetch
visibleLinks.forEach(link => {
const href = link.getAttribute('href');
if (href && href.startsWith('/') && !link.dataset.prefetched) {
const prefetchLink = document.createElement('link');
prefetchLink.rel = 'prefetch';
prefetchLink.href = href;
document.head.appendChild(prefetchLink);
link.dataset.prefetched = 'true';
}
});
}
// 优先级队列实现
class ResourcePriorityQueue {
constructor() {
this.highPriority = [];
this.mediumPriority = [];
this.lowPriority = [];
this.loaded = new Set();
}
add(resource, priority = 'medium') {
if (this.loaded.has(resource.url)) return;
switch(priority) {
case 'high':
this.highPriority.push(resource);
break;
case 'low':
this.lowPriority.push(resource);
break;
default:
this.mediumPriority.push(resource);
}
}
processNext() {
const resource = this.highPriority.shift() ||
this.mediumPriority.shift() ||
this.lowPriority.shift();
if (!resource) return Promise.resolve();
return this.loadResource(resource)
.then(() => {
this.loaded.add(resource.url);
return this.processNext();
})
.catch(err => {
console.error(`Failed to load ${resource.url}:`, err);
return this.processNext();
});
}
loadResource(resource) {
// 根据资源类型选择加载策略
switch(resource.type) {
case 'script':
return this.loadScript(resource.url, resource.async);
case 'style':
return this.loadStyle(resource.url);
case 'image':
return this.loadImage(resource.url);
default:
return this.loadGeneric(resource.url);
}
}
// 各种资源加载实现...
loadScript(url, async = false) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = url;
script.async = async;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
loadStyle(url) {
return new Promise((resolve, reject) => {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = url;
link.onload = resolve;
link.onerror = reject;
document.head.appendChild(link);
});
}
// 其他加载方法...
}
// 使用示例
document.addEventListener('DOMContentLoaded', () => {
const queue = new ResourcePriorityQueue();
// 添加各种优先级的资源
queue.add({ url: '/critical.js', type: 'script' }, 'high');
queue.add({ url: '/menu.js', type: 'script' }, 'medium');
queue.add({ url: '/analytics.js', type: 'script' }, 'low');
// 开始处理队列
queue.processNext();
// 视口可见性变化时处理低优先级资源
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
queue.processNext();
}
});
});
</script>
🗄️ 第三难:缓存控制 - 浏览器存储的"八卦炉"
问题:如何合理利用浏览器缓存加速页面访问?不同缓存策略在什么场景下最有效?
深度技术:
浏览器缓存是性能优化的强大工具,正确的缓存策略可以显著减少网络请求,加速页面加载。前端缓存策略需要考虑多个层面:HTTP缓存(通过Cache-Control和ETag控制)、ServiceWorker离线缓存、以及浏览器存储API(LocalStorage、SessionStorage、IndexedDB)。
设计缓存策略的关键在于平衡缓存时效性和命中率:频繁变化的内容需要短缓存或验证缓存,而静态资源则可使用长缓存配合内容哈希。理解不同缓存机制的特点和适用场景,才能构建出最优的缓存架构。
代码示例:
// 1. HTTP缓存控制
// nginx服务器配置
server {
// 长缓存静态资源(带版本号或哈希的文件)
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
etag off;
access_log off;
}
// HTML文件使用验证缓存
location ~* \.html$ {
add_header Cache-Control "no-cache";
add_header ETag ""; // 启用ETag
}
// API响应不缓存
location /api/ {
add_header Cache-Control "no-store";
add_header Pragma "no-cache";
}
}
// Express服务器配置
app.use('/static', express.static('public', {
maxAge: '1y',
etag: false,
lastModified: false,
setHeaders: (res, path) => {
if (path.includes('chunk.') || path.includes('vendor.')) {
// 包含哈希的文件使用不变缓存
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
}
}
}));
// 2. Service Worker缓存
// sw.js - Service Worker文件
const CACHE_NAME = 'app-cache-v1';
const urlsToCache = [
'/',
'/index.html',
'/styles/main.css',
'/scripts/main.js',
'/images/logo.png'
];
// 安装Service Worker
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
// 缓存优先,网络回退策略
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
// 缓存命中
if (response) {
return response;
}
// 缓存未命中,发起网络请求
return fetch(event.request).then(
networkResponse => {
// 检查是否为有效响应
if (!networkResponse || networkResponse.status !== 200 || networkResponse.type !== 'basic') {
return networkResponse;
}
// 复制响应,因为响应流只能被读取一次
const responseToCache = networkResponse.clone();
caches.open(CACHE_NAME)
.then(cache => {
// 只缓存同源资源
if (new URL(event.request.url).origin === location.origin) {
cache.put(event.request, responseToCache);
}
});
return networkResponse;
}
);
})
);
});
// 更新Service Worker时清理旧缓存
self.addEventListener('activate', event => {
const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});
// 注册Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
})
.catch(error => {
console.log('ServiceWorker registration failed: ', error);
});
});
}
// 3. 浏览器存储API
// LocalStorage - 持久化简单数据
const StorageManager = {
// 设置带过期时间的缓存项
setItem(key, value, expiryInMinutes = 60) {
const item = {
value,
expiry: expiryInMinutes ? Date.now() + (expiryInMinutes * 60000) : null
};
localStorage.setItem(key, JSON.stringify(item));
},
// 获取数据,自动检查是否过期
getItem(key) {
const itemStr = localStorage.getItem(key);
if (!itemStr) return null;
try {
const item = JSON.parse(itemStr);
if (item.expiry && Date.now() > item.expiry) {
localStorage.removeItem(key);
return null;
}
return item.value;
} catch (e) {
console.error('Error parsing storage item:', e);
return null;
}
},
// 移除数据
removeItem(key) {
localStorage.removeItem(key);
},
// 清理所有过期项
clearExpired() {
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
this.getItem(key); // 会自动检查和清理过期项
}
}
};
// 使用示例
StorageManager.setItem('user-preferences', { theme: 'dark', fontSize: 'large' }, 1440); // 24小时过期
const preferences = StorageManager.getItem('user-preferences');
// 4. IndexedDB - 存储大量结构化数据
class IndexedDBStorage {
constructor(dbName, version) {
this.dbName = dbName;
this.version = version;
this.db = null;
}
open() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, this.version);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve(this.db);
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
// 创建存储对象
if (!db.objectStoreNames.contains('articles')) {
const store = db.createObjectStore('articles', { keyPath: 'id' });
store.createIndex('by_date', 'date');
}
};
});
}
// 保存文章
saveArticle(article) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction(['articles'], 'readwrite');
const store = transaction.objectStore('articles');
// 添加或更新文章
const request = store.put({
...article,
cacheDate: new Date()
});
request.onsuccess = () => resolve(article);
request.onerror = () => reject(request.error);
});
}
// 获取文章
getArticle(id) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction(['articles']);
const store = transaction.objectStore('articles');
const request = store.get(id);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
// 获取符合日期范围的文章
getArticlesByDateRange(startDate, endDate) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction(['articles']);
const store = transaction.objectStore('articles');
const index = store.index('by_date');
const range = IDBKeyRange.bound(startDate, endDate);
const request = index.openCursor(range);
const articles = [];
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
articles.push(cursor.value);
cursor.continue();
} else {
resolve(articles);
}
};
request.onerror = () => reject(request.error);
});
}
}
// 使用示例
async function cacheArticles() {
const db = new IndexedDBStorage('content-db', 1);
await db.open();
try {
// 获取文章并缓存
const response = await fetch('/api/articles');
const articles = await response.json();
for (const article of articles) {
await db.saveArticle(article);
}
console.log('All articles cached successfully');
} catch (error) {
console.error('Error caching articles:', error);
}
}
// 5. 高级缓存策略实现
class CacheStrategy {
constructor() {
this.strategies = {
cacheFirst: this.cacheFirst.bind(this),
networkFirst: this.networkFirst.bind(this),
staleWhileRevalidate: this.staleWhileRevalidate.bind(this)
};
}
// 缓存优先,适用于不经常变化的资源
async cacheFirst(url, cacheName) {
const cache = await caches.open(cacheName);
const cachedResponse = await cache.match(url);
if (cachedResponse) {
return cachedResponse;
}
const networkResponse = await fetch(url);
cache.put(url, networkResponse.clone());
return networkResponse;
}
// 网络优先,适用于经常更新的内容
async networkFirst(url, cacheName, timeoutMs = 3000) {
const cache = await caches.open(cacheName);
try {
// 设置超时,避免网络请求过长
const networkPromise = fetch(url);
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Network timeout')), timeoutMs);
});
// 竞态Promise,哪个先完成用哪个
const networkResponse = await Promise.race([networkPromise, timeoutPromise]);
cache.put(url, networkResponse.clone());
return networkResponse;
} catch (error) {
const cachedResponse = await cache.match(url);
if (cachedResponse) {
return cachedResponse;
}
throw error;
}
}
// 边用缓存边更新,平衡速度和新鲜度
async staleWhileRevalidate(url, cacheName) {
const cache = await caches.open(cacheName);
// 立即返回缓存(如果有)
const cachedResponse = await cache.match(url);
// 无论是否有缓存,都触发网络更新
const updatePromise = fetch(url).then(networkResponse => {
cache.put(url, networkResponse.clone());
return networkResponse;
});
// 如果有缓存就返回缓存,否则等待网络请求
return cachedResponse || updatePromise;
}
// 根据URL选择合适的策略
async fetch(url, options = {}) {
const { strategy = 'networkFirst', cacheName = 'default-cache' } = options;
if (!this.strategies[strategy]) {
throw new Error(`Unknown cache strategy: ${strategy}`);
}
return this.strategies[strategy](url, cacheName);
}
}
// 使用示例
const cacheManager = new CacheStrategy();
// 对API请求使用网络优先策略
async function fetchUserData(userId) {
try {
const response = await cacheManager.fetch(`/api/users/${userId}`, {
strategy: 'networkFirst',
cacheName: 'api-cache'
});
return response.json();
} catch (error) {
console.error('Failed to fetch user data:', error);
return null;
}
}
// 对静态资源使用缓存优先策略
async function fetchStaticContent(path) {
try {
const response = await cacheManager.fetch(`/static/${path}`, {
strategy: 'cacheFirst',
cacheName: 'static-cache'
});
return response;
} catch (error) {
console.error(`Failed to fetch static content: ${path}`, error);
return null;
}
}
🖼️ 第四难:图片优化 - 下一代格式的"缩骨术"
问题:如何在不影响图片质量的前提下,显著减少图片体积?现代图片格式和优化技术能带来哪些提升?
深度技术:
图片优化是前端性能优化的重要环节,现代图片格式如WebP、AVIF等提供了更好的压缩率,而响应式图片技术则能根据设备特性提供最优图片。理解图片格式特性、压缩原理和加载策略,是提升页面性能的关键。
代码示例:
// 1. 响应式图片实现
<picture>
<!-- WebP格式,支持时优先使用 -->
<source
srcset="
/images/hero-400.webp 400w,
/images/hero-800.webp 800w,
/images/hero-1200.webp 1200w
"
type="image/webp"
sizes="(max-width: 400px) 400px,
(max-width: 800px) 800px,
1200px"
>
<!-- 传统格式回退 -->
<source
srcset="
/images/hero-400.jpg 400w,
/images/hero-800.jpg 800w,
/images/hero-1200.jpg 1200w
"
type="image/jpeg"
sizes="(max-width: 400px) 400px,
(max-width: 800px) 800px,
1200px"
>
<!-- 最终回退方案 -->
<img
src="/images/hero-400.jpg"
alt="Hero image"
loading="eager"
width="1200"
height="600"
>
</picture>
// 2. 图片懒加载实现
class LazyLoader {
constructor(options = {}) {
this.options = {
root: null,
rootMargin: '0px',
threshold: 0.1,
...options
};
this.observer = new IntersectionObserver(
this.handleIntersection.bind(this),
this.options
);
}
observe(elements) {
elements.forEach(element => {
if (element.dataset.src) {
this.observer.observe(element);
}
});
}
handleIntersection(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.loadImage(entry.target);
this.observer.unobserve(entry.target);
}
});
}
loadImage(element) {
const src = element.dataset.src;
if (!src) return;
// 创建新图片对象预加载
const img = new Image();
img.onload = () => {
element.src = src;
element.classList.add('loaded');
};
img.src = src;
}
}
// 3. 图片压缩和转换
const sharp = require('sharp');
async function optimizeImage(inputPath, outputPath, options = {}) {
const {
width,
height,
quality = 80,
format = 'webp'
} = options;
try {
let pipeline = sharp(inputPath);
// 调整尺寸
if (width || height) {
pipeline = pipeline.resize(width, height, {
fit: 'inside',
withoutEnlargement: true
});
}
// 根据格式转换
switch (format) {
case 'webp':
pipeline = pipeline.webp({ quality });
break;
case 'avif':
pipeline = pipeline.avif({ quality });
break;
case 'jpeg':
pipeline = pipeline.jpeg({ quality });
break;
default:
pipeline = pipeline.webp({ quality });
}
await pipeline.toFile(outputPath);
console.log(`Image optimized: ${outputPath}`);
} catch (error) {
console.error('Image optimization failed:', error);
}
}
// 4. 图片加载优化
class ImageLoader {
constructor() {
this.loadingQueue = [];
this.maxConcurrent = 3;
this.currentLoading = 0;
}
load(url, priority = 'normal') {
return new Promise((resolve, reject) => {
this.loadingQueue.push({
url,
priority,
resolve,
reject
});
this.processQueue();
});
}
processQueue() {
if (this.currentLoading >= this.maxConcurrent) return;
// 按优先级排序
this.loadingQueue.sort((a, b) => {
const priorityOrder = { high: 0, normal: 1, low: 2 };
return priorityOrder[a.priority] - priorityOrder[b.priority];
});
const next = this.loadingQueue.shift();
if (!next) return;
this.currentLoading++;
const img = new Image();
img.onload = () => {
this.currentLoading--;
next.resolve(img);
this.processQueue();
};
img.onerror = () => {
this.currentLoading--;
next.reject(new Error(`Failed to load image: ${next.url}`));
this.processQueue();
};
img.src = next.url;
}
}
// 5. 图片预加载策略
class ImagePreloader {
constructor() {
this.cache = new Map();
this.loader = new ImageLoader();
}
preload(urls, priority = 'low') {
return Promise.all(
urls.map(url => {
if (this.cache.has(url)) {
return Promise.resolve(this.cache.get(url));
}
return this.loader.load(url, priority).then(img => {
this.cache.set(url, img);
return img;
});
})
);
}
// 预测用户可能查看的图片
predictNextImages(currentPath) {
const predictions = {
'/': ['/images/hero.webp', '/images/featured-1.webp'],
'/products': ['/images/product-list.webp'],
'/about': ['/images/team.webp']
};
return predictions[currentPath] || [];
}
}
// 使用示例
const preloader = new ImagePreloader();
// 预加载当前页面图片
document.addEventListener('DOMContentLoaded', () => {
const currentImages = Array.from(document.querySelectorAll('img[data-src]'))
.map(img => img.dataset.src);
preloader.preload(currentImages, 'high');
// 预加载可能的下一个页面图片
const nextImages = preloader.predictNextImages(window.location.pathname);
preloader.preload(nextImages, 'low');
});
🧠 第五难:JavaScript性能 - 内存管理的"收心法"
问题:如何避免内存泄漏?如何优化JavaScript执行性能?大型应用如何保持流畅运行?
深度技术:
JavaScript性能优化涉及多个层面:内存管理、执行效率、代码分割等。理解V8引擎的工作原理、垃圾回收机制,以及如何编写高性能的JavaScript代码,是提升应用性能的关键。
代码示例:
// 1. 内存泄漏检测
class MemoryLeakDetector {
constructor() {
this.snapshots = [];
this.interval = null;
}
start(intervalMs = 10000) {
this.interval = setInterval(() => {
this.takeSnapshot();
}, intervalMs);
}
stop() {
if (this.interval) {
clearInterval(this.interval);
this.interval = null;
}
}
takeSnapshot() {
const snapshot = {
timestamp: Date.now(),
heapSize: performance.memory?.usedJSHeapSize,
domNodes: document.getElementsByTagName('*').length,
eventListeners: this.countEventListeners()
};
this.snapshots.push(snapshot);
this.analyzeSnapshots();
}
countEventListeners() {
// 获取所有元素的事件监听器数量
const elements = document.getElementsByTagName('*');
let count = 0;
for (const element of elements) {
const listeners = getEventListeners(element);
count += Object.keys(listeners).length;
}
return count;
}
analyzeSnapshots() {
if (this.snapshots.length < 2) return;
const current = this.snapshots[this.snapshots.length - 1];
const previous = this.snapshots[this.snapshots.length - 2];
const heapGrowth = current.heapSize - previous.heapSize;
const domGrowth = current.domNodes - previous.domNodes;
const listenerGrowth = current.eventListeners - previous.eventListeners;
if (heapGrowth > 1000000 || domGrowth > 100 || listenerGrowth > 50) {
console.warn('Potential memory leak detected:', {
heapGrowth: `${(heapGrowth / 1024 / 1024).toFixed(2)}MB`,
domGrowth,
listenerGrowth
});
}
}
}
// 2. 性能优化工具类
class PerformanceOptimizer {
// 防抖函数
debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// 节流函数
throttle(func, limit) {
let inThrottle;
return function executedFunction(...args) {
if (!inThrottle) {
func(...args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// 批量处理
batchProcess(items, processFn, batchSize = 100) {
return new Promise((resolve) => {
let index = 0;
function processBatch() {
const batch = items.slice(index, index + batchSize);
if (batch.length === 0) {
resolve();
return;
}
Promise.all(batch.map(processFn))
.then(() => {
index += batchSize;
setTimeout(processBatch, 0);
});
}
processBatch();
});
}
// 虚拟列表实现
createVirtualList(container, items, itemHeight, renderItem) {
const visibleItems = Math.ceil(container.clientHeight / itemHeight);
const totalHeight = items.length * itemHeight;
container.style.position = 'relative';
container.style.height = `${totalHeight}px`;
const visibleRange = {
start: 0,
end: visibleItems
};
function updateVisibleItems() {
const scrollTop = container.scrollTop;
const start = Math.floor(scrollTop / itemHeight);
const end = Math.min(start + visibleItems + 1, items.length);
if (start !== visibleRange.start || end !== visibleRange.end) {
visibleRange.start = start;
visibleRange.end = end;
// 清除现有内容
container.innerHTML = '';
// 渲染可见项
for (let i = start; i < end; i++) {
const item = renderItem(items[i], i);
item.style.position = 'absolute';
item.style.top = `${i * itemHeight}px`;
item.style.height = `${itemHeight}px`;
container.appendChild(item);
}
}
}
container.addEventListener('scroll', this.throttle(updateVisibleItems, 16));
updateVisibleItems();
}
}
// 3. 代码分割和懒加载
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
maxInitialRequests: 10,
maxAsyncRequests: 10,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
};
// 动态导入示例
async function loadModule(moduleName) {
try {
const module = await import(`./modules/${moduleName}.js`);
return module;
} catch (error) {
console.error(`Failed to load module: ${moduleName}`, error);
return null;
}
}
// 4. 性能监控
class PerformanceMonitor {
constructor() {
this.metrics = {};
this.observers = new Map();
}
startMonitoring() {
// 监控长任务
this.observeLongTasks();
// 监控资源加载
this.observeResourceTiming();
// 监控布局变化
this.observeLayoutShifts();
// 监控内存使用
this.observeMemoryUsage();
}
observeLongTasks() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 50) {
console.warn('Long task detected:', entry);
}
}
});
observer.observe({ entryTypes: ['longtask'] });
this.observers.set('longtask', observer);
}
observeResourceTiming() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.initiatorType === 'script' && entry.duration > 1000) {
console.warn('Slow script loading:', entry);
}
}
});
observer.observe({ entryTypes: ['resource'] });
this.observers.set('resource', observer);
}
observeLayoutShifts() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.value > 0.1) {
console.warn('Layout shift detected:', entry);
}
}
});
observer.observe({ entryTypes: ['layout-shift'] });
this.observers.set('layout-shift', observer);
}
observeMemoryUsage() {
if (performance.memory) {
setInterval(() => {
const used = performance.memory.usedJSHeapSize;
const total = performance.memory.totalJSHeapSize;
const limit = performance.memory.jsHeapSizeLimit;
if (used / limit > 0.9) {
console.warn('High memory usage detected:', {
used: `${(used / 1024 / 1024).toFixed(2)}MB`,
total: `${(total / 1024 / 1024).toFixed(2)}MB`,
limit: `${(limit / 1024 / 1024).toFixed(2)}MB`
});
}
}, 5000);
}
}
stopMonitoring() {
for (const [type, observer] of this.observers) {
observer.disconnect();
}
this.observers.clear();
}
}
// 使用示例
const optimizer = new PerformanceOptimizer();
const monitor = new PerformanceMonitor();
// 启动性能监控
monitor.startMonitoring();
// 使用防抖处理搜索输入
const searchInput = document.querySelector('#search');
searchInput.addEventListener('input', optimizer.debounce((e) => {
// 处理搜索逻辑
}, 300));
// 使用节流处理滚动事件
window.addEventListener('scroll', optimizer.throttle(() => {
// 处理滚动逻辑
}, 16));
// 批量处理大量数据
const items = Array.from({ length: 10000 }, (_, i) => i);
optimizer.batchProcess(items, async (item) => {
// 处理单个项目
}, 100);
🎨 第六难:CSS性能 - 选择器与动画的"抱火诀"
问题:如何优化CSS选择器性能?如何实现高性能的动画效果?如何避免重排重绘?
深度技术:
CSS性能优化涉及选择器效率、动画性能、渲染优化等多个方面。理解浏览器渲染原理、CSS选择器匹配规则,以及如何编写高性能的CSS代码,是提升页面渲染性能的关键。
代码示例:
/* 1. 高性能选择器 */
/* 避免使用通配符选择器 */
* { margin: 0; padding: 0; } /* 不推荐 */
/* 避免使用标签选择器 */
div { margin: 10px; } /* 不推荐 */
/* 使用类选择器 */
.container { margin: 10px; } /* 推荐 */
/* 避免过度嵌套 */
.nav ul li a { color: blue; } /* 不推荐 */
.nav-link { color: blue; } /* 推荐 */
/* 2. 高性能动画 */
/* 使用transform和opacity进行动画 */
.animate {
/* 不推荐 */
left: 0;
transition: left 0.3s ease;
}
.animate {
/* 推荐 */
transform: translateX(0);
transition: transform 0.3s ease;
}
/* 使用will-change提示浏览器 */
.animate {
will-change: transform;
transform: translateX(0);
transition: transform 0.3s ease;
}
/* 3. 避免重排重绘 */
/* 批量修改DOM */
function batchUpdate() {
// 不推荐
element.style.width = '100px';
element.style.height = '100px';
element.style.margin = '10px';
// 推荐
element.style.cssText = `
width: 100px;
height: 100px;
margin: 10px;
`;
}
/* 使用DocumentFragment */
function createList(items) {
const fragment = document.createDocumentFragment();
items.forEach(item => {
const li = document.createElement('li');
li.textContent = item;
fragment.appendChild(li);
});
document.querySelector('ul').appendChild(fragment);
}
/* 4. CSS性能优化工具 */
class CSSOptimizer {
constructor() {
this.rules = new Map();
}
// 分析选择器性能
analyzeSelector(selector) {
const complexity = this.calculateSelectorComplexity(selector);
const specificity = this.calculateSpecificity(selector);
return {
complexity,
specificity,
performance: this.evaluatePerformance(complexity, specificity)
};
}
calculateSelectorComplexity(selector) {
// 计算选择器复杂度
const parts = selector.split(/\s+/);
return parts.length;
}
calculateSpecificity(selector) {
// 计算选择器特异性
let a = 0, b = 0, c = 0;
// ID选择器
a = (selector.match(/#/g) || []).length;
// 类选择器、属性选择器、伪类
b = (selector.match(/\.|\[|:/g) || []).length;
// 元素选择器、伪元素
c = (selector.match(/^[a-zA-Z]|::/g) || []).length;
return { a, b, c };
}
evaluatePerformance(complexity, specificity) {
// 评估选择器性能
const score = complexity * 0.4 + (specificity.a + specificity.b + specificity.c) * 0.6;
if (score < 2) return 'excellent';
if (score < 4) return 'good';
if (score < 6) return 'fair';
return 'poor';
}
// 优化动画性能
optimizeAnimation(element, properties) {
const optimized = {};
for (const [prop, value] of Object.entries(properties)) {
if (prop === 'transform' || prop === 'opacity') {
optimized[prop] = value;
} else {
console.warn(`Avoid animating ${prop}, use transform instead`);
}
}
return optimized;
}
// 检测重排重绘
detectReflow(element) {
const originalStyle = window.getComputedStyle(element);
return {
before: () => {
this.originalLayout = element.getBoundingClientRect();
},
after: () => {
const newLayout = element.getBoundingClientRect();
const newStyle = window.getComputedStyle(element);
const hasReflow = this.originalLayout.width !== newLayout.width ||
this.originalLayout.height !== newLayout.height;
const hasRepaint = originalStyle.color !== newStyle.color ||
originalStyle.backgroundColor !== newStyle.backgroundColor;
return { hasReflow, hasRepaint };
}
};
}
}
// 使用示例
const optimizer = new CSSOptimizer();
// 分析选择器性能
const selectorAnalysis = optimizer.analyzeSelector('.nav > ul > li > a');
console.log('Selector performance:', selectorAnalysis);
// 优化动画
const animation = optimizer.optimizeAnimation(element, {
transform: 'translateX(100px)',
opacity: 0.5,
width: '200px' // 不推荐
});
// 检测重排重绘
const reflowDetector = optimizer.detectReflow(element);
reflowDetector.before();
// 执行可能引起重排重绘的操作
const { hasReflow, hasRepaint } = reflowDetector.after();
if (hasReflow) console.warn('Layout reflow detected');
if (hasRepaint) console.warn('Repaint detected');
📝 第七难:字体加载 - FOUT与FOIT的"隐身术"
问题:如何优化字体加载体验?如何避免字体闪烁?如何实现平滑的字体切换?
深度技术:
字体加载优化是提升用户体验的重要环节。理解FOUT(Flash of Unstyled Text)和FOIT(Flash of Invisible Text)的区别,以及如何控制字体加载行为,是优化字体显示效果的关键。
代码示例:
// 1. 字体加载策略
class FontLoader {
constructor(options = {}) {
this.options = {
display: 'swap',
timeout: 3000,
...options
};
this.loadedFonts = new Set();
}
// 加载字体
loadFont(fontFamily, urls) {
if (this.loadedFonts.has(fontFamily)) {
return Promise.resolve();
}
const fontFaceSet = new FontFaceSet();
const fontFaces = urls.map(url => {
const format = url.split('.').pop();
return new FontFace(fontFamily, `url(${url})`, {
style: 'normal',
weight: '400',
display: this.options.display
});
});
return Promise.all(fontFaces.map(face => face.load()))
.then(loadedFaces => {
loadedFaces.forEach(face => {
document.fonts.add(face);
});
this.loadedFonts.add(fontFamily);
})
.catch(error => {
console.error(`Failed to load font ${fontFamily}:`, error);
});
}
// 预加载字体
preloadFonts(fonts) {
const links = fonts.map(font => {
const link = document.createElement('link');
link.rel = 'preload';
link.href = font.url;
link.as = 'font';
link.crossOrigin = 'anonymous';
return link;
});
links.forEach(link => document.head.appendChild(link));
}
// 监控字体加载
observeFontLoading() {
const observer = new FontFaceObserver('Custom Font');
observer.load(null, this.options.timeout)
.then(() => {
document.documentElement.classList.add('fonts-loaded');
})
.catch(() => {
document.documentElement.classList.add('fonts-failed');
});
}
}
// 2. 字体显示控制
class FontDisplayController {
constructor() {
this.fontDisplay = new Map();
}
// 设置字体显示策略
setFontDisplay(fontFamily, display) {
this.fontDisplay.set(fontFamily, display);
const style = document.createElement('style');
style.textContent = `
@font-face {
font-family: ${fontFamily};
font-display: ${display};
}
`;
document.head.appendChild(style);
}
// 获取字体加载状态
getFontStatus(fontFamily) {
return document.fonts.check(`12px "${fontFamily}"`);
}
// 监听字体加载完成
onFontLoaded(fontFamily, callback) {
if (this.getFontStatus(fontFamily)) {
callback();
return;
}
document.fonts.ready.then(() => {
if (this.getFontStatus(fontFamily)) {
callback();
}
});
}
}
// 3. 字体回退策略
class FontFallback {
constructor() {
this.fallbacks = new Map();
}
// 设置字体回退链
setFallback(fontFamily, fallbackChain) {
this.fallbacks.set(fontFamily, fallbackChain);
const style = document.createElement('style');
style.textContent = `
.${fontFamily} {
font-family: ${fallbackChain.join(', ')};
}
`;
document.head.appendChild(style);
}
// 应用字体回退
applyFallback(element, fontFamily) {
const fallbackChain = this.fallbacks.get(fontFamily);
if (fallbackChain) {
element.style.fontFamily = fallbackChain.join(', ');
}
}
}
// 使用示例
const fontLoader = new FontLoader({
display: 'swap',
timeout: 3000
});
const displayController = new FontDisplayController();
const fontFallback = new FontFallback();
// 加载自定义字体
fontLoader.loadFont('Custom Font', [
'/fonts/custom-font.woff2',
'/fonts/custom-font.woff'
]);
// 设置字体显示策略
displayController.setFontDisplay('Custom Font', 'swap');
// 设置字体回退
fontFallback.setFallback('Custom Font', [
'Custom Font',
'Arial',
'sans-serif'
]);
// 监听字体加载
displayController.onFontLoaded('Custom Font', () => {
document.body.classList.add('custom-font-loaded');
});
// 预加载字体
fontLoader.preloadFonts([
{ url: '/fonts/custom-font.woff2' }
]);
// 监控字体加载
fontLoader.observeFontLoading();
🔮 第八难:预加载技术 - Prefetch与Preload的"千里眼"
问题:如何预测用户行为并预加载资源?如何平衡预加载与性能消耗?如何实现智能预加载策略?
深度技术:
预加载技术是提升用户体验的重要手段,通过预测用户行为并提前加载资源,可以显著减少等待时间。理解不同预加载策略的特点和适用场景,是优化资源加载效率的关键。
代码示例:
// 1. 预加载管理器
class PreloadManager {
constructor() {
this.prefetchQueue = new Set();
this.preloadQueue = new Set();
this.maxConcurrent = 3;
this.currentLoading = 0;
}
// 预获取资源
prefetch(url, options = {}) {
if (this.prefetchQueue.has(url)) return;
this.prefetchQueue.add(url);
this.processPrefetchQueue();
}
// 预加载资源
preload(url, options = {}) {
if (this.preloadQueue.has(url)) return;
this.preloadQueue.add(url);
this.processPreloadQueue();
}
// 处理预获取队列
processPrefetchQueue() {
if (this.currentLoading >= this.maxConcurrent) return;
const url = Array.from(this.prefetchQueue)[0];
if (!url) return;
this.prefetchQueue.delete(url);
this.currentLoading++;
const link = document.createElement('link');
link.rel = 'prefetch';
link.href = url;
link.onload = () => {
this.currentLoading--;
this.processPrefetchQueue();
};
link.onerror = () => {
this.currentLoading--;
this.processPrefetchQueue();
};
document.head.appendChild(link);
}
// 处理预加载队列
processPreloadQueue() {
if (this.currentLoading >= this.maxConcurrent) return;
const url = Array.from(this.preloadQueue)[0];
if (!url) return;
this.preloadQueue.delete(url);
this.currentLoading++;
const link = document.createElement('link');
link.rel = 'preload';
link.href = url;
link.as = this.getResourceType(url);
link.onload = () => {
this.currentLoading--;
this.processPreloadQueue();
};
link.onerror = () => {
this.currentLoading--;
this.processPreloadQueue();
};
document.head.appendChild(link);
}
// 获取资源类型
getResourceType(url) {
const extension = url.split('.').pop().toLowerCase();
switch (extension) {
case 'js':
return 'script';
case 'css':
return 'style';
case 'jpg':
case 'jpeg':
case 'png':
case 'gif':
case 'webp':
return 'image';
case 'woff':
case 'woff2':
case 'ttf':
case 'otf':
return 'font';
default:
return 'fetch';
}
}
}
// 2. 智能预加载
class SmartPreloader {
constructor() {
this.preloadManager = new PreloadManager();
this.userBehavior = new Map();
this.threshold = 0.7;
}
// 记录用户行为
recordBehavior(action, target) {
const key = `${action}-${target}`;
this.userBehavior.set(key, (this.userBehavior.get(key) || 0) + 1);
}
// 预测下一个动作
predictNextAction(currentAction) {
const predictions = new Map();
for (const [key, count] of this.userBehavior) {
const [action, target] = key.split('-');
if (action === currentAction) {
predictions.set(target, count);
}
}
return Array.from(predictions.entries())
.filter(([_, count]) => count > this.threshold)
.sort((a, b) => b[1] - a[1])
.map(([target]) => target);
}
// 预加载预测资源
preloadPredictedResources(currentAction) {
const predictedTargets = this.predictNextAction(currentAction);
predictedTargets.forEach(target => {
const resources = this.getResourcesForTarget(target);
resources.forEach(resource => {
this.preloadManager.preload(resource);
});
});
}
// 获取目标相关资源
getResourcesForTarget(target) {
// 根据目标返回需要预加载的资源
const resourceMap = {
'home': ['/styles/home.css', '/scripts/home.js'],
'products': ['/styles/products.css', '/scripts/products.js'],
'about': ['/styles/about.css', '/scripts/about.js']
};
return resourceMap[target] || [];
}
}
// 3. 路由预加载
class RoutePreloader {
constructor() {
this.preloadManager = new PreloadManager();
this.routes = new Map();
}
// 注册路由
registerRoute(path, resources) {
this.routes.set(path, resources);
}
// 预加载路由资源
preloadRoute(path) {
const resources = this.routes.get(path);
if (resources) {
resources.forEach(resource => {
this.preloadManager.preload(resource);
});
}
}
// 监听路由变化
observeRouteChanges() {
let lastPath = window.location.pathname;
// 监听点击事件
document.addEventListener('click', (event) => {
const link = event.target.closest('a');
if (link && link.href.startsWith(window.location.origin)) {
const path = new URL(link.href).pathname;
this.preloadRoute(path);
}
});
// 监听路由变化
window.addEventListener('popstate', () => {
const currentPath = window.location.pathname;
if (currentPath !== lastPath) {
this.preloadRoute(currentPath);
lastPath = currentPath;
}
});
}
}
// 使用示例
const preloadManager = new PreloadManager();
const smartPreloader = new SmartPreloader();
const routePreloader = new RoutePreloader();
// 预获取资源
preloadManager.prefetch('/images/hero.jpg');
preloadManager.prefetch('/styles/main.css');
// 预加载资源
preloadManager.preload('/scripts/main.js');
preloadManager.preload('/fonts/custom-font.woff2');
// 记录用户行为
smartPreloader.recordBehavior('click', 'products');
smartPreloader.recordBehavior('click', 'about');
// 预加载预测资源
smartPreloader.preloadPredictedResources('click');
// 注册路由
routePreloader.registerRoute('/products', [
'/styles/products.css',
'/scripts/products.js'
]);
// 监听路由变化
routePreloader.observeRouteChanges();
📊 第九难:性能监控 - 前端埋点的"顺风耳"
问题:如何全面监控前端性能?如何收集和分析性能数据?如何建立性能监控体系?
深度技术:
性能监控是优化前端性能的基础,通过收集和分析性能数据,可以发现问题并持续改进。理解性能指标、监控方法和数据分析技术,是建立完整性能监控体系的关键。
代码示例:
// 1. 性能监控器
class PerformanceMonitor {
constructor() {
this.metrics = {};
this.observers = new Map();
}
// 监控核心性能指标
monitorCoreWebVitals() {
// 监控LCP (Largest Contentful Paint)
this.observeLCP();
// 监控FID (First Input Delay)
this.observeFID();
// 监控CLS (Cumulative Layout Shift)
this.observeCLS();
}
// 监控LCP
observeLCP() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
this.metrics.lcp = entry.startTime;
this.reportMetric('lcp', entry.startTime);
}
});
observer.observe({ entryTypes: ['largest-contentful-paint'] });
this.observers.set('lcp', observer);
}
// 监控FID
observeFID() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
this.metrics.fid = entry.processingStart - entry.startTime;
this.reportMetric('fid', this.metrics.fid);
}
});
observer.observe({ entryTypes: ['first-input'] });
this.observers.set('fid', observer);
}
// 监控CLS
observeCLS() {
let clsValue = 0;
let clsEntries = [];
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
clsValue += entry.value;
clsEntries.push(entry);
}
}
this.metrics.cls = clsValue;
this.reportMetric('cls', clsValue);
});
observer.observe({ entryTypes: ['layout-shift'] });
this.observers.set('cls', observer);
}
// 监控资源加载
monitorResourceLoading() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.initiatorType === 'script' && entry.duration > 1000) {
this.reportMetric('slow-script', {
url: entry.name,
duration: entry.duration
});
}
}
});
observer.observe({ entryTypes: ['resource'] });
this.observers.set('resource', observer);
}
// 监控长任务
monitorLongTasks() {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 50) {
this.reportMetric('long-task', {
duration: entry.duration,
startTime: entry.startTime
});
}
}
});
observer.observe({ entryTypes: ['longtask'] });
this.observers.set('longtask', observer);
}
// 监控内存使用
monitorMemoryUsage() {
if (performance.memory) {
setInterval(() => {
const used = performance.memory.usedJSHeapSize;
const total = performance.memory.totalJSHeapSize;
const limit = performance.memory.jsHeapSizeLimit;
this.reportMetric('memory', {
used,
total,
limit
});
}, 5000);
}
}
// 报告性能指标
reportMetric(name, value) {
// 发送到性能监控服务
console.log(`Metric: ${name}`, value);
// 可以发送到后端API
// fetch('/api/metrics', {
// method: 'POST',
// body: JSON.stringify({ name, value })
// });
}
// 停止监控
stopMonitoring() {
for (const [type, observer] of this.observers) {
observer.disconnect();
}
this.observers.clear();
}
}
// 2. 性能数据收集器
class PerformanceCollector {
constructor() {
this.data = new Map();
}
// 收集性能指标
collectMetrics() {
// 收集导航计时
const navigation = performance.getEntriesByType('navigation')[0];
this.data.set('navigation', {
dns: navigation.domainLookupEnd - navigation.domainLookupStart,
tcp: navigation.connectEnd - navigation.connectStart,
request: navigation.responseEnd - navigation.requestStart,
dom: navigation.domComplete - navigation.domLoading,
load: navigation.loadEventEnd - navigation.loadEventStart
});
// 收集资源计时
const resources = performance.getEntriesByType('resource');
this.data.set('resources', resources.map(resource => ({
url: resource.name,
type: resource.initiatorType,
duration: resource.duration,
size: resource.transferSize
})));
// 收集用户计时
const userTiming = performance.getEntriesByType('measure');
this.data.set('userTiming', userTiming.map(measure => ({
name: measure.name,
duration: measure.duration
})));
}
// 收集错误信息
collectErrors() {
const errors = [];
window.addEventListener('error', (event) => {
errors.push({
type: 'error',
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
stack: event.error?.stack
});
});
window.addEventListener('unhandledrejection', (event) => {
errors.push({
type: 'unhandledrejection',
message: event.reason?.message || event.reason,
stack: event.reason?.stack
});
});
this.data.set('errors', errors);
}
// 收集用户行为
collectUserBehavior() {
const behavior = [];
// 记录点击事件
document.addEventListener('click', (event) => {
behavior.push({
type: 'click',
target: event.target.tagName,
timestamp: Date.now()
});
});
// 记录滚动事件
let scrollTimeout;
window.addEventListener('scroll', () => {
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(() => {
behavior.push({
type: 'scroll',
position: window.scrollY,
timestamp: Date.now()
});
}, 100);
});
this.data.set('behavior', behavior);
}
// 获取收集的数据
getData() {
return Object.fromEntries(this.data);
}
}
// 3. 性能分析器
class PerformanceAnalyzer {
constructor(data) {
this.data = data;
}
// 分析性能问题
analyze() {
const issues = [];
// 分析加载时间
const navigation = this.data.navigation;
if (navigation.dom > 1000) {
issues.push({
type: 'slow-dom',
message: 'DOM加载时间过长',
value: navigation.dom
});
}
// 分析资源加载
const resources = this.data.resources;
const slowResources = resources.filter(r => r.duration > 1000);
if (slowResources.length > 0) {
issues.push({
type: 'slow-resources',
message: '存在慢资源加载',
resources: slowResources
});
}
// 分析错误
const errors = this.data.errors;
if (errors.length > 0) {
issues.push({
type: 'errors',
message: '存在未处理的错误',
errors
});
}
return issues;
}
// 生成性能报告
generateReport() {
const issues = this.analyze();
return {
timestamp: Date.now(),
metrics: this.data,
issues,
recommendations: this.generateRecommendations(issues)
};
}
// 生成优化建议
generateRecommendations(issues) {
const recommendations = [];
issues.forEach(issue => {
switch (issue.type) {
case 'slow-dom':
recommendations.push({
type: 'dom',
message: '考虑优化DOM结构,减少DOM节点数量'
});
break;
case 'slow-resources':
recommendations.push({
type: 'resources',
message: '考虑使用CDN加速资源加载,或优化资源大小'
});
break;
case 'errors':
recommendations.push({
type: 'errors',
message: '建议添加错误监控和自动上报机制'
});
break;
}
});
return recommendations;
}
}
// 使用示例
const monitor = new PerformanceMonitor();
const collector = new PerformanceCollector();
const analyzer = new PerformanceAnalyzer(collector.getData());
// 启动性能监控
monitor.monitorCoreWebVitals();
monitor.monitorResourceLoading();
monitor.monitorLongTasks();
monitor.monitorMemoryUsage();
// 收集性能数据
collector.collectMetrics();
collector.collectErrors();
collector.collectUserBehavior();
// 分析性能问题
const report = analyzer.generateReport();
console.log('Performance Report:', report);
// 停止监控
monitor.stopMonitoring();
结语
通过这九大性能优化心经,我们从前端性能的各个维度进行了深入探讨。从网络传输到渲染优化,从资源加载到性能监控,每个环节都蕴含着提升用户体验的关键技术。希望这些实战技巧能帮助你在前端性能优化的道路上走得更远,打造出真正流畅、高效的用户体验。
记住,性能优化不是一蹴而就的,而是需要持续关注和改进的过程。让我们继续在前端性能优化的道路上探索前行,为用户创造更好的体验。