优化 JavaScript 资源以提高页面速度

已发表: 2020-05-05

下面的示例来自一个大型、复杂的新闻网站。 他们多年来一直在失去自然流量。 它们的 DOMContentLoaded 事件时间为 2615.2 MS。 您可能认为他们的 DOM 大小非常庞大,但没有……

和谷歌推荐的差不多,本文档中只有 1230 个 HTML 节点。

您可以使用 DevTools 计算您的 domContentLoaded 事件时间和流程,并将其与您的竞争对手进行比较。

检查此示例表明 DOM 大小不一定是关键点。 这里,主要问题是资源顺序:“主选项卡”中的蓝色部分用于 HTML 解析。 但是,该网站在 HTML 解析过程完成之前通过 JavaScript 渲染中断了浏览器。

(您也可以使用调用树部分来帮助您的 IT 团队找到类似的错误。)

这个例子清楚地说明了优化 JavaScript 资产的重要性,以及当您在页面速度优化中忽略 JavaScript 时会出现什么问题。

这是四篇系列文章中的第三篇。 为了更好地理解本文,您可能需要阅读该系列的前两篇文章:

  • Javascript 渲染和页面速度与浏览器的渲染引擎如何创建网页密切相关。
  • 在阅读本文之前,您还应该了解高级页面速度指标。

我将使用前两篇文章中的一些示例来帮助提供本文的上下文。

什么是 Javascript 渲染以及它如何影响您的页面速度?

Javascript 渲染是最后一个页面加载部分,它可以交互地更改使用 DOM 和 CSSOM 创建的结构。 任何页面元素都可以以用户可触发的格式更改或正常显示。 任何具有 display:none 属性且无法被渲染树访问的元素都可以使用 JavaScript 渲染为可见,或者通过不同的 HTML 元素注入到 DOM 中。

JavaScript 会中断 DOM 和 CSSOM,因为它会在浏览器读取 DOM 和 CSSOM 时更改它们。 因此,为了避免对页面加载时间和速度产生负面影响,有必要检查 DOM、CSSOM 和 JavaScript 渲染之间的关系。

上面是一个渲染树的例子。 CSSOM 和 HTML 节点中的所有互锁代码片段在渲染树中都有语义等价物。 如果您仔细观察,您会注意到“Action Button”HTML 节点不在渲染树中。 主要原因是“display:none;” CSS 属性。 由于这个不可见命令,它不包含在渲染树中。 要了解此树中的元素是如何构建的,您可能需要阅读本系列的第一篇文章。

如果您有许多页面元素不会在第一次加载时出现,因为它们取决于用户行为,那么在资源加载顺序中,您必须将这些项目分开并将它们放在最后一行。 此时使用影子 DOM 或虚拟 DOM 是更好的选择。

JavaScript 资源的延迟和异步属性

如果您将 JS 文件放入该部分并且不使用 'defer' 或 'async' 属性,它可能会延迟您的 DOMContentLoaded 时间。 为了防止这种情况,我们可以使用这两个属性。 Defer 是延迟 JS 文件的加载过程,而 'Async' 是并行加载 JS 和其他源。 两者都有优点和缺点。 我们将在这里只讨论主要的。

  • 如果您在主 JS 文件上使用 defer,则在安装之前您可能不会看到它的“启动器”效果。
  • 如果使用 defer 过多,可能会导致页面加载结束时出现 CPU 瓶颈。

自撰写本文以来,Chrome 80 更新已发布。 在 Initiator 列中,现在更容易查看哪个资源被哪个资源调用。 例如,你可以看到一个由 JS 调用的图像或 CSS 文件。 如果您通过按住 shift 键滚动资源,您还将看到在不加载其他资源的情况下无法使用哪个资源。

按住 shift 键滚动:红色表示以绿色突出显示的资源的条件资源。

您还可以使用 Chrome 中的新发起程序部分来获得更详细的资源加载顺序、发起程序和优先级审查。 这使您可以检测到极长且成本高昂的 JS 调用链,如下所示。

