为现代浏览器提供现代代码,以加快页面加载速度

构建在所有主流浏览器上都能良好运行的网站是开放 Web 生态系统的核心原则。但是,这意味着需要额外的工作来确保您编写的所有代码在您计划定位的每个浏览器中都受支持。如果您想使用新的 JavaScript 语言功能,则需要将这些功能转译为向后兼容的格式,以供尚不支持它们的浏览器使用。

Babel 是最广泛使用的工具,用于将包含较新语法的代码编译为不同的浏览器和环境(例如 Node)可以理解的代码。本指南假定您正在使用 Babel,因此您需要按照设置说明将其包含到您的应用程序中(如果您尚未这样做)。如果您在应用中使用 webpack 作为模块打包器,请在 Build Systems 中选择 webpack

要使用 Babel 仅转译用户需要的内容,您需要

  1. 确定您要定位的浏览器。
  2. @babel/preset-env 与适当的浏览器目标一起使用。
  3. 使用 <script type="module"> 停止向不需要它的浏览器发送转译后的代码。

确定您要定位的浏览器

在开始修改应用程序中代码的转译方式之前,您需要确定哪些浏览器访问您的应用程序。分析您的用户当前正在使用的浏览器以及您计划定位的浏览器,以便做出明智的决定。

使用 @babel/preset-env

转译代码通常会导致文件大小比原始形式更大。通过最大限度地减少您执行的编译量,您可以减小捆绑包的大小,从而提高网页的性能。

Babel 没有包含特定的插件来有选择地编译您正在使用的某些语言功能,而是提供了一些将插件捆绑在一起的预设。使用 @babel/preset-env 仅包含您计划定位的浏览器所需的转换和 polyfill。

@babel/preset-env 包含在 Babel 配置文件 .babelrcpresets 数组中

{
 "presets": [
   [
     "@babel/preset-env",
     {
       "targets": ">0.25%"
     }
   ]
 ]
}

使用 targets 字段通过向 browsers 字段添加适当的查询来指定您要包含的浏览器版本。@babel/preset-env 与 browserslist 集成,browserslist 是不同工具之间共享的用于定位浏览器的开源配置。兼容查询的完整列表位于 browserslist 文档中。另一种选择是使用 .browserslistrc 文件列出您希望定位的环境。

">0.25%" 值告诉 Babel 仅包含支持全球使用率超过 0.25% 的浏览器所需的转换。这确保您的捆绑包不包含非常小比例用户使用的浏览器的不必要的转译代码。

在大多数情况下,这比使用以下配置更好

  "targets": "last 2 versions"

"last 2 versions" 值将您的代码转译为每个浏览器的最后两个版本,这意味着为已停产的浏览器(如 Internet Explorer)提供支持。如果您不希望这些浏览器用于访问您的应用程序,这可能会不必要地增加您的捆绑包大小。

最终,您应该选择适当的查询组合,仅定位符合您需求的浏览器。

启用现代错误修复

@babel/preset-env 将多个 JavaScript 语法功能分组到集合中,并根据指定的目标浏览器启用/禁用它们。虽然这效果很好,但当目标浏览器仅包含单个功能中的错误时,就会转换整个语法功能集合。这通常会导致比必要更多的转换代码。

最初作为单独的预设开发,@babel/preset-env 中的 bugfixes 选项通过将某些浏览器中损坏的现代语法转换为在这些浏览器中未损坏的最接近的等效语法来解决此问题。结果是几乎相同的现代代码,只有一些小的语法调整,保证了所有目标浏览器的兼容性。要使用此优化,请确保您已安装 @babel/preset-env 7.10 或更高版本,然后将 bugfixes 属性设置为 true

{
 "presets": [
   [
     "@babel/preset-env",
     {
       "bugfixes": true
     }
   ]
 ]
}

在 Babel 8 中,bugfixes 选项将默认启用。

使用 <script type="module">

JavaScript 模块或 ES 模块是 所有主流浏览器中都支持的相对较新的功能。您可以使用模块来创建可以从其他模块导入和导出的脚本,但您也可以将它们与 @babel/preset-env 一起使用,仅定位支持它们的浏览器。

与其查询特定的浏览器版本或市场份额,不如考虑在 .babelrc 文件的 targets 字段中指定 "esmodules" : true

{
   "presets":[
      [
         "@babel/preset-env",
         {
            "targets":{
               "esmodules": true
            }
         }
      ]
   ]
}

许多使用 Babel 编译的较新的 ECMAScript 功能在支持 JavaScript 模块的环境中已受支持。因此,通过这样做,您可以简化确保仅将转译后的代码用于实际需要的浏览器的过程。

支持模块的浏览器会忽略带有 nomodule 属性的脚本。相反,不支持模块的浏览器会忽略带有 type="module" 的 script 元素。这意味着您可以同时包含模块和编译后的后备方案。

理想情况下,应用程序的两个版本脚本应如下所示包含

  <script type="module" src="main.mjs"></script>
  <script nomodule src="compiled.js" defer></script>

支持模块的浏览器会获取并执行 main.mjs 并忽略 compiled.js。不支持模块的浏览器则相反。

如果您使用 webpack,则可以在配置中为应用程序的两个单独版本设置不同的目标

  • 仅适用于支持模块的浏览器的版本。
  • 包含可在任何旧版浏览器中运行的编译脚本的版本。由于转译需要支持更广泛的浏览器,因此文件大小更大。

感谢 Connor Clark 和 Jason Miller 的评论。