GTM 一掛上去,PageSpeed Insights(PSI)分數就往下掉?先別急著把它整個拔掉。很多時候,問題不是「有沒有 GTM」,而是它在首屏最忙的時候也跑進來搶頻寬、搶主執行緒。

這篇會帶你把 GTM 從「頁面一開就啟動」改成「使用者互動或瀏覽器有空時才啟動」,再用同一套流程檢查 LCP、TBT、INP,以及轉換事件有沒有被延遲到漏掉。簡單說,就是先讓瀏覽器把早餐吃完,再請 GTM 進來開會。

為什麼 GTM 會拖慢 PSI?

GTM 常見的影響大致分兩條線:

  1. 網路頻寬競爭gtm.js 下載時,可能和 hero 圖片、CSS 等首屏資源競爭,讓 LCP 晚一點出現。
  2. 主執行緒工作增加:容器啟動後,GA4、Pixel、Hotjar 等標籤會執行 JavaScript。標籤太多或太重,就可能製造長任務,推高 TBT,也讓互動回饋變慢。

延遲載入可以保護首屏,但它不是免費午餐:越晚啟動,越可能漏掉頁面剛開啟時的分析或轉換事件。因此要一起看效能和資料完整度。

核心做法:互動觸發加 idle fallback

可以讓以下兩條路都呼叫同一個 boot()

  • 使用者真的開始使用頁面,例如滾動、按鍵或觸控。
  • 使用者沒有互動時,等主執行緒進入 idle;如果瀏覽器不支援,就退回 setTimeout

重點是用 started flag 去重。once: true 只會移除某一個事件的 listener,不能取消已排程的 idle callback,也不能保證兩條路不會同時進入函式。

可直接複製的最小版本

GTM-XXXXXXX 換成自己的 container ID。這段程式會先建立 dataLayer,再在互動或 idle 時只載入一次 GTM。

(function (w, d) {
  var started = false;
  var idleId = null;
  var events = ['pointerdown', 'keydown', 'touchstart', 'scroll'];

  w.dataLayer = w.dataLayer || [];

  function boot() {
    if (started) return;
    started = true;

    if (idleId !== null && w.cancelIdleCallback) {
      w.cancelIdleCallback(idleId);
    }

    w.dataLayer.push({ 'gtm.start': Date.now(), event: 'gtm.js' });

    var script = d.createElement('script');
    script.async = true;
    script.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXXX';
    d.head.appendChild(script);
  }

  events.forEach(function (type) {
    d.addEventListener(type, boot, { once: true, passive: true });
  });

  if ('requestIdleCallback' in w) {
    idleId = w.requestIdleCallback(boot, { timeout: 2000 });
  } else {
    idleId = w.setTimeout(boot, 2000);
  }
})(window, document);

requestIdleCallbacktimeout 不是精準鬧鐘。瀏覽器會先等 idle period;若期限到了還沒執行,才把 callback 排進 event loop。它能限制等待時間,但不代表 GTM 會在那一刻完成下載或執行。

另外,Safari 和不同 WebView 的支援狀況可能不同,所以用 feature detection 比寫死「Safari 不支援」安全。舊版環境才需要 fallback。

timeout 要設多少?

真正要平衡的是「首屏保護」和「資料多早開始收集」。沒有一個數字適合所有網站,先用實測建立基準比較可靠。

timeout優點風險適合情境
1500–2500ms適合先做第一輪實驗早期 pageview 或轉換可能延後一般內容站、非關鍵分析
1000ms 以下較早啟動,資料風險較小首屏和 TBT 的保護變弱頁面很輕、轉換追蹤較重要
3000ms 以上首屏保護較久秒退與早期轉換漏算更多只有實測支持時才用

如果登入、註冊或購買事件不能漏,不要把所有 tag 都綁在延遲初始化上。可以保留關鍵事件的即時路徑,只延後非必要的分析和行銷 tag。

dataLayer 會保留事件,但不是資料保險箱

GTM 載入前先建立陣列,並把資料與 event 放在同一個 object:

window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
  event: 'signup_complete',
  plan: 'free'
});

這能保留「目前頁面、GTM 載入前已成功 push」的事件,GTM 載入後會依序處理 queue。但要記得三個界線:

  • dataLayer 不會自動跨頁保存;下一頁要重新初始化和 push。
  • queue 被處理,不代表 tag 的網路請求已經送達。
  • 使用者秒退或立即導向時,下載、執行或 beacon 仍可能被中斷。

因此,GTM Preview 或 Tag Assistant 顯示「有 push」只是第一關,還要到分析或廣告平台的測試工具確認事件真的送出。

延遲前先做的兩件事

先精簡 GTM container

每隔一段時間檢查 container:

  • 刪掉過期活動和不再使用的第三方 tag。
  • 把不需要每頁執行的 tag 改成 Window Loaded 或自訂事件。
  • 減少會反覆掃描 DOM 的變數和觸發條件。

如果只是把一堆不必要的 tag 延後,主執行緒只是晚點忙,並沒有真正變輕。

高階方案再考慮 Worker

Partytown 等方案可以把部分第三方腳本移到 Web Worker,但需要額外整合,且依賴 DOM 的 tag 可能不相容。它比較適合已經完成 tag 審計、仍有明確長任務問題的技術型網站,不是第一個該按的按鈕。

怎麼驗收:不要只看一次 PSI 分數

用下面流程比較立即載入版和延遲版:

  1. 固定同一個 URL、GTM container、裝置策略和 cookie/consent 狀態。
  2. PSI 兩個版本都跑多次,記錄 LCP、TBT、CLS、FCP,不要用單次分數宣稱因果。
  3. Lighthouse Navigation mode 看首屏;Timespan mode 則用來觀察互動後載入 GTM 的影響。
  4. 在 Performance trace 和 Network 面板確認 gtm.js、tag 長任務與請求發生時間。
  5. 用 GTM Preview/Tag Assistant 檢查 queue、Custom Event trigger、consent 和轉換 tag。
  6. 以真實流量的 CrUX 或 PSI field data 觀察 INP。TBT 是 lab 指標,不能直接當成真實 INP。

如果首屏改善了,但註冊或購買事件明顯少掉,這不是成功的優化;它只是把問題從 PSI 搬到分析資料裡。

推薦工具/資源

需求工具/做法適合誰
首屏與 lab 指標比較PageSpeed Insights、Chrome Lighthouse想確認 LCP、TBT 是否改善的站長
真實互動體驗CrUX/PSI field data有足夠流量、要看 INP 的網站
Tag 行為驗證GTM Preview、Tag Assistant不能接受事件悄悄漏掉的網站
整理效能與 SEO 驗收SEO 與 Core Web Vitals 檢查流程想把單次修正變成固定流程的站長

結語:先讓主執行緒喘口氣

  • GTM 不一定要刪,先把它改成互動觸發加 idle fallback。
  • timeout 越小,資料越早開始;越大,首屏保護越強,但早期事件風險也越高。
  • dataLayer 能排隊目前頁面的事件,不能保證跨頁保存或請求送達。

下一步:先用 2000ms 做基準,將立即載入版與延遲版各跑多次,同時核對轉換事件,再依 LCP、TBT、INP 和資料完整度調整。修完再喝一口咖啡,這次讓瀏覽器先喝。