来自同一站点的长且昂贵的 JS 调用链示例。 所选资源上方是其启动器。 下一部分显示由所选资源启动的资源。

  • 延迟的 JS 文件是在 domInteractive 事件之后下载的,所以你需要根据你的 CSS 文件和图片来选择它们。
  • 如果您延迟某些用户跟踪器 3rd 方 JS 文件,您可能无法跟踪某些用户行为。
  • Defer 通常不会阻塞 DOM 进程,但 Async 会。 具有异步属性的 JS 文件在 HTML 解析和 CSSOM 处理期间由浏览器下载。
  • 如果你过多地使用 async 属性,可能会造成 CPU 处理瓶颈,并且会减慢 DOM 和 CSSOM 进程。 您需要仔细选择要延迟或异步的内容。

这是 async 和 defer 属性的示例方案。 第一个在 domContentLoaded 之前加载,在获取期间不拆分 HTML 解析。 第二种,在 HTML 解析完成之前,不执行获取的 JS 文件。

Javascript 渲染和性能的建议和技巧

在开始实践示例之前,这里有一些提高 JavaScript 渲染性能的建议。 这也可能有助于更好地了解页面速度和浏览器的工作方式。

不要使用不必要的变量。

如果您是 SEO,您可能会注意到 JavaScript 文件中不必要或未使用的变量。 有许多工具可用于检测此类错误。 您将在下面找到未使用和不必要的变量的两个基本示例。

var carName=品牌+””+年份;
document.getElementById(“demo”).innerHTML = carName;

这里,变量“carName”是不必要的。 您可以建议进行以下修改:
document.getElementById(“demo”).innerHTML = 品牌+ ” ” + 年份

或者:

[a, b, c, d, e].forEach(function (value, index) {
控制台.log(索引);
});

这里,“value”参数不是必需的,因为它没有被使用。 您可以删除它:
[a, b, c, d, e].forEach(function (index) {
控制台.log(索引);
});

在右侧,您可以看到更长的连接时间(白线),并且由于“异步”Javascripts,CSS 和 JS 文件以不对称的顺序加载。

在左侧,连接时间更短,并且 CSS 和 JS 文件没有混合,因为每个源都是按行加载的。 异步属性会降低您的 Speed Index,因为它可以延长 TBT 时间,因此您需要进行调查并将其报告给您的开发团队,以获取性能选项卡中的性能跟踪器 JS 文件,或者您可以自己运行一些实验。

使用工具完成困难的任务

对于代码初学者来说,找到不必要或未使用的变量可能很困难。 您可能希望使用一些工具来完成这些任务,例如 Chrome DevTools 或 Node.js 包,例如 Unused(Kami/node-unused:一个报告代码中已定义但未使用的变量的模块。或更多未使用的变量)。 如果您发现甚至是一些小错误,我相信您的 IT 团队会倾听您的意见,让您的 JavaScript 文件变得更好。

使用 Chrome DevTools 覆盖率报告查找未使用的 JavaScript 代码

Chrome DevTools 覆盖率报告显示了未使用的 JavaScript 代码片段,但它不是很实用。 您可能认为您可以从代码中删除每个红色部分,但事实并非如此……相反,您应该为大量类别页面找到完全未使用的函数或变量。 这样,您的开发团队可以被说服使用 TreeShaking 过程。

TreeShaking 意味着从文件中删除死代码。 我建议学习使用未使用的 JS 变量和函数查找器包,以获得时间。

较小的 DOM 大小也有助于 JavaScript 渲染。 每个 (getElementsByTagName) 命令都会扫描你的 DOM。 在呈现 JavaScript 时,较小的 DOM 大小将需要较少的浏览器资源和设备的 CPU/网络。

随着新的 Chrome 80 更新,覆盖报告也发生了变化。 他们添加了可选的 Per Function 和 Per Block 选项。 每块是这里的默认值。

如果您选择 Per Function,您会在报告中看到很大的不同。 出现这种情况的主要原因是 Per Function 视图检查是否所有函数都在使用。 如果正在使用 95% 的函数,则 Per Function 选项会将其定义为未使用的代码,因为 5% 的代码未使用,尽管函数的大部分已使用。

压缩、缩小或丑化您的 JS 文件。

这可以通过两种方式完成。 首先,删除空格和不必要的注释。 其次,为您的 JS 文件使用改进的 JavaScript 运算符,并使用现成的技术来修改名称、变量、函数。

