對(duì)于前端頁面來講,頁面卡頓是一個(gè)常見的性能問題,這里主要從造成頁面卡頓的原因、如何檢測(cè)和排查頁面卡頓、以及優(yōu)化頁面卡頓的最佳實(shí)踐這三個(gè)方向進(jìn)行分析。
一、造成頁面卡頓的原因
1.1 頁面掉幀
- 回流和重繪多:優(yōu)化DOM操作。
- DOM節(jié)點(diǎn)多:采用分頁、虛擬列表等方式進(jìn)行優(yōu)化。
1.2 內(nèi)存占用高,存在內(nèi)存泄漏
1.2.1 全局變量引起的內(nèi)存泄漏
- js 有個(gè)特點(diǎn),未聲明的變量會(huì)直接掛載到
window
上,也被稱為隱式全局變量,這樣雖然方便后續(xù)變量訪問,但會(huì)造成內(nèi)存泄漏。
<script>
a = 1;
</script>
- 在
window
上掛載大內(nèi)存對(duì)象:
window.largeFloatArray = new Float32Array(1000000);
1.2.2 閉包引起的內(nèi)存泄漏
function addEvent(){
const el = document.getElementById("button");
const hugeData = new Array(100000).join("hello");
el.addEventListener("click", ()=>{
console.log(hugeData)
})
}
addEvent();
這里通過閉包引用了 hugeData
,導(dǎo)致 hugeData
無法被回收,從而造成內(nèi)存泄漏。需要在合適的時(shí)機(jī)移除掉事件監(jiān)聽器。
1.2.3 定時(shí)器引起的內(nèi)存泄露
function genTimer(){
let count = 0;
setInterval(function(){
count++;
console.log(count);
})
}
genTimer();
這里的 setInterval
定時(shí)器的回調(diào)函數(shù)引用了外部的 count
變量,如果在合適的時(shí)機(jī)沒有清除定時(shí)器,就會(huì)導(dǎo)致內(nèi)存泄漏。
1.2.4 未解除的DOM引用造成的內(nèi)存泄漏
let el = document.getElementById('button');
如果 el
被存儲(chǔ)在某個(gè)地方,并且該元素被移除,但 el
仍然被引用,那么它將不會(huì)被垃圾回收,從而導(dǎo)致內(nèi)存泄漏。
1.2.5 循環(huán)引用
循環(huán)引用指的是兩個(gè)或多個(gè)對(duì)象相互引用,形成一個(gè)循環(huán)結(jié)構(gòu),導(dǎo)致無法被回收。
let a = {};
let b = {};
a.c = b;
b.c = a;
1.3 長(zhǎng)任務(wù)
由長(zhǎng)任務(wù)會(huì)讓 JavaScript
執(zhí)行時(shí)間過長(zhǎng),導(dǎo)致渲染不及時(shí),頁面卡頓。
function longSyncTask(duration) {
const start = performance.now();
while (performance.now() - start < duration) {
}
console.log(`同步長(zhǎng)任務(wù)完成,耗時(shí) ${performance.now() - start}ms`);
}
longSyncTask(3000);
二、頁面卡頓如何排查
2.1 使用 Chrome DevTools 性能分析
console.profile('性能分析');
console.profileEnd('性能分析');
然后就可以在 Chrome DevTools
的 Performance 面板中查看詳細(xì)分析結(jié)果。
2.2 測(cè)量代碼執(zhí)行時(shí)間
console.time('操作計(jì)時(shí)');
console.timeEnd('操作計(jì)時(shí)');
const start = performance.now();
const duration = performance.now() - start;
console.log(`操作耗時(shí): ${duration}毫秒`);
2.3 長(zhǎng)任務(wù)檢測(cè)
使用 PerformanceObserver API
可以檢測(cè)檢測(cè)長(zhǎng)任務(wù)(>50ms)。
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('長(zhǎng)任務(wù):', entry);
}
});
observer.observe({ entryTypes: ['longtask'] });
2.4 幀率(FPS)監(jiān)控
let lastTime = performance.now();
let frameCount = 0;
function checkFPS() {
const now = performance.now();
frameCount++;
if (now > lastTime + 1000) {
const fps = Math.round((frameCount * 1000) / (now - lastTime));
console.log(`當(dāng)前FPS: ${fps}`);
if (fps < 30) {
console.warn('幀率過低,可能存在性能問題');
}
frameCount = 0;
lastTime = now;
}
requestAnimationFrame(checkFPS);
}
requestAnimationFrame(checkFPS);
前端頁面的 FPS(Frames Per Second 每秒幀數(shù),代表頁面的流暢度和卡頓程度)如果低于 30 幀,就可以認(rèn)為頁面出現(xiàn)明顯卡頓的情況。 一般來說,F(xiàn)PS 在 60 幀及以上能夠提供流暢的用戶體驗(yàn),但如果頁面中包含大量的動(dòng)畫、視頻、音頻,導(dǎo)致元素?cái)?shù)量、復(fù)雜度、計(jì)算量等過高,那就需要更高的 FPS 才能讓頁面很流暢。
三、優(yōu)化頁面卡頓的最佳實(shí)踐
- 分批處理大任務(wù):使用
requestIdleCallback
或 setTimeout
分塊執(zhí)行。 - 避免頻繁DOM操作:使用文檔片段或虛擬DOM。
- 優(yōu)化動(dòng)畫:使用
requestAnimationFrame
而非 setTimeout
。 - 使用
Web Worker
:將計(jì)算密集型任務(wù)移出主線程。
?轉(zhuǎn)自https://juejin.cn/post/7534922574057930798
該文章在 2025/8/7 17:18:17 編輯過