发布时间:2021 年 7 月 29 日
代码是指插入到网站中的第三方代码段,通常通过跟踪代码管理器插入。代码最常用于营销和分析。
代码和跟踪代码管理器对性能的影响因网站而异。跟踪代码管理器可比作信封:跟踪代码管理器提供了一个容器,但您要填充什么内容以及如何使用它,主要取决于您自己。
本文将介绍优化代码和代码管理器以提升性能和 Core Web Vitals 指标的相关技巧。虽然本文档提及了 Google 跟踪代码管理器,但其中讨论的许多概念也适用于其他跟踪代码管理器。
对 Core Web Vitals 的影响
代码管理器通常会消耗快速加载网页和确保其快速响应所需的资源,从而间接影响 Core Web Vitals。带宽可能会用于下载网站的跟踪代码管理器 JavaScript,或用于此 JavaScript 进行的后续调用。主线程上的 CPU 时间可用于评估和执行代码管理器和代码中包含的 JavaScript。
Largest Contentful Paint (LCP) 在关键网页加载时间内容易受到带宽争用的影响。此外,阻塞主线程可能会延迟 LCP 渲染时间。
累积布局偏移 (CLS) 可能会受到以下因素的影响:在首次渲染之前延迟加载关键资源,或者代码管理器向网页注入内容。
Interaction to Next Paint (INP) 容易受到主线程上的 CPU 争用影响,我们发现跟踪代码管理器的大小与 INP 得分较低之间存在相关性。
选择合适的代码类型
代码对性能的影响因代码类型而异。一般来说,图片代码(“像素”)的效果最好,其次是自定义模板,最后是自定义 HTML 代码。供应商代码因其允许的功能而异。
请注意,代码的使用方式会极大地影响其性能影响。“像素”的性能非常出色,这在很大程度上是因为此类代码的性质对其使用方式施加了严格的限制;自定义 HTML 代码不一定总是会影响性能,但由于它们为用户提供了很大的自由度,因此很容易被滥用,从而影响性能。
在考虑代码时,请注意规模:单个代码对性能的影响可能微不足道,但如果同一网页上使用了数十个或数百个代码,则影响可能会变得显著。
并非所有脚本都应通过跟踪代码管理器加载
通常,代码管理器不是加载用于实现用户体验的即时视觉或功能方面的资源(例如 Cookie 通知、主推图片或网站功能)的最佳方式。使用代码管理器加载这些资源通常会延迟其传送。这不利于用户体验,还可能会增加 LCP 和 CLS 等指标。
此外,部分用户会屏蔽代码管理器。使用跟踪代码管理器实现用户体验功能可能会导致部分用户无法访问您的网站。
谨慎使用自定义 HTML 代码
自定义 HTML 代码已存在多年,在大多数网站上都被广泛使用。借助自定义 HTML 代码,您可以输入自己的代码,且限制很少,因为尽管名称如此,但此代码的主要用途是向网页添加自定义 <script>
元素。
自定义 HTML 代码的使用方式多种多样,对性能的影响也大不相同。在衡量网站的性能时,请注意,大多数工具会将自定义 HTML 代码的性能影响归因于注入该代码的代码管理器,而不是代码本身。
自定义 HTML 代码可将元素插入周围网页中。向页面中插入元素的操作可能会导致性能问题,在某些情况下,还会导致布局偏移。
- 在大多数情况下,如果将元素插入到网页中,浏览器必须重新计算网页上每个项的大小和位置。此过程称为布局。单个布局对性能的影响微乎其微,但如果过多地发生,则可能会导致性能问题。这种现象对低端设备和 DOM 元素数量较多的网页的影响更大。
- 如果在周围区域已呈现后将可见的页面元素插入 DOM,则可能会导致布局偏移。这种现象并非仅限于代码管理器。不过,由于代码通常比网页的其他部分更晚加载,因此通常会在周围网页已呈现后插入 DOM。
使用自定义模板
自定义模板支持与自定义 HTML 代码相同的一些操作,但基于沙盒化版本的 JavaScript 构建,可为脚本注入和像素注入等常见用例提供 API。顾名思义,高级用户可以使用此类模板创建模板,并在构建时考虑性能。这样,技术水平较低的用户也可以使用该模板。这通常比提供完整的自定义 HTML 访问权限更安全。
由于对自定义模板施加了更严格的限制,因此这些代码不太可能出现性能或安全问题。出于同样的原因,自定义模板并不适用于所有用例。
正确注入脚本
使用代码管理器注入脚本是一种非常常见的用例。建议使用自定义模板和 injectScript
API 来实现此目的。
如需了解如何使用 injectScript API 转换现有的自定义 HTML 代码,请参阅转换现有代码。
如果您必须使用自定义 HTML 代码,请注意:
- 应使用下载外部文件的脚本标记(例如
<script src="external-scripts.js">
)加载库和大型第三方脚本,而不是直接将脚本内容复制粘贴到标记中。虽然放弃使用<script>
标记可消除下载脚本内容的单独往返,但此做法会增加容器大小,并阻止浏览器单独缓存脚本。 - 许多供应商都建议将
<script>
代码放在<head>
的顶部。不过,对于使用跟踪代码管理器加载的脚本,这通常是没有必要的。在大多数情况下,在代码管理器执行时,浏览器已完成<head>
的解析。
使用像素
有时,第三方脚本可以替换为图片或 iframe 像素。与基于脚本的像素相比,基于事件的像素可能支持的功能较少,因此通常被视为不太理想的实现方式。不过,在代码管理器中使用时,像素可以更加动态,因为它们可以根据触发器触发并传递不同的变量。
像素是性能最高且最安全的代码类型,因为在像素触发后,系统不会执行 JavaScript。像素的资源大小非常小(小于 1 KB),并且不会导致布局偏移。
请与您的第三方提供商联系,详细了解他们对像素的支持情况。此外,您还可以尝试检查其代码是否包含 <noscript>
标记。如果供应商支持像素,他们通常会在 <noscript>
标记中添加像素。
像素的替代方案
像素之所以广受欢迎,主要在于在服务器响应不相关的情况下(例如向分析服务提供商发送数据时),像素是发出 HTTP 请求的最便宜且最可靠的方式之一。navigator.sendBeacon()
和 fetch() keepalive
API 旨在解决相同的用例,但可以说比像素更可靠。
继续使用像素并无不妥,因为我们会为其提供良好的支持,并且对性能的影响微乎其微。不过,如果您要构建自己的信标,不妨考虑使用其中一个 API。
sendBeacon()
navigator.sendBeacon()
API 旨在在服务器响应不重要的情况下,将少量数据发送到 Web 服务器。
const url = "https://example.com/analytics";
const data = JSON.stringify({
event: "checkout",
time: performance.now()
});
navigator.sendBeacon(url, data);
sendBeacon()
的 API 有限:它仅支持发出 POST 请求,不支持设置自定义标头。所有现代浏览器都支持它。
Fetch API keepalive
keepalive
是一个标志,可让 Fetch API 用于发出事件报告和分析等非阻塞请求。您可以通过在传递给 fetch()
的参数中添加 keepalive: true
来使用此功能。
const url = "https://example.com/analytics";
const data = JSON.stringify({
event: "checkout",
time: performance.now()
});
fetch(url, {
method: 'POST',
body: data,
keepalive: true
});
如果 fetch() keepalive
和 sendBeacon()
看起来非常相似,那是因为它们确实非常相似。事实上,在 Chromium 浏览器中,sendBeacon()
现在基于 fetch()
keepalive
构建。
在 fetch() keepalive
和 sendBeacon()
之间进行选择时,请务必考虑您需要的功能和浏览器支持。fetch() API 的灵活性要高得多;不过,与 sendBeacon()
相比,keepalive
的浏览器支持较少。
了解这些代码的用途
代码通常是按照第三方供应商提供的指导创建的。如果您不清楚供应商代码的用途,不妨咨询相关人员。听取第二种意见有助于确定代码是否可能会导致性能或安全问题。
我们建议您在跟踪代码管理器中为代码添加所有者标签。很容易忘记代码的所有者,因此会担心移除代码会破坏某些内容!
触发器
概括地说,优化代码触发器通常包括确保触发代码的次数不超过必要的次数,以及选择能够平衡业务需求与性能开销的触发器。
触发器是会增加跟踪代码管理器大小和执行开销的 JavaScript 代码。虽然大多数触发器的影响很小,但累积效应可能会有所增加。例如,如果有多个点击事件或计时器触发器,则可能会大幅增加代码管理器的工作负载。
选择适当的触发事件
代码对性能的影响可能会有所不同。一般来说,代码触发得越早,对性能的影响就越大。在初始网页加载期间,资源通常受到限制,因此加载或执行特定资源(或代码)会占用其他资源。
虽然为所有代码选择合适的触发器很重要,但对于加载大型资源或执行长脚本的代码,这一点尤为重要。
代码可以基于网页浏览(通常是 Page load
、on DOM Ready
、on Window Loaded
)或自定义事件触发。为避免影响网页加载速度,请在 Window Loaded
之后触发非必需的代码。
使用自定义事件
使用自定义事件触发触发器,以响应 Google 跟踪代码管理器内置触发器未涵盖的网页事件。例如,许多代码都使用网页浏览触发器。不过,DOM Ready
和 Window Loaded
之间的时间可能会很长,因此很难精确调整代码的触发时间。自定义事件可以解决此问题。
首先,创建自定义事件触发器,然后更新代码以使用此触发器。
如需触发触发器,请将相应事件推送到数据层。
// Custom event trigger that fires after 2 seconds
setTimeout(() => {
dataLayer.push({
'event' : 'my-custom-event'
});
}, 2000);
使用特定的触发器条件
指定特定的触发器条件,以免在不需要时触发代码。为此,最简单且有效的方法之一是确保代码仅在实际使用它的网页上触发。
内置变量可纳入触发器条件中,以限制代码触发。
在适当的时间加载跟踪代码管理器
您可以调整代码管理器本身的加载时间,从而提升效果。无论触发器的配置方式如何,都必须等到跟踪代码管理器加载完毕后才能触发。请尝试调整加载代码管理器的时间,因为这可能产生与上述因素相同或更大的影响。此决策会影响网页上的所有代码。
通过稍后加载跟踪代码管理器,您可以避免日后出现性能问题,因为这样可以防止不小心过早加载代码。
变量
使用变量从网页中读取数据。它们在触发器和代码本身中都很有用。
与触发器一样,变量会向跟踪代码管理器添加 JavaScript 代码,因此可能会导致性能问题。变量可以相对较小,例如用于读取网址、Cookie、数据层或 DOM 的部分代码。它们还可以包含功能(和大小)不受限制的自定义 JavaScript。
尽量减少变量用量,因为跟踪代码管理器会持续评估变量。移除不再使用的旧变量,以缩减跟踪代码管理器脚本的大小和其所用的处理时间。
跟踪代码管理
高效使用代码可降低出现性能问题的风险。
使用数据层
数据层是一个 JavaScript 对象数组,其中包含与网页相关的信息。这些对象包含您希望传递给 Google 跟踪代码管理器的所有信息。
数据层还可用于触发代码。
// Contents of the data layer
window.dataLayer = [{
'pageCategory': 'signup',
'visitorType': 'high-value'
}];
// Pushing a variable to the data layer
window.dataLayer.push({'variable_name': 'variable_value'});
// Pushing an event to the data layer
window.dataLayer.push({'event': 'event_name'});
虽然您无需数据层即可使用 Google 跟踪代码管理器,但我们强烈建议您使用数据层。数据层会将第三方脚本可以访问的数据整合到一个位置,从而更好地了解其使用情况。除此之外,这还有助于减少冗余的变量计算和脚本执行。
通过使用数据层,您可以控制代码可以访问哪些数据,而不是授予对 JavaScript 变量或 DOM 的完整访问权限。
由于更新数据层会导致 Google 跟踪代码管理器重新评估所有容器变量并可能触发代码(这需要执行 JavaScript),因此数据层带来的效果优势可能并不明显。虽然数据层可能会被滥用,但一般来说,如果数据层似乎是性能问题的根源,则容器本身可能存在性能问题。数据层会使这些问题更加明显。
移除重复和未使用的代码
如果代码不仅通过跟踪代码管理器注入,还包含在网页的 HTML 标记中,就可能会出现重复代码。
应暂停或移除未使用的代码,而不是通过使用触发器例外情况来阻止代码。暂停或移除代码会将代码从容器中移除;屏蔽不会。
移除未使用的代码后,请检查触发器和变量,确定它们是否也可以移除。
暂停的代码会影响容器大小,但总载荷会比代码处于活动状态时小。
使用许可名单和拒绝名单
使用许可名单和禁止名单可对网页上允许使用的代码、触发器和变量配置非常精细的限制。这有助于强制执行效果最佳实践和其他政策。
许可名单和禁止名单是通过数据层配置的。
window.dataLayer = [{
'gtm.allowlist': ['<id>', '<id>', ...],
'gtm.blocklist': ['customScripts']
}];
例如,您可以禁止使用自定义 HTML 代码、JavaScript 变量或直接 DOM 访问。这意味着,您只能使用像素和预定义代码,并使用数据层中的数据。虽然这会带来一些限制,但可以实现更高性能和更安全的跟踪代码管理器实现。
考虑使用服务器端代码植入
您不妨考虑改用服务器端代码植入,对于希望更好地控制其数据的大型网站来说,这一点尤为重要。服务器端代码植入会从客户端移除供应商代码,并将处理工作从客户端分流到服务器。
例如,使用客户端代码植入时,向多个 Google Analytics 账号发送数据意味着客户端会针对每个端点发出单独的请求。使用服务器端代码植入时,客户端会向服务器端容器发出单个请求,然后系统会将这些数据转发到不同的 Google Analytics 账号。
请注意,服务器端代码植入仅适用于部分代码。代码兼容性因供应商而异。
如需了解详情,请参阅服务器端代码植入简介。
容器
代码管理器通常允许在其设置中包含多个实例(通常称为容器)。您可以在一个跟踪代码管理器账号中控制多个容器。
每个网页仅使用一个容器
在单个网页上使用多个容器可能会导致严重的性能问题,因为这会增加开销并增加脚本执行次数。至少,它会复制核心代码本身,由于该代码是作为容器的 JavaScript 的一部分提供的,因此无法在容器之间重复使用。
很少有应用能够有效使用多个容器。不过,在某些情况下,如果控制得当,这种方法也能奏效。例如:
- 包含较轻的“提前加载”容器和较重的“延迟加载”容器,而不是一个大型容器。
- 为技术水平较低的用户使用受限容器,为更复杂的代码使用受限程度较低但控制更为严格的容器。
如果您必须在每个网页上使用多个容器,请按照 Google 跟踪代码管理器中的指南设置多个容器。
如有需要,请使用单独的容器
如果您为多个媒体资源(例如网站应用和移动应用)使用跟踪代码管理器,您使用的容器数量可能会提高或降低工作流效率。这也可能会影响性能。
如果多个网站的使用和结构类似,则一个容器可在多个网站上高效使用。例如,虽然某个品牌的移动应用和 Web 应用可能提供类似的功能,但这两款应用的结构可能不同,因此通过单独的容器进行管理会更有效。
如果过于广泛地重复使用单个容器,可能会导致复杂的逻辑来管理代码和触发器,从而增加容器的复杂性和大小。
关注容器大小
容器的大小取决于其代码、触发器和变量。虽然小容器可能仍会对网页性能产生负面影响,但大容器几乎肯定会。
在优化代码用量时,容器大小不应是最关键的指标。不过,容器大小过大通常是容器未得到妥善维护且可能被滥用的警示信号。
Google 跟踪代码管理器会将容器大小限制为 300 KB,并在容器大小达到大小上限的 70% 时发出警告。
大多数网站都应力求将容器大小控制在限制以下。相比之下,网站容器的中位数约为 50 KB。 经过压缩,Google 跟踪代码管理器库本身大约为 33 KB。
为容器版本命名
容器版本是容器内容在特定时间点的快照。使用有意义的名称并附上对其中有意义的更改的简短说明,有助于更轻松地调试未来的性能问题。
代码植入工作流
请务必管理代码更改,以免对网页性能产生负面影响。
在部署前进行测试
在部署之前测试代码,以便在发布之前发现问题、性能问题等。
测试代码时需要注意的事项包括:
- 代码能否正常运行?
- 该代码是否会导致任何布局偏移?
- 该代码会加载任何资源吗?这些资源有多大?
- 该代码会触发长时间运行的脚本吗?
预览模式
借助预览模式,您可以在实际网站上测试代码更改,而无需先将其部署到公开环境。预览模式包含一个调试控制台,可提供有关代码的信息。
由于在调试控制台中显示信息需要额外的开销,因此在预览模式下运行时,Google 跟踪代码管理器的执行时间会有所不同(略慢)。因此,不建议将在预览模式下收集的 Web Vitals 测量结果与在生产环境中收集的测量结果进行比较。不过,这种差异不应影响代码本身的执行行为。
独立测试
测试代码的另一种方法是设置一个空白页面,其中包含一个包含单个代码(即您要测试的代码)的容器。这种测试设置不太现实,无法捕获某些问题(例如代码是否会导致布局偏移),但可以更轻松地隔离和衡量代码对脚本执行等方面的影响。了解 Telegraph 如何使用此隔离方法来提升第三方代码的性能。
监控代码效果
Google 跟踪代码管理器 Monitoring API 可用于收集有关特定代码执行时间的信息。这些信息会报告到您选择的端点。
如需了解详情,请参阅如何构建 Google 跟踪代码管理器监视器。
容器更改需要获得批准
第一方代码通常会在部署前经过审核和测试。对待代码的方式保持一致。
添加两步验证(需要管理员批准才能更改容器)就是其中一种方法。或者,如果您不想要求进行两步验证,但仍希望密切关注更改,可以设置容器通知,以便接收有关您选择的容器事件的电子邮件提醒。
定期审核代码使用情况
使用代码时遇到的一个难题是,代码往往会随着时间的推移而累积:代码会被添加,但很少被移除。定期审核代码是扭转这一趋势的一种方法。执行此操作的理想频率取决于您网站代码的更新频率。
为每个代码添加标签以明确所有者,这样可以更轻松地确定谁负责该代码,并确定该代码是否仍有用。
在审核代码时,请务必清理触发器和变量。它们也往往是导致性能问题的原因。
如需了解详情,请参阅控制第三方脚本。