随着网站功能的不断扩展,引入的第三方脚本数量也在不断增加。然而,我们往往忽视了对这些脚本的管理和优化,导致它们的不当使用严重影响了网站的整体性能。
衡量对网页的影响
我们希望通过嵌入第三方脚本来为网站提供额外的功能,但同时不希望这些脚本拖慢页面加载速度。然而,实际情况确实...,第三方脚本可能会带来以下问题:
-
过多的 JavaScript:加载过多的
JavaScript代码会导致主线程繁忙,从而阻碍DOM的构建和网页的呈现。CPU密集型脚本的解析和执行不仅会延迟用户的互动,还会增加设备的能耗。 -
阻止内容显示:某些特殊功能的同步脚本或某些异步脚本在处理完毕之前会阻止内容的显示。例如,在
A/B测试中,脚本可能会阻塞关键渲染路径,导致用户看到空白页面或部分加载的内容。 -
增加网络开销:
- 重复拉取多个框架和库:多个第三方脚本可能依赖相同的框架或库,导致重复加载。
- 过多的网络请求:向多个服务器发送大量网络请求会显著延长加载时间。每次请求都可能涉及
DNS查找、重定向以及与最终服务器的多次往返。 - 频繁触发网络请求:频繁的网络请求会占用浏览器的网络队列,导致其他重要资源的加载被推迟。
- 未经优化的静态资源:第三方脚本可能拉取未经优化的静态资源,进一步增加加载时间和带宽消耗。
-
全局变量冲突:不同脚本之间可能存在同名的全局变量,导致互相覆盖,引发意外错误和行为。
-
事件监听过多影响
INP、卡顿:若多个脚本引入过多的事件监听器,则会降低代码快速响应时间的能力,进而拖累INP和卡顿率。
为了识别网站上的第三方脚本并确定其性能影响,我们可以使用一些免费的 Web 速度测试工具,如 Chrome 开发者工具、PageSpeed Insights 和 WebPageTest。这些工具提供了丰富的诊断信息,帮助我们了解网站中使用的第三方脚本数量及其执行时间。
优化加载第三方脚本
推迟第三方脚本
在网页中直接插入同步脚本会延长页面的渲染时间。浏览器在遇到同步脚本时,会暂停DOM构建和页面渲染,直到该脚本被完全加载并执行完毕后才继续。
这不仅会导致用户看到的是一个半成品的页面,而且如果脚本尝试访问尚未构建的DOM元素,还会出现无法选取到这些元素的情况。
预先连接到所需的源
在慢速网络环境下,与第三方源建立连接可能会消耗相当长的时间。特别是对于HTTPS连接,这个过程可能涉及DNS查找、重定向以及多次服务器间的通信。通过提前准备这些步骤,我们可以显著加快页面的加载速度,同时不会对带宽使用造成额外负担。大部分时间实际上是花费在等待而非数据交换上。
dns-prefetch
- 功能:
dns-prefetch允许浏览器预先解析特定域名。这样,当实际需要访问该域名时,DNS查找已经完成,从而减少了延迟。 - 适用场景:当你确定页面会请求某个外部资源(如图片、脚本或样式表)时,可以使用
dns-prefetch来提前进行DNS查询。
<link rel="dns-prefetch" href="https://example.com" />.
preconnect
- 功能:
preconnect不仅执行DNS解析,还完成了TCP握手及TLS协商(针对HTTPS)。这样做可以极大地减少首次请求资源时的延迟。 - 适用场景:如果你确信页面将要请求某个外部资源,并且希望尽可能缩短延迟时间,那么就可以考虑添加
preconnect。
<link rel="preconnect" href="https://example.com">
preload
- 功能:
preload指示浏览器在后台提前加载指定资源,以便它们能在需要时迅速可用。 - 适用场景:当你知道某个资源将在页面加载过程中被使用,并希望提前加载它时,可以使用
preload。这通常适用于重要的CSS文件、JavaScript文件和字体等(最好是当前页面第一屏展示的必要静态资源文件)。
<link rel="preload" href="styles.css" as="style">
<link rel="preload" href="script.js" as="script">
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin="anonymous">
尽管资源提示(如preconnect、dns-prefetch、preload)能够有效提升页面性能,但过度使用也可能带来一些问题:
- 资源浪费:如果预连接的域名实际上并未被使用,或者只有在用户采取特定行动后才会用到,那么为这些连接所分配的
CPU、内存和带宽就白白浪费了。 - 影响关键资源加载:过多的
preload请求可能会占用宝贵的带宽和连接池,进而延后了那些对首屏显示至关重要的资源的加载。
因此,最佳实践是在深入分析站点的基础上,采用动态解析方法来识别并仅对真正必要的资源使用资源提示技术,并且使用预先连接/加载的资源不宜过多,尽量保持在总量=10左右。 。
自行托管第三方脚本
为什么要自行托管
第三方脚本供应商会提供JavaScript文件的URL,这些文件通常托管在内容分发网络(CDN)上。这种方法的优点在于,您只需复制和粘贴URL即可快速上手,无需任何维护开销,因为服务器配置和脚本更新都由第三方供应商处理。
然而,由于这些资源与我们站点的其他资源不在同一源上,从公共CDN加载文件会产生网络成本。浏览器需要进行DNS查找、建立新的HTTP连接,并在安全源上与供应商服务器进行SSL握手。
此外,使用来自第三方服务器的文件时,我们很少能控制缓存策略。依赖第三方供应商的缓存策略可能会导致脚本过于频繁地从网络中重新加载。
但由于它们与我们站点的其余资源不在同一源上, 从公共 CDN 加载文件会产生网络成本。浏览器需要 执行 DNS 查找、建立新的 HTTP 连接,以及在安全源上 与供应商服务器进行 SSL 握手。并且,使用来自第三方服务器的文件时,很少能够控制 缓存。依赖第三方供应商的缓存策略可能会导致脚本 过于频繁地从网络中重新提取。
因此,自行托管第三方脚本主要有以下作用和好处:
- 减少DNS查找时间: 每次访问一个新的域名时,浏览器需要进行
DNS查找。将第三方脚本托管在自己的服务器/CDN上,可以减少DNS查找时间,从而加快页面加载速度。 - 减少连接建立时间: 每次访问一个新的域名时,浏览器需要进行
TCP握手和TLS协商(如果是HTTPS)。将第三方脚本托管在自己的服务器上,可以减少连接建立时间。 - 减少对第三方服务器的依赖: 第三方服务器可能会出现宕机、网络问题或其他不可控的情况,导致脚本无法加载。将脚本托管在自己的服务器上,可以减少对第三方服务器的依赖,提高网站的可靠性。
- 更好地控制内容安全策略(CSP) : 内容安全策略(CSP)是一种防止跨站脚本攻击(XSS)的安全机制。将脚本托管在自己的服务器上,可以更好地控制CSP策略,减少安全漏洞。
- 更好地控制版本和更新:第三方脚本可能会频繁更新,导致兼容性问题。将脚本托管在自己的服务器上,可以更好地控制版本和更新,确保兼容性。
- 更好地控制缓存策略:将脚本托管在自己的服务器上,可以更好地控制缓存策略,优化资源加载。
如何托管
- 下载第三方脚本: 从第三方服务器下载所需的脚本文件,并将其保存到你的网站服务器或CDN上。
- 更新引用路径: 将HTML文件中引用第三方脚本的路径更新为你自己的服务器路径。
- 引入文件:重新在站点源码中引入脚本
托管操作注意点
- 更新和维护:第三方脚本可能会不断进行
API更改或安全修复。自行托管后,你需要负责这些脚本的更新和维护,以确保其安全性和兼容性,这会增加开发成本。 - 边缘缓存和压缩效果:自行托管第三方脚本时,请务必使用
CDN托管,并确保脚本已被压缩,否则可能会延长加载时间。最好在托管前测试当前脚本的加载性能数据,并在托管后进行对比。
作为替代方案,可以使用Service Worker来缓存来自第三方服务器的脚本。这样可以更好地控制缓存,同时保留第三方CDN的优势。
延迟加载第三方资源
如果嵌入的第三方资源满足以下条件:
显著降低网页加载速度、不是关键功能,或者位于非首屏位置(即用户需要滚动屏幕才能看到),那么延迟加载可以显著提高网页速度和渲染性能,从而为用户提供更好的首页浏览体验。延迟加载的基本思路是在主页内容加载完成后,但在用户与页面交互之前,延迟加载第三方内容。
IntersectionObserver是一个浏览器 API,可以让页面所有者有效地检测元素何时进入或离开浏览器的视口,该API兼容性不错,但如果需要兼容一些低版本设备,也可以考虑使用较低效的方法,侦听scroll或resize事件,然后使用getBoundingClientRect()等 DOM API 来计算元素相对于视口的位置