使用经典的 HTTP/1.0 超文本传输协议,Javascript、CSS、HTML、图像等资源被加载到请求/响应对中,这意味着浏览器发送请求以请求资源(无论是 CSS、Javascript等),并在请求另一个资源之前等待响应返回。即使它们被加载到请求/响应对中,由于网络延迟、服务器响应时间、服务器当前正在经历的负载等方面的随机性,请求和响应对并不总是严格遵循相同的顺序。
使用 HTTP/2 和 HTTP/3(HTTP 协议的较新版本),可以一次发送所有请求,而不是等待响应返回再发送另一个请求。我检查了您的网站,发现您的网站正在使用 HTTP/2 和 HTTP/3。使用 HTTP/2 和 HTTP/3 协议,由于可以一次发送所有请求,因此它也会导致一定程度的“不一致”,等等。即使使用 HTTP 1,也总是存在一定程度的随机性,因为有很多因素会影响它,例如服务器响应时间会有所不同,网络延迟会有所不同,等等。
为了说明这一点,如果您使用的是 Chrome 浏览器,请通过单击浏览器右上角的三个点打开“开发者工具”选项卡,然后单击“更多工具”,然后单击“开发者工具” ”。或者,如果您使用 Windows,则可以执行“Ctrl+Shift+I”或在 Mac 上执行“Command + Option + I”。然后转到其“网络”选项卡,然后刷新页面。每次刷新页面,资源加载的顺序都会有点不同:
在上图中,以 Google Tag Manager UA-174548329-1 Javascript 为例(我知道它可能不是 Google 地图),它作为 4th 资源加载。
当我再次刷新页面时,您的 Google 跟踪代码管理器 UA-174548329-1 Javascript 将作为 11th 资源加载:
当页面正在加载或您在 Google 的 PageSpeed Insight 上运行它时,由于请求和响应的随机性,主线程有时会很忙,有时不会。您的主线程也在构建 DOM,并且做了很多工作。有时它会被阻止渲染的资源(例如 Javascript)阻止。
默认情况下,Javascript 总是会阻止关键渲染路径。如果不查看您的 Javascript SetTimeOut,很难说您使用什么实现来延迟您的 Javascript,但可以肯定的是,它可能无助于清除关键渲染路径。您应该使用 defer 或 async,而不是使用 SetTimeOut。
您可以在此处详细了解Critical Rendering Path。主线程是您的浏览器运行的主要进程,用于处理和呈现页面上的 CSS、Javascript、HTML 的大部分工作。关键的渲染路径是“浏览器将 HTML、CSS 和 JavaScript 转换为屏幕上的像素的一系列步骤”。 - 引用自Critical Rendering Path。关键渲染路径是您的 Javascript、HTML、CSS、图像和其他资源被下载和渲染的顺序。优化关键渲染路径需要大量知识,这不是一件容易的事。但是,您可以尝试在脚本标签中使用两个属性,即“async”和“defer”来控制何时执行 Javascript。
看看这张图片:
致谢:与网络一起成长
https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/loading-third-party-javascript/?utm_source=lighthouse&utm_medium=unknown
如您所见,您可以尝试将async 属性放入您的脚本或将defer 属性放入您的脚本标签中,看看是否有帮助。
在脚本标签中带有'async'属性,这意味着您的Javascript将在下载后立即异步执行。如图所示,<script async> 下方的蓝色条显示在解析 HTML 的同时下载了脚本,因为可以看到绿色条和蓝色条并行执行。脚本下载完成后,立即执行脚本。此时,HTML 解析会暂停,直到脚本执行完毕。而如果没有 'async' 属性,您的 HTML 解析将在下载和执行脚本时暂停(或阻止)。
在脚本标签中使用'defer' 属性,这意味着您将推迟执行 Javascript,直到 DOM 完成解析。虽然浏览器一收到javascript资源就会下载,但是下载不会阻塞HTML解析。
总之,您可以在您的第三方脚本中使用“异步”属性在一定程度上“解除阻塞”您的主线程,以便在渲染 DOM 时在后台下载和执行主线程。这将加快主线程的速度。但是需要注意的是,执行仍将是渲染阻塞的。需要注意的一个非常重要的事情是,通过使用“异步”属性,准备好查看页面的一些可能的不稳定行为,因为现在可能会发生更多“不一致”,因为现在可以在渲染路径中随时执行 Javascript,因此如果在脚本之前或之后需要发生一些事情,你可能会破坏流程和它的逻辑。
或者您可以在您的第三方脚本中使用“defer”属性来告诉您的脚本只有在 DOM 完全加载后才能执行。这只能稍微加快这个过程,只是一点点,因为现在可以在 HTML 解析发生时并行下载脚本,而不是使用默认脚本标记而不指定 defer 或 async,但执行仍将继续占用主线程的开销。
根据 Google 的支持文档,How do you load third-party script efficiently? 上有一个部分,这里有几种方法:
"
-
使用 async 或 defer 属性加载脚本以避免阻塞文档解析。
-
如果第三方服务器速度较慢,请考虑自行托管脚本。
-
如果脚本不能为您的网站增加明确的价值,请考虑删除它。
-
考虑使用 <link rel=preconnect> 或 <link rel=dns-prefetch> 之类的资源提示来对托管第三方脚本的域执行 DNS 查找。
"
其他方法:
了解如何将各种 Javascript 文件压缩、缩小或合并到一个文件中(如果您使用的是文件形式的 Javascript)。使用GZIP compression 压缩您的Javascript、CSS。另请查看如何使用 CDN(内容分发网络/内容分发网络)等加载第三方脚本。
2020 年 8 月 12 日更新:
在回复您的评论时,由于您提到您的第三方脚本来自您无法将“异步”或“延迟”属性编码到脚本标签中的插件,您可以考虑在其他脚本之前添加它:
<script>
// If your script tag has an id, use either one below:
document.getElementById("your_script_tag_id").async = true;
document.getElementById("your_script_tag_id").defer = true;
// If your script tag has a class name, use either one below:
document.getElementsByClassName("your_script_tag_class_name")[0].async = true;
document.getElementsByClassName("your_script_tag_class_name")[0].defer = true;
// If for once and for all scripts, use either one below:
document.getElementsByTagName("script")[0].async = true;
document.getElementsByTagName("script")[0].defer = true;
</script>
您也可以查看:Async JavaScript,这允许您延迟或异步您的 Javascript,包括第三方的。