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