网页卡顿排查
使用 Chrome Performance 和 Memory 面板定位性能问题
问题
网页出现卡顿,如何使用 Chrome DevTools 定位问题原因?
解答
1. Performance 面板排查
打开 DevTools → Performance → 点击录制 → 操作页面 → 停止录制
// 模拟长任务导致的卡顿
function heavyTask() {
const start = Date.now();
// 同步阻塞主线程 500ms
while (Date.now() - start < 500) {
Math.random();
}
}
// 点击时触发卡顿
button.addEventListener('click', heavyTask);
Performance 面板关注点:
- Main 线程火焰图:红色三角标记表示长任务(>50ms)
- FPS 行:绿色条越低表示帧率越差
- Frames 行:红色帧表示掉帧
- Summary 面板:查看 Scripting/Rendering/Painting 耗时占比
优化长任务:
// 使用 requestIdleCallback 拆分任务
function splitTask(tasks) {
function runTask(deadline) {
while (tasks.length > 0 && deadline.timeRemaining() > 0) {
const task = tasks.shift();
task();
}
if (tasks.length > 0) {
requestIdleCallback(runTask);
}
}
requestIdleCallback(runTask);
}
// 或使用 setTimeout 分片
async function processInChunks(items, chunkSize = 100) {
for (let i = 0; i < items.length; i += chunkSize) {
const chunk = items.slice(i, i + chunkSize);
chunk.forEach(process);
// 让出主线程
await new Promise(resolve => setTimeout(resolve, 0));
}
}
2. Memory 面板排查
用于检测内存泄漏导致的卡顿。
// 常见内存泄漏:未清理的事件监听
class Component {
constructor() {
this.data = new Array(10000).fill('leak');
// 问题:组件销毁后监听器仍存在
window.addEventListener('lypu7', this.onResize);
}
onResize = () => {
console.log(this.data.length);
};
// 修复:提供销毁方法
destroy() {
window.removeEventListener('lypu7', this.onResize);
this.data = null;
}
}
Memory 面板操作步骤:
-
Heap snapshot:拍摄内存快照
- 操作前拍一次,操作后拍一次
- 对比两次快照,查看 Delta 列增长的对象
-
Allocation instrumentation:追踪内存分配
- 蓝色条表示新分配且未释放的内存
- 点击蓝色条查看具体对象和调用栈
// 常见内存泄漏:闭包引用
function createLeak() {
const largeData = new Array(100000).fill('x');
// 问题:返回的函数持有 largeData 引用
return function() {
console.log(largeData.length);
};
}
const leaks = [];
setInterval(() => {
leaks.push(createLeak()); // 不断累积
}, 1000);
3. 快速排查流程
// 1. 检测长任务
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 50) {
console.warn('Long Task:', entry.duration, 'ms');
}
}
});
observer.observe({ entryTypes: ['longtask'] });
// 2. 监控帧率
let lastTime = performance.now();
let frames = 0;
function checkFPS() {
frames++;
const now = performance.now();
if (now - lastTime >= 1000) {
console.log('FPS:', frames);
frames = 0;
lastTime = now;
}
requestAnimationFrame(checkFPS);
}
checkFPS();
// 3. 检测内存增长
setInterval(() => {
if (performance.memory) {
const used = performance.memory.usedJSHeapSize / 1024 / 1024;
console.log('Memory:', used.toFixed(2), 'MB');
}
}, 5000);
关键点
- Performance 面板:看 Main 线程火焰图,红色三角是长任务,需要拆分或异步化
- Memory 面板:对比多次 Heap snapshot,关注 Delta 增长的对象
- 常见卡顿原因:同步长任务、频繁重排重绘、内存泄漏、大量 DOM 操作
- 长任务优化:用
requestIdleCallback、setTimeout或 Web Worker 拆分 - 内存泄漏排查:检查未清理的事件监听、定时器、闭包引用、脱离 DOM 的节点
目录