我让网站的分析脚本等页面先到。代价是什么

在这个网站上打开一篇文章,读者下载的东西里有 60% 是 Google 的分析脚本,而且它们在文章还没到齐时就开始抢路。我让它们等到页面空闲、或者读者第一次触碰页面时才加载。这里是前后的数字、那段代码、可能少算的访问,以及测速工具看不到的那一部分。

其他语言版本 English

Google 原来占了一篇文章多少

2026 年 10 月 2 日早上,这个网站的每一个文章页面一共 492 到 494 KB,其中 297 KB 来自 Google。

那天稍早,我拿掉了 Cookie 横幅。分析工具从此对每一位读者都加载,页脚的 Cookie 链接里可以关掉;欧洲经济区、英国和瑞士的读者默认是关的。横幅在的时候,Google 要等读者点了「允许」才加载。横幅一拿掉,每个页面一开始加载,两个脚本就跟着启动:Tag Manager 的容器 gtm.js,大约 122 KB;Google Analytics 的标签 gtag/js,大约 174 KB。

我用 Lighthouse 的手机模式测了嘛嘛档那篇文章,每个页面跑两次。英文版是 94 分和 75 分,中文版是 95 分和 92 分,首页是 68 分和 56 分。

读者真正来读的那些字,只占手机下载量的一小部分。

要紧的是顺序,不只是大小

标了 async 的脚本不会挡住页面绘制,但它还是在跟页面抢。手机上,Google 的文件和文章自己的图片、字体共用一条连接;文件到了之后,又要在同一条主线程上运行,而那条线程正忙着把文字排出来。

分析工具不需要排第一。它只需要到一次,而且及时到,把这次访问记下来。

所以问题不是要不要留着 Google,而是什么时候去请它来。

改动:等空闲,或者等第一次触碰

从 10 月 2 日起,规则很简单。页面先加载完。然后,两件事里先发生的那一件去请求 gtm.js:浏览器说它空闲了,或者读者动了一下。

具体来说:

  1. 同意设置的默认值,以及读者之前存下的选择,一开始就立刻推送,跟以前一样。任何数据都不会在错误的同意状态下发出去。
  2. 页面的 load 事件之后,脚本请浏览器在空闲时通知它,最多等 2,000 毫秒。Safari 没有空闲回调,所以在那里是 load 之后等 1,500 毫秒。
  3. 如果读者先滚动、点按、点击、转滚轮或者按键,gtm.js 就在那一刻请求。
  4. 哪一件先发生就算哪一件,其余的全部取消。每次访问只发生一次,读者跳到站内另一个页面时也一样。
  5. 关掉了分析的读者,不会向 Google 请求任何东西。

核心就是下面这段,从这个网站实际运行的脚本里精简出来:

const INTERACTIONS = ['pointerdown', 'keydown', 'touchstart', 'scroll', 'wheel'];
const LISTEN = { capture: true, passive: true };
let loaded = false;

function loadGtm() {
  if (loaded) return;
  loaded = true;
  INTERACTIONS.forEach((type) => window.removeEventListener(type, loadGtm, LISTEN));
  window.dataLayer.push({ 'gtm.start': Date.now(), event: 'gtm.js' });
  const s = document.createElement('script');
  s.async = true;
  s.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXXX';
  document.head.appendChild(s);
}

function waitForIdle() {
  if ('requestIdleCallback' in window) requestIdleCallback(loadGtm, { timeout: 2000 });
  else setTimeout(loadGtm, 1500);
}

INTERACTIONS.forEach((type) => window.addEventListener(type, loadGtm, LISTEN));
if (document.readyState === 'complete') waitForIdle();
else window.addEventListener('load', waitForIdle, { once: true });

有两个细节要紧。gtm.start 那一行和插入脚本放在同一个函数里,所以 Tag Manager 自己的计时,是从文件真正被请求的那一刻开始。还有,监听器都是被动的(passive),永远不会拖慢滚动。

我用两个 AI 会话工作,一个 Brain 负责规划,一个 Hand 负责建造。拿掉中文网页字体之后,Google 成了文章页面上最重的东西,于是 Brain 提了这个改动。Hand 把它做出来,每一种情况都写了测试,包括在 Google 加载之前关掉分析、再在同一个页面上打开。我是先读清楚它要我付出什么,才点头的,那部分在下面。

前后对比

下面是线上网站的 Lighthouse 手机模式中位数,同一天,改动前跑两次,改动后跑三次。只有两次的时候,改动前的「中位数」就是两次的平均。

改动前 改动后
英文文章,分数 84.5 98
英文文章,最大内容绘制(LCP) 3.95 秒 1.6 秒
英文文章,总阻塞时间(TBT) 125 毫秒 50 毫秒
中文文章,分数 93.5 97
中文文章,最大内容绘制(LCP) 2.45 秒 2.1 秒
中文文章,总阻塞时间(TBT) 165 毫秒 60 毫秒
首页,分数 62 68
首页,最大内容绘制(LCP) 5.65 秒 5.7 秒

