博客 / 詳情

返回

用這 9 個 API,我把頁面性能幹到了 90+

🧑‍💻 寫在開頭

點贊 + 收藏 === 學會🤣🤣🤣

最近項目上線,用户一多,頁面就卡得不行。首屏加載 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 → 節流優化

每一個都能在特定場景下帶來顯著提升。建議從 IntersectionObserverpreload 開始,逐步引入,效果立竿見影。

如果對您有所幫助,歡迎您點個關注,我會定時更新技術文檔,大家一起討論學習,一起進步。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.