🧠 第一章:事件机制与事件循环
1.1 事件流的三阶段
javascript
// 事件流:捕获阶段 → 目标阶段 → 冒泡阶段
element.addEventListener('click', () => {
console.log('冒泡阶段执行');
}, false); // 默认false,冒泡阶段
element.addEventListener('click', () => {
console.log('捕获阶段执行');
}, true); // true,捕获阶段
执行顺序:
css
【捕获阶段】window → document → html → body → parent → target
【目标阶段】target上的监听器(按注册顺序)
【冒泡阶段】target → parent → body → html → document → window
1.2 事件代理(委托)
javascript
// ❌ 不好:给每个li绑定事件
document.querySelectorAll('li').forEach(li => {
li.addEventListener('click', () => console.log('点击'));
});
// ✅ 好:利用冒泡,一个事件搞定
document.querySelector('ul').addEventListener('click', (e) => {
if (e.target.tagName === 'LI') {
console.log('点击了', e.target.textContent);
}
});
优势:
- 减少内存占用
- 动态添加的元素也能响应
1.3 自定义事件
javascript
// 创建自定义事件
const event = new CustomEvent('userLogin', {
detail: { userId: 123, name: '张三' },
bubbles: true,
cancelable: true
});
// 触发事件
window.dispatchEvent(event);
// 监听
window.addEventListener('userLogin', (e) => {
console.log('用户登录:', e.detail);
});
1.4 事件循环(Event Loop)的微观细节
javascript
console.log('1'); // 同步
setTimeout(() => console.log('2'), 0); // 宏任务
Promise.resolve().then(() => {
console.log('3'); // 微任务
Promise.resolve().then(() => console.log('4')); // 微任务嵌套
});
queueMicrotask(() => console.log('5')); // 微任务
requestAnimationFrame(() => console.log('6')); // 渲染前执行
// 输出顺序:1, 3, 4, 5, 2, 6
任务优先级:
javascript
【微任务】Promise.then、MutationObserver、queueMicrotask
↓
【渲染】requestAnimationFrame、样式计算、布局、绘制
↓
【宏任务】setTimeout、setInterval、I/O、UI交互
↓
【微任务】...(下一轮循环)
- 微任务在当前宏任务结束后、下一个宏任务前执行
- requestAnimationFrame在渲染前执行
- 事件循环的每一轮 = 1个宏任务 + 所有微任务 + 可能的渲染
🎨 第二章:现代Web API与性能
2.1 Intersection Observer(交叉观察器)
javascript
// 监听元素是否进入视口
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('元素进入视口', entry.target);
// 懒加载图片
const img = entry.target;
img.src = img.dataset.src;
// 停止观察(加载后就不需要了)
observer.unobserve(img);
}
});
}, {
threshold: 0.5, // 50%可见时触发
rootMargin: '50px' // 提前50px触发
});
// 观察所有懒加载图片
document.querySelectorAll('img[data-src]').forEach(img => {
observer.observe(img);
});
应用场景:
- 图片懒加载
- 无限滚动
- 曝光埋点
- 广告可见性统计
2.2 Resize Observer(尺寸变化观察)
javascript
// 监听元素尺寸变化
const observer = new ResizeObserver((entries) => {
for (let entry of entries) {
const { width, height } = entry.contentRect;
console.log('尺寸变化:', width, height);
// 自适应布局
if (width < 600) {
entry.target.classList.add('mobile');
} else {
entry.target.classList.remove('mobile');
}
}
});
observer.observe(document.querySelector('.container'));
2.3 Mutation Observer(DOM变化观察)
javascript
// 监听DOM变化
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (mutation.type === 'childList') {
console.log('子节点变化:', mutation.addedNodes, mutation.removedNodes);
}
if (mutation.type === 'attributes') {
console.log('属性变化:', mutation.attributeName);
}
});
});
observer.observe(document.body, {
childList: true, // 子节点变化
attributes: true, // 属性变化
subtree: true, // 子树也观察
characterData: true // 文本变化
});
2.4 Performance API
javascript
// 性能监控
const perfData = {
// 导航计时
navigation: performance.getEntriesByType('navigation')[0],
// 资源加载
resources: performance.getEntriesByType('resource'),
// 关键时间点
timing: performance.timing,
// 内存信息(Chrome only)
memory: performance.memory
};
// 计算关键指标
const paint = performance.getEntriesByType('paint');
const fcp = paint.find(entry => entry.name === 'first-contentful-paint');
console.log('FCP:', fcp.startTime);
// 自定义打点
performance.mark('start-fetch');
await fetch('/api/data');
performance.mark('end-fetch');
performance.measure('fetch-time', 'start-fetch', 'end-fetch');
// 获取测量结果
const measures = performance.getEntriesByType('measure');
2.5 长任务监控
javascript
// 监控长任务(>50ms的任务)
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('长任务:', {
duration: entry.duration,
startTime: entry.startTime,
name: entry.name
});
// 上报监控系统
reportLongTask(entry);
}
});
observer.observe({ entryTypes: ['longtask'] });
🌐 第三章:网络高级特性
3.1 预加载系列
html
<!-- 预加载:当前页面必须的资源 -->
<link rel="preload" href="style.css" as="style">
<link rel="preload" href="font.woff2" as="font" crossorigin>
<!-- 预连接:提前建立连接 -->
<link rel="preconnect" href="https://api.example.com">
<!-- DNS预解析 -->
<link rel="dns-prefetch" href="https://cdn.example.com">
<!-- 预获取:下一页可能用到的资源 -->
<link rel="prefetch" href="next-page.js">
<!-- 预渲染:提前渲染下一页 -->
<link rel="prerender" href="https://example.com/next-page">
优先级:
lua
preload > preconnect > dns-prefetch > prefetch
3.2 HTTP/2 和 HTTP/3
javascript
// HTTP/2 特性
// 1. 多路复用(一个连接并发多个请求)
// 2. 服务器推送(主动推送资源)
// 3. 头部压缩
// 4. 二进制分帧
// HTTP/3 特性
// 1. 基于QUIC(UDP)
// 2. 0-RTT 连接建立
// 3. 更好的丢包处理
// 4. 无队头阻塞
3.3 WebSocket
javascript
// 建立连接
const ws = new WebSocket('wss://example.com/chat');
// 连接打开
ws.addEventListener('open', () => {
ws.send('Hello Server!');
});
// 接收消息
ws.addEventListener('message', (event) => {
console.log('收到:', event.data);
});
// 心跳检测(防止连接断开)
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send('ping');
}
}, 30000);
// 断线重连
ws.addEventListener('close', () => {
setTimeout(() => {
reconnect();
}, 3000);
});
3.4 Server-Sent Events
javascript
// 服务器推送事件(单向)
const eventSource = new EventSource('/api/events');
// 监听默认消息
eventSource.onmessage = (event) => {
console.log('收到:', event.data);
};
// 监听自定义事件
eventSource.addEventListener('notification', (event) => {
console.log('通知:', event.data);
});
// 错误处理
eventSource.onerror = () => {
console.log('连接断开');
};
🚀 第四章:渲染性能进阶
4.1 关键渲染路径
html
<!-- 优化关键渲染路径 -->
<head>
<!-- 关键CSS内联 -->
<style>
.header { background: red; }
.hero { height: 500px; }
</style>
<!-- 非关键CSS异步加载 -->
<link rel="preload" href="non-critical.css" as="style" onload="this.rel='stylesheet'">
<!-- 关键JS内联 -->
<script>
// 首屏需要的JS
</script>
<!-- 非关键JS延迟加载 -->
<script src="app.js" defer></script>
</head>
4.2 图层管理高级技巧
css
/* 主动创建图层 */
.will-animate {
will-change: transform, opacity;
transform: translateZ(0); /* 老方案,不推荐 */
}
/* 避免层爆炸 */
.too-many-layers * {
will-change: transform; /* 危险!每个元素都创建层 */
}
/* 合理使用contain */
.independent-component {
contain: layout style paint; /* 告诉浏览器内部变化不影响外部 */
}
4.3 渲染阻塞诊断
javascript
// 检测长任务
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
if (entry.duration > 50) {
console.log('渲染阻塞任务:', entry);
// 分析调用栈
if (entry.attribution) {
console.log('原因:', entry.attribution[0]?.containerType);
}
}
});
});
observer.observe({ entryTypes: ['longtask', 'frame'] });
📦 第五章:Web Workers 与多线程
5.1 Dedicated Worker
javascript
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ type: 'compute', data: largeArray });
worker.onmessage = (e) => {
console.log('计算结果:', e.data);
};
worker.onerror = (e) => {
console.error('Worker错误:', e.message);
};
// worker.js
self.onmessage = (e) => {
const { type, data } = e.data;
if (type === 'compute') {
// 耗时计算
const result = heavyComputation(data);
// 返回结果
self.postMessage(result);
}
};
// 关闭worker
worker.terminate();
5.2 Shared Worker
javascript
// 多个页面共享同一个worker
// main.js
const worker = new SharedWorker('shared-worker.js');
worker.port.start();
worker.port.postMessage('hello');
worker.port.onmessage = (e) => {
console.log('收到:', e.data);
};
// shared-worker.js
const connections = [];
self.onconnect = (e) => {
const port = e.ports[0];
connections.push(port);
port.onmessage = (e) => {
// 广播给所有连接
connections.forEach(conn => {
conn.postMessage(e.data);
});
};
};
5.3 Service Worker(离线能力)
javascript
// sw.js
const CACHE_NAME = 'v1';
const urlsToCache = ['/', '/style.css', '/app.js'];
// 安装时缓存资源
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll(urlsToCache);
})
);
});
// 拦截请求
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
// 有缓存返回缓存,否则网络请求
return response || fetch(event.request).then((networkResponse) => {
// 可选:缓存新资源
if (event.request.url.includes('/api/')) {
return networkResponse;
}
const responseClone = networkResponse.clone();
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, responseClone);
});
return networkResponse;
});
})
);
});
// 更新缓存
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) => {
return Promise.all(
keys.filter(key => key !== CACHE_NAME)
.map(key => caches.delete(key))
);
})
);
});
🔧 第六章:调试与诊断高级技巧
6.1 Performance 面板深度使用
javascript
// 添加自定义标记
performance.mark('start-fetch');
await fetch('/api/data');
performance.mark('end-fetch');
// 测量
performance.measure('fetch-time', 'start-fetch', 'end-fetch');
// 在Performance面板可以看到
关键指标解读:
- FPS:帧率,低于30表示卡顿
- CPU:占用率,红色表示满负荷
- NET:网络请求,长条表示慢请求
- HEAP:内存使用,锯齿状是正常GC
6.2 Memory 面板深度使用
javascript
// 排查内存泄漏
// 1. 拍快照
// 2. 执行操作
// 3. 再拍快照对比
// 查找分离的DOM节点
// 在快照中搜索"Detached"
6.3 Layers 面板
javascript
// 查看图层信息
// 1. 打开DevTools
// 2. 三个点 → More tools → Layers
// 3. 查看每个图层的大小、内存占用
// 4. 找出不必要的图层
6.4 Coverage 面板
javascript
// 查看代码覆盖率
// 1. 打开Coverage面板
// 2. 点击录制
// 3. 操作页面
// 4. 红色表示未执行的代码
🚀 第七章:浏览器性能优化
7.1 性能指标
javascript
// Google定义的三大核心指标
const coreWebVitals = {
LCP: 'Largest Contentful Paint', // 最大内容绘制 - 加载性能
FID: 'First Input Delay', // 首次输入延迟 - 交互性能
CLS: 'Cumulative Layout Shift' // 累计布局偏移 - 视觉稳定性
};
// 理想阈值
const thresholds = {
LCP: { good: 2500, poor: 4000 }, // 2.5秒内良好,4秒以上差
FID: { good: 100, poor: 300 }, // 100ms内良好,300ms以上差
CLS: { good: 0.1, poor: 0.25 } // 0.1以内良好,0.25以上差
};
其他重要指标
javascript
const otherMetrics = {
// 传统指标
FP: 'First Paint', // 首次绘制
FCP: 'First Contentful Paint', // 首次内容绘制
TTI: 'Time to Interactive', // 可交互时间
TBT: 'Total Blocking Time', // 总阻塞时间
// 业务指标
TTFB: 'Time to First Byte', // 首字节时间
DOMContentLoaded: 'DOMContentLoaded', // DOM解析完成
Load: 'Load', // 所有资源加载完成
FPS: 'Frames Per Second' // 帧率
};
7.2 性能优化模型
- RAIL 模型
javascript
// Google提出的性能模型
const RAIL = {
Response: '响应', // 100ms内响应
Animation: '动画', // 10ms内生成一帧 (60fps)
Idle: '空闲', // 利用空闲时间处理任务
Load: '加载' // 1s内加载完成
};
// RAIL 目标
const railTargets = {
response: 100, // 用户操作100ms内要有反馈
animation: 10, // 每帧10ms内完成工作 (剩下6ms给浏览器)
idle: 50, // 空闲任务不超过50ms
load: 1000 // 首屏1秒内加载
};
- 性能优化的三个层次
javascript
const performanceLayers = {
// 第一层:网络优化
network: {
goal: '减少请求次数,减小资源体积',
techniques: ['缓存', '压缩', 'CDN', '预加载']
},
// 第二层:渲染优化
rendering: {
goal: '减少重排重绘,提高渲染效率',
techniques: ['图层优化', '动画优化', '读写分离']
},
// 第三层:计算优化
computing: {
goal: '减少JS执行时间,避免阻塞主线程',
techniques: ['Web Worker', '任务拆分', '懒计算']
}
};
7.3 网络层优化
- 减少请求次数
javascript
// ❌ 不好的做法:多个小文件
import Button from './Button';
import Input from './Input';
import Modal from './Modal';
// ✅ 好的做法:打包合并
import { Button, Input, Modal } from './components';
// webpack 配置
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
minSize: 20000,
maxSize: 244000,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
}
}
}
}
};
- 减小资源体积
javascript
// 1. 代码压缩
// webpack 自动压缩
module.exports = {
mode: 'production',
optimization: {
minimize: true,
minimizer: ['...', new TerserPlugin()]
}
};
// 2. Tree Shaking
// 只引入使用的代码
import { debounce } from 'lodash-es'; // 用es版本支持tree shaking
// 3. 图片优化
// 使用 WebP 格式
<picture>
<source srcset="image.webp" type="image/webp">
<img src="image.jpg" loading="lazy">
</picture>
// 4. Gzip/Brotli 压缩
// Nginx 配置
gzip on;
gzip_types text/plain text/css application/javascript;
gzip_comp_level 6;
brotli on;
brotli_types text/plain text/css application/javascript;
- 缓存策略
javascript
// 1. 强缓存 (长期)
Cache-Control: public, max-age=31536000, immutable
// 2. 协商缓存 (验证)
Cache-Control: no-cache
ETag: "33a64df551"
// 3. 文件名哈希 (非覆盖式发布)
// 构建后: app.8d3f9e.js
// 下次发布: app.a1b2c3.js
- 预加载系列
html
<!-- 预加载当前页关键资源 -->
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="font.woff2" as="font" crossorigin>
<!-- 预连接第三方域 -->
<link rel="preconnect" href="https://api.example.com">
<!-- DNS预解析 -->
<link rel="dns-prefetch" href="https://cdn.example.com">
<!-- 预获取下一页资源 -->
<link rel="prefetch" href="next-page.js">
<!-- 预渲染整个页面 -->
<link rel="prerender" href="https://example.com/next">
- 资源优先级
html
<!-- 关键资源:高优先级 -->
<link rel="preload" href="style.css" as="style">
<!-- 非关键资源:低优先级 -->
<link rel="prefetch" href="analytics.js">
<!-- 图片懒加载 -->
<img src="image.jpg" loading="lazy" />
<!-- iframe懒加载 -->
<iframe src="video.html" loading="lazy"></iframe>
7.4 渲染层优化
- 关键渲染路径优化
html
<!DOCTYPE html>
<html>
<head>
<!-- 1. 关键CSS内联 -->
<style>
/* 首屏需要的样式 */
.header { background: #333; }
.hero { height: 500px; }
</style>
<!-- 2. 非关键CSS异步加载 -->
<link rel="preload" href="non-critical.css" as="style" onload="this.rel='stylesheet'">
<!-- 3. 关键JS内联 -->
<script>
// 首屏需要的JS
window.initialData = { user: 'guest' };
</script>
<!-- 4. 非关键JS延迟加载 -->
<script src="app.js" defer></script>
</head>
<body>
<!-- 内容 -->
</body>
</html>
- 减少重排重绘
javascript
// ❌ 不好的写法:多次触发重排
const el = document.getElementById('box');
el.style.width = '100px'; // 重排
el.style.height = '200px'; // 重排
el.style.margin = '10px'; // 重排
console.log(el.offsetWidth); // 强制重排!
// ✅ 好的写法:批量操作
el.style.cssText = 'width:100px; height:200px; margin:10px;';
// 或者使用class
el.classList.add('box-style');
// ✅ 读写分离
const width = el.offsetWidth; // 读
const height = el.offsetHeight; // 读
el.style.width = width + 100 + 'px'; // 批量写
el.style.height = height + 100 + 'px';
// ✅ 使用transform代替位置属性
// ❌ 不好
el.style.top = '100px';
// ✅ 好
el.style.transform = 'translateY(100px)';
- 图层优化
css
/* 创建新图层的情况 */
.animate {
transform: translateZ(0); /* 老方法,不推荐 */
will-change: transform; /* 现代方法 */
}
/* 避免层爆炸 */
/* ❌ 危险 */
.too-many-layers * {
will-change: transform;
}
/* ✅ 合理使用 */
.carousel {
will-change: transform; /* 轮播图需要动画 */
}
/* 使用contain隔离 */
.widget {
contain: layout style paint; /* 内部变化不影响外部 */
}
- 动画优化
css
/* ✅ 合成器动画(推荐) */
@keyframes move {
from { transform: translateX(0); }
to { transform: translateX(100px); }
}
/* ❌ 触发布局的动画 */
@keyframes move-bad {
from { left: 0; }
to { left: 100px; }
}
/* 使用requestAnimationFrame */
function animate() {
element.style.transform = `translateX(${pos}px)`;
pos += 1;
if (pos < 100) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
7.5 JavaScript 执行优化
- 长任务拆分
javascript
// ❌ 不好的写法:长任务阻塞主线程
function processLargeArray(array) {
array.forEach(item => {
heavyComputation(item); // 同步执行,可能卡顿
});
}
// ✅ 好的写法:任务拆分
async function processLargeArray(array) {
for (let i = 0; i < array.length; i++) {
// 每次处理一小部分
heavyComputation(array[i]);
// 每10次让出主线程
if (i % 10 === 0) {
await new Promise(resolve => setTimeout(resolve, 0));
}
}
}
// ✅ 使用requestIdleCallback
function processWithIdle(array) {
let i = 0;
function processChunk(deadline) {
while (i < array.length && deadline.timeRemaining() > 0) {
heavyComputation(array[i]);
i++;
}
if (i < array.length) {
requestIdleCallback(processChunk);
}
}
requestIdleCallback(processChunk);
}
- Web Worker 多线程
javascript
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ type: 'process', data: largeArray });
worker.onmessage = (e) => {
console.log('处理结果:', e.data);
};
// worker.js
self.onmessage = (e) => {
if (e.data.type === 'process') {
// 耗时计算,不阻塞主线程
const result = heavyComputation(e.data.data);
self.postMessage(result);
}
};
// 线程池实现
class WorkerPool {
constructor(workerScript, size = navigator.hardwareConcurrency) {
this.workers = [];
this.queue = [];
this.active = new Map();
for (let i = 0; i < size; i++) {
const worker = new Worker(workerScript);
worker.onmessage = (e) => this.handleResult(worker, e);
this.workers.push(worker);
}
}
runTask(data) {
return new Promise((resolve, reject) => {
const task = { data, resolve, reject };
const idleWorker = this.workers.find(w => !this.active.has(w));
if (idleWorker) {
this.executeTask(idleWorker, task);
} else {
this.queue.push(task);
}
});
}
executeTask(worker, task) {
this.active.set(worker, task);
worker.postMessage(task.data);
}
handleResult(worker, e) {
const task = this.active.get(worker);
this.active.delete(worker);
task.resolve(e.data);
if (this.queue.length > 0) {
this.executeTask(worker, this.queue.shift());
}
}
}
- 虚拟列表
javascript
// 只渲染可视区域的列表项
class VirtualList {
constructor(container, items, itemHeight) {
this.container = container;
this.items = items;
this.itemHeight = itemHeight;
this.visibleCount = Math.ceil(container.clientHeight / itemHeight);
this.startIndex = 0;
container.style.overflow = 'auto';
container.style.position = 'relative';
container.addEventListener('scroll', () => {
this.startIndex = Math.floor(container.scrollTop / itemHeight);
this.render();
});
this.render();
}
render() {
const fragment = document.createDocumentFragment();
const endIndex = Math.min(this.startIndex + this.visibleCount + 2, this.items.length);
for (let i = this.startIndex; i < endIndex; i++) {
const item = this.items[i];
const div = document.createElement('div');
div.style.position = 'absolute';
div.style.top = i * this.itemHeight + 'px';
div.style.height = this.itemHeight + 'px';
div.textContent = item;
fragment.appendChild(div);
}
this.container.innerHTML = '';
this.container.appendChild(fragment);
}
}
7.6 资源加载优化
- 代码分割
javascript
// React 懒加载
const LazyComponent = React.lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<Loading />}>
<LazyComponent />
</Suspense>
);
}
// Vue 异步组件
const AsyncComponent = () => ({
component: import('./HeavyComponent.vue'),
loading: LoadingComponent,
error: ErrorComponent,
delay: 200,
timeout: 3000
});
// 路由懒加载
const routes = [
{
path: '/dashboard',
component: () => import('./pages/Dashboard')
}
];
- 图片优化
html
<!-- 响应式图片 -->
<img
srcset="
small.jpg 300w,
medium.jpg 600w,
large.jpg 900w
"
sizes="(max-width: 320px) 280px,
(max-width: 640px) 560px,
800px"
src="fallback.jpg"
loading="lazy"
alt="optimized"
>
<!-- 现代格式 -->
<picture>
<source srcset="image.avif" type="image/avif">
<source srcset="image.webp" type="image/webp">
<img src="image.jpg" loading="lazy">
</picture>
<!-- 渐进式图片 -->
<img src="thumb.jpg" data-src="full.jpg" class="lazy">
- 字体优化
css
/* 字体预加载 */
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
/* font-display 控制 */
@font-face {
font-family: 'CustomFont';
src: url('font.woff2') format('woff2');
font-display: swap; /* 先用系统字体,加载完再换 */
}
/* 字体子集化 */
@font-face {
font-family: 'CustomFont';
src: url('font-latin.woff2') format('woff2');
unicode-range: U+00-FF; /* 只加载拉丁字符 */
}
7.7 性能监控与诊断
- 性能监控 SDK
javascript
class PerformanceMonitor {
constructor(options = {}) {
this.metrics = {};
this.initObservers();
}
initObservers() {
// LCP
new PerformanceObserver((list) => {
const entries = list.getEntries();
this.metrics.lcp = entries[entries.length - 1].startTime;
}).observe({ entryTypes: ['largest-contentful-paint'] });
// FID
new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
this.metrics.fid = entry.processingStart - entry.startTime;
});
}).observe({ entryTypes: ['first-input'] });
// CLS
let cls = 0;
new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
if (!entry.hadRecentInput) {
cls += entry.value;
this.metrics.cls = cls;
}
});
}).observe({ entryTypes: ['layout-shift'] });
// 资源
new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
if (entry.duration > 300) {
this.reportSlowResource(entry);
}
});
}).observe({ entryTypes: ['resource'] });
// 长任务
new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
if (entry.duration > 50) {
this.reportLongTask(entry);
}
});
}).observe({ entryTypes: ['longtask'] });
}
reportToAnalytics(data) {
if (navigator.sendBeacon) {
navigator.sendBeacon('/api/perf', JSON.stringify(data));
} else {
fetch('/api/perf', {
method: 'POST',
body: JSON.stringify(data),
keepalive: true
});
}
}
}
// 使用
const monitor = new PerformanceMonitor();
- 性能问题诊断
javascript
// 1. 使用 Performance 面板
// 记录 → 操作 → 分析
// 2. 使用 Lighthouse
// Chrome DevTools → Lighthouse → 生成报告
// 3. 使用 Web Vitals 库
import { getLCP, getFID, getCLS } from 'web-vitals';
getLCP(console.log);
getFID(console.log);
getCLS(console.log);
// 4. 自定义性能标记
performance.mark('start-fetch');
await fetch('/api/data');
performance.mark('end-fetch');
performance.measure('fetch-time', 'start-fetch', 'end-fetch');
javascript
// 优化前的问题
const problems = [
'首屏图片太多',
'JS打包过大',
'交互卡顿',
'API请求慢'
];
// 优化方案
const solutions = {
// 1. 图片优化
images: () => {
// 使用WebP
// 懒加载
// 尺寸适配
},
// 2. 代码分割
codeSplit: () => {
// 路由懒加载
// 组件动态导入
// 第三方包拆包
},
// 3. 渲染优化
rendering: () => {
// 虚拟列表
// 防抖节流
// Web Worker
},
// 4. 缓存策略
cache: () => {
// Service Worker
// API缓存
// 预加载下一页
}
};
// 优化效果
const result = {
LCP: '2.8s → 1.2s',
FID: '120ms → 45ms',
CLS: '0.35 → 0.08',
TTI: '4.5s → 2.1s'
};
7.8 性能优化的原则
优化的四个维度
javascript
const optimizationPrinciples = {
// 1. 减少
reduce: {
network: '减少请求数和体积',
render: '减少重排重绘',
compute: '减少JS执行时间'
},
// 2. 复用
reuse: {
cache: '充分利用缓存',
connection: '复用TCP连接',
data: '复用计算结果'
},
// 3. 延迟
defer: {
load: '非关键资源延迟加载',
compute: '计算延迟到空闲时',
render: '虚拟列表延迟渲染'
},
// 4. 提前
preload: {
dns: '预解析DNS',
connect: '预连接',
fetch: '预获取资源',
render: '预渲染'
}
};
优化的优先级
javascript
const priority = {
// 第一优先级:核心体验
critical: [
'LCP优化', // 用户看到内容
'FID优化', // 用户可以交互
'CLS优化' // 不晃动
],
// 第二优先级:流畅度
smoothness: [
'滚动流畅',
'动画60fps',
'无卡顿'
],
// 第三优先级:加载速度
speed: [
'页面完全加载',
'资源缓存',
'预加载'
],
// 第四优先级:资源消耗
resource: [
'内存占用',
'CPU使用',
'电量消耗'
]
};
性能优化的黄金法则
javascript
const goldenRules = {
// 1. 用户为中心
rule1: '优先优化用户感知的指标 (LCP/FID/CLS)',
// 2. 测量先行
rule2: '不测量就无法优化',
// 3. 基于原理
rule3: '理解浏览器原理才能精准优化',
// 4. 持续改进
rule4: '性能优化是持续的过程,不是一次性任务',
// 5. 平衡取舍
rule5: '在加载速度、流畅度、资源消耗间找到平衡'
};
🎯 第八章:重点解析
Q1:requestAnimationFrame 和 requestIdleCallback 的区别?
javascript
// requestAnimationFrame:渲染前执行
requestAnimationFrame(() => {
console.log('下一帧渲染前执行');
});
// requestIdleCallback:浏览器空闲时执行
requestIdleCallback((deadline) => {
while (deadline.timeRemaining() > 0) {
// 执行任务
}
}, { timeout: 2000 });
区别:
- rAF:必须执行,优先级高
- rIC:有空才执行,优先级低
Q2:什么是 Layout Thrashing(布局抖动)?
javascript
// 坏代码:反复触发强制布局
for (let i = 0; i < 1000; i++) {
box.style.left = i + 'px'; // 写
console.log(box.offsetLeft); // 读 → 强制布局!
}
// 好代码:读写分离
for (let i = 0; i < 1000; i++) {
box.style.left = i + 'px'; // 批量写
}
const positions = []; // 统一读
for (let i = 0; i < 1000; i++) {
positions.push(box.offsetLeft);
}
Q3:什么是 FOUC(无样式内容闪烁)?
html
<!-- 原因:CSS加载延迟 -->
<head>
<link rel="stylesheet" href="slow.css">
</head>
<body>
<h1>先显示默认样式,CSS加载后突然变化</h1>
</body>
解决方案:
- 内联关键CSS
- 使用
<link rel="preload"> - CSS放头部,JS放底部
Q4:什么是 FP、FCP、LCP、FID、CLS?
javascript
// 核心Web指标
const metrics = {
FP: 'First Paint', // 第一个像素绘制
FCP: 'First Contentful Paint', // 第一个内容绘制
LCP: 'Largest Contentful Paint', // 最大内容绘制(加载性能)
FID: 'First Input Delay', // 首次输入延迟(交互性能)
CLS: 'Cumulative Layout Shift' // 累计布局偏移(视觉稳定性)
};
// 获取指标
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log(entry.name, entry.startTime);
}
}).observe({ entryTypes: ['paint', 'layout-shift', 'first-input'] });
Q5:浏览器是如何处理多个标签页的?
javascript
// 进程模型
// - 每个标签页通常独立进程(Chrome)
// - 但同源页面可能共享进程(优化)
// - 浏览器的"站点隔离"策略
// 标签页间通信方式
// 1. localStorage事件
window.addEventListener('storage', (e) => {});
// 2. BroadcastChannel
const channel = new BroadcastChannel('tab-channel');
channel.postMessage('hello');
// 3. SharedWorker
// 4. postMessage + iframe
// 5. IndexedDB