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 和資料完整度調整。修完再喝一口咖啡,這次讓瀏覽器先喝。