GTM 一掛上去,PageSpeed Insights(PSI)分數就往下掉?先別急著把它整個拔掉。很多時候,問題不是「有沒有 GTM」,而是它在首屏最忙的時候也跑進來搶頻寬、搶主執行緒。
這篇會帶你把 GTM 從「頁面一開就啟動」改成「使用者互動或瀏覽器有空時才啟動」,再用同一套流程檢查 LCP、TBT、INP,以及轉換事件有沒有被延遲到漏掉。簡單說,就是先讓瀏覽器把早餐吃完,再請 GTM 進來開會。
目錄
為什麼 GTM 會拖慢 PSI?
GTM 常見的影響大致分兩條線:
- 網路頻寬競爭:
gtm.js下載時,可能和 hero 圖片、CSS 等首屏資源競爭,讓 LCP 晚一點出現。 - 主執行緒工作增加:容器啟動後,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);requestIdleCallback 的 timeout 不是精準鬧鐘。瀏覽器會先等 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 分數
用下面流程比較立即載入版和延遲版:
- 固定同一個 URL、GTM container、裝置策略和 cookie/consent 狀態。
- PSI 兩個版本都跑多次,記錄 LCP、TBT、CLS、FCP,不要用單次分數宣稱因果。
- Lighthouse Navigation mode 看首屏;Timespan mode 則用來觀察互動後載入 GTM 的影響。
- 在 Performance trace 和 Network 面板確認
gtm.js、tag 長任務與請求發生時間。 - 用 GTM Preview/Tag Assistant 檢查 queue、Custom Event trigger、consent 和轉換 tag。
- 以真實流量的 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 和資料完整度調整。修完再喝一口咖啡,這次讓瀏覽器先喝。