英文那一行要小心读。它的「改动前」是 94 和 75 的平均,而 75 只是一次跑得慢。老实的总结是:改动前,文章页面多数在 90 多分,中间夹着几次差的;改动后,大多数时候是 97 到 99 分,页面被卡住的时间少了一半或更多。

首页几乎没动,原因我知道。首页跑着一个 3D 场景,它的代价比分析工具大,那一笔我另外写过。

在真实浏览器里全新打开一次:页面在 692 毫秒加载完,gtm.js 在 718 毫秒请求,页面浏览在 1,222 毫秒送到 Google。同一天早上用旧代码:gtm.js 在 659 毫秒请求,那时页面还没加载完,页面浏览在 1,212 毫秒发出。在快的网络上,页面浏览发出的时间差不多。变的是,Google 不再趁页面还在路上时就开始挤。

测速工具看不到的那一部分

Lighthouse 在网络安静下来时就结束测量。如果这发生在空闲那一刻之前,Google 根本不会出现在测试里。

改动后的九次测试里,有两次就是这样。一次英文测试总共 195 KB,里面完全没有 Google。另一次只有 gtm.js,122 KB。真实的读者还是会下载全部 297 KB,只是排在页面之后。

所以,实验室分数变好的一部分,是实验室没有待够久。这不是撤回改动的理由:读者的页面现在确实先到了。但这是一个理由,不要拿最好的那一次来说事,而是更相信上面的中位数和浏览器里的实际时间,胜过任何一次单独的分数。

还有一次测试,说明了为什么一次不算数。改动后首页的第一次测试是 45 分,第一段文字过了 22.9 秒才出现。页面上什么都没变,只是网络坏了一分钟。

代价是什么

代价是很短的访问。读者打开一个页面,一两秒内没有滚动、点按或按键就离开,可能在 Google 被请来之前就走了,这次访问就不会被算到。

在快的网络上,这个空档很小:页面加载完大约 30 毫秒,空闲就到了。在慢的手机上会长一些。我不知道这样少算了多少次访问,也没办法知道,因为分析工具算不到它从没见过的访问。这个我接受。这个网站是写来读的,一秒内一动不动就离开的人,什么也没读到。

它没有让我付出的:

  • Google 到达之前推送的事件,会在队列里等着,Google 一到就读取。
  • 跳到站内另一个页面,不会重复加载。第二个页面的浏览照样记下。
  • 关掉了分析的读者,仍然什么都不发送。改动后我重新加载、滚动、点击、按键检查过:对 Google 的请求是零。

如果你投广告、要算转化,你的答案可能跟我不一样。花钱买来、一秒就走的访问,也还是花了钱的访问。

怎么在你自己的网站上做

一个下午就能试,同一个下午就能量出结果。

  1. 先量。用 Lighthouse 手机模式在一篇文章页面上跑三次,记下来自 googletagmanager.com 和 google-analytics.com 的 KB 数。
  2. 把页面头部的 Tag Manager 代码段拿掉,但 dataLayer 和同意设置的默认值,仍然放在页面前面。
  3. 在你自己的脚本文件里加一个像上面那样的触发器;如果你的安全策略不允许内联脚本(这个网站就不允许),就不要写成内联代码段。
  4. 在真实浏览器里确认页面浏览还能送到:打开开发者工具,进入 Network(网络),筛选 collect,先试一次全新打开、什么都不碰,再试一次滚动。
  5. 确认关掉分析后,仍然什么都不发送。
  6. 再量一次,跑三次,比较中位数,不要比你最好的那一次。
  7. 想清楚可能少算的短访问,对你这类网站重不重要。

常见问题

延迟加载 Google Tag Manager 能让网页变快吗?

通常能改善衡量页面本身多快出现的那些数字。在这个网站上,一篇英文文章在手机上的最大内容绘制中位数从 3.95 秒降到 1.6 秒,页面被卡住的时间从 125 毫秒降到 50 毫秒。Google 的文件还是会下载,只是排在页面之后。

延迟加载 Google Analytics 会少算数据吗?

可能会少算很短的访问:读者在一两秒内没有滚动、点按或按键就离开。读者留下来、或者碰过页面的访问,照样会被算到,之前推送的事件也会在队列里等着。少算了多少次,你量不出来,因为分析工具根本看不到它们。

应该等浏览器空闲,还是等读者第一次互动?

两个都用,哪个先到就用哪个。只等互动,会漏掉每一个不滚动、只是静静读的读者。只等空闲,在忙碌的手机上可能来得太晚。这个网站用的是:页面加载完后的空闲回调,最多等 2,000 毫秒;浏览器没有空闲回调时,用 1,500 毫秒的计时器;再加上第一次滚动、点按、点击、转滚轮或按键。

为什么延迟加载之后,Lighthouse 有时完全看不到 Google Tag Manager?

因为 Lighthouse 在网络安静下来时就停止测量,而这可能发生在空闲那一刻之前。这个网站的九次测试里,有两次在 Google 加载之前就结束了。真实的读者还是会下载这些文件。比较几次测试的中位数,并在浏览器里看实际时间,不要只信一次分数。