您应该了解用于这种压缩的箭头函数。 例如,代替这个 84 个字符的示例:

函数箭头 Islev (a, b) {
控制台.log (a + b);
}
箭头Fonksiyon (5, 6);

您可以使用箭头函数将其压缩为仅 50 个字符 =>
常量 ab = (a, b) => b + a;
console.log (ab (5, 6));

另一种缩短/压缩方法对 If 语句有效。 而不是这个 63 个字符的代码片段:
如果 (a<b) {
控制台.log(ab);
}
别的 {
控制台.log(a+b);

您可以使用下面的 43 个字符:
(a<b) ? 控制台.log(ab) : 控制台.log(a+b);

您还可以向您的 IT 团队建议他们使用 $ 和 _ 符号进行压缩。 大多数 JavaScript 文件都用于重新解释 DOM。 对此,您可能会看到很多 document.getElementById(x); 文件中的代码片段。 您可以为此任务使用 $ 符号。 这将使您免于一大堆无用的大小。

大多数 JavaScript 库默认使用 $ 来定义函数,但不是全部,因为 $ 也是一个字母字符。

在这种情况下,您可能会建议您的 IT 团队使用:
函数 $(x) {return document.getElementById(x);} 。

使用适当的渲染类型

就 SEO 兼容性而言,JavaScript 和网页呈现类型。
SSR Hydration 意味着一些 JS 组件将使用客户端渲染进行渲染。 它对 FP 和 FMP 很有用,但 TTI 和速度指数得分可能有一些缺点。
图片来源:Notprovided.eu

JavaScript 渲染性能的编码最佳实践

  • 另一个重要的压缩贡献来自“_”的使用。 您可以使用“underscore.js”来改进 JavaScript 编写格式和功能。 通过这种方式,您将创建更小的 JS 文件,同时使用更短的 JS 函数来操作列表和集合,而无需内置 JS 函数。
  • 使用大量冗长而费力的变量更改和全局变量污染也是渲染缓慢的根源。 您应该确定函数的一般范围选择和全局变量/长变量类型。 使用带有“Let”的局部变量更适合渲染。 由于局部变量,浏览器不会审核其他全局函数变量以进行下一次更改。

为了模拟更真实的性能检查,例如您可能在低端手机上看到的内容,您应该使用 Chrome DevTools 中的 CPU Throttling 和 Fast/Slow 3G Connection 首选项。
图片来源:Addy Osmani

  • 使用较小的 JS 函数和变量链将有助于您的渲染性能。 此外,使用“this”选择器而不是“with”选择器会更好。 Javascript 中的“this”选择器是一个本地功能代码,与“with”不同,let 和 var 的相同逻辑在这里也有效。
  • 如果你在 For 循环代码片段中使用你的语句,这也会稍微降低你的渲染速度。 因为您的函数语句将遍历循环的每个元素。 您可以简单地为循环元素创建一个新变量,并且可以使用 For 循环之外的函数调用这些元素。
  • 如果你想多次访问一个 HTML 元素,你可以为它创建一个变量,然后你可以用你想要的函数调用它。 使用 JavaScript 访问 HTML 元素并不是一个快速的过程。 您可能只是为您的浏览器增加了负担。

提高 Javascript 渲染性能的另一种方法是通过 Service Worker 进行三态渲染。 您可以将一些 JS 文件放入客户端的浏览器内存中以备将来使用。 这样,您可以使您的网站离线工作。
您可以在这里找到一个简单的演示,以便更好地理解和与服务人员一起练习的机会

绑起来:​​优化 JavaScript 和页面速度如何影响 SEO

为了防止 JavaScript 资产破坏您的页面速度,我们已经看到了 Defer 和 Async 如何产生巨大的影响。 我们还研究了一些“调试”策略和编码技巧,它们可以帮助您使用 JavaScript 资源提高页面的速度。

既然我们已经了解了浏览器如何构建网页,页面速度是如何衡量和影响的,以及优化 JavaScript 在页面加载时间中所起的作用,下一篇文章将演示资源加载顺序如何影响页面速度和爬取预算。

如果您有兴趣回顾本系列四篇文章中的前几篇文章,可以在这里找到它们:

  • “浏览器如何创建网页”
  • “高级页面速度指标”。
开始免费试用