🧑💻 寫在開頭
點贊 + 收藏 === 學會🤣🤣🤣
最近項目上線,用户一多,頁面就卡得不行。首屏加載 3 秒起,滾動掉幀,手機發燙……被 QA 喊去聊了好幾次。
沒辦法,只能低頭研究性能優化。翻了一圈文檔和實戰案例,發現現代瀏覽器其實給了我們很多“外掛”——那些你可能聽過但一直沒用起來的高級 API。
真正用上去之後,頁面流暢度提升非常明顯。今天就來分享我在項目中實測有效的 9 個 API,每一個都帶來了實實在在的性能提升。
1. IntersectionObserver:懶加載的終極方案
以前做圖片懶加載,都是監聽 scroll 事件,手動判斷元素位置。結果就是:一滾動,頁面卡成 PPT。
後來改用 IntersectionObserver,直接交給瀏覽器去監聽:
// 創建一個觀察器實例
// entries 是所有被觀察元素的狀態集合
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
// 判斷元素是否進入視口(可見)
if (entry.isIntersecting) {
const img = entry.target; // 獲取當前圖片元素
// 將 data-src 中的真實圖片地址賦給 src,開始加載
img.src = img.dataset.src;
// 加載完成後,停止觀察,避免重複觸發
observer.unobserve(img);
}
});
});
// 找到所有帶有 data-src 的圖片(懶加載圖片)
document.querySelectorAll('img[data-src]').forEach(img => {
// 讓觀察器開始監聽每個圖片
observer.observe(img);
});
效果:首屏加載時間直接砍掉 40%,滾動絲滑,CPU 佔用也降了。
關鍵是沒有重排重繪,完全是瀏覽器層面的優化,比手動監聽 scroll 強太多。
2. requestIdleCallback:把非關鍵任務丟到空閒時執行
有些事不着急,比如上報埋點、預加載下一頁數據、清理緩存。但放在主線程裏,總怕影響用户體驗。
requestIdleCallback 就是幹這個的——告訴瀏覽器:“等你空了再執行”。
// 瀏覽器會在主線程空閒時執行這個回調
// 不會阻塞高優先級任務(如渲染、用户輸入)
requestIdleCallback(() => {
// 發送用户行為埋點
sendAnalytics();
// 預加載下一頁可能需要的資源
preloadNextPage();
});
它不會搶佔主線程,適合處理低優先級任務。用了之後,頁面交互明顯更跟手了。
3. requestAnimationFrame:動畫就該這麼寫
以前用 setTimeout 做動畫,總感覺卡卡的,還容易掉幀。
換成 requestAnimationFrame 後,動畫終於和屏幕刷新率同步了:
function animate() {
// 更新元素位置
element.style.transform = `translateX(${x}px)`;
// 如果還沒到目標位置,繼續下一幀動畫
if (x < 200) {
requestAnimationFrame(animate);
}
}
// 啓動動畫
requestAnimationFrame(animate);
優勢:
- 自動適配 60fps / 120fps 屏幕
- 頁面不可見時自動暫停,省電
- 比
setTimeout更精準
動畫類交互都建議換成這個。
4. ResizeObserver:監聽元素尺寸變化
想監聽某個 div 的寬高變化?別再用 window.resize + getBoundingClientRect 了,又慢又不準。
ResizeObserver 可以精確監聽任意元素的尺寸變化:
const observer = new ResizeObserver(entries => {
// entries 包含所有被觀察元素的尺寸信息
entries.forEach(entry => {
// entry.contentRect 包含元素的寬高、位置等
console.log('新尺寸:', entry.contentRect);
// 可以在這裏調整子元素佈局或重繪圖表
});
});
// 開始監聽指定元素
observer.observe(document.getElementById('chart-container'));
特別適合圖表、自適應容器這類組件,再也不用手動觸發 resize 事件了。
5. performance.now():精準測量性能
Date.now() 精度不夠,還可能被系統時間干擾。
performance.now() 是高精度時間戳,適合測量函數執行時間:
// 獲取當前高精度時間(毫秒,精確到微秒)
const start = performance.now();
// 執行一個耗時操作
heavyCalculation();
// 再次獲取時間
const end = performance.now();
// 計算耗時,結果非常精確
console.log(`耗時: ${end - start}ms`);
配合 performance.mark() 和 measure(),還能做更復雜的性能分析。
6. preload 和 prefetch:資源預加載
preload:關鍵資源,立刻加載
<!-- 告訴瀏覽器:這個 CSS 很重要,馬上就要用,優先加載 --> <link rel="preload" href="critical.css" as="style"> <!-- 預加載字體文件,避免文字閃動 --> <link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
用於首屏必須用到的資源,瀏覽器會優先加載,提升首屏速度。
prefetch:未來可能用到的資源
<!-- 告訴瀏覽器:用户可能會訪問下一頁,空閒時預加載這個 JS --> <link rel="prefetch" href="/user/profile.js">
在空閒時預加載下一頁的 JS 或數據,實現“秒開”跳轉。
這兩個配合使用,體驗提升非常明顯。
7. Cache API + Service Worker:讓頁面離線可用
PWA 的核心就是緩存。用 Cache API,可以把靜態資源存到客户端:
// service-worker.js
self.addEventListener('fetch', event => {
// 攔截網絡請求
event.respondWith(
// 先在緩存中查找是否有匹配的請求
caches.match(event.request).then(cached => {
// 如果緩存中有,直接返回緩存內容
// 否則發起網絡請求
return cached || fetch(event.request);
})
);
});
第一次訪問正常加載,第二次直接從緩存讀,速度快到飛起。
而且即使斷網,核心頁面也能打開,用户體驗直接拉滿。
8. Web Workers:把重任務移出主線程
項目裏有個功能要處理上萬條數據,一執行頁面就卡死。
後來用 Web Workers 把計算放到後台線程:
// main.js - 主線程
// 創建一個 Web Worker,運行 worker.js 文件
const worker = new Worker('worker.js');
// 發送數據給 Worker
worker.postMessage(data);
// 監聽 Worker 的返回結果
worker.onmessage = (e) => {
console.log('處理完成:', e.data);
};
// worker.js - 後台線程
// 監聽來自主線程的消息
self.onmessage = function(e) {
// 執行耗時的數據處理
const result = heavyProcess(e.data);
// 將結果返回給主線程
self.postMessage(result);
};
主線程再也不卡了,用户可以正常操作頁面,處理完再通知前端。
9. document.visibilityState:頁面不可見時節省資源
用户切到別的標籤頁,頁面還在瘋狂發請求、跑動畫?太浪費了。
用 visibilityState 判斷頁面是否激活:
document.addEventListener('visibilitychange', () => {
// visibilityState 的值可能是:
// 'visible':頁面在前台
// 'hidden':頁面在後台(最小化、切標籤)
if (document.visibilityState === 'hidden') {
// 頁面不可見時,暫停視頻播放
stopVideo();
// 停止定時輪詢接口
stopPolling();
} else {
// 頁面回到前台,恢復視頻播放
resumeVideo();
}
});
頁面不可見時暫停輪詢、視頻、動畫,回來再恢復。省電、省流量、省服務器壓力。
總結
這 9 個 API 不是“炫技”,而是真正在解決性能問題:
IntersectionObserver→ 懶加載requestIdleCallback→ 空閒任務requestAnimationFrame→ 流暢動畫ResizeObserver→ 尺寸監聽performance.now()→ 性能測量preload/prefetch→ 資源預加載Cache API→ 離線緩存Web Workers→ 後台計算visibilityState→ 節流優化
每一個都能在特定場景下帶來顯著提升。建議從 IntersectionObserver 和 preload 開始,逐步引入,效果立竿見影。