为现代浏览器提供现代代码以加快页面加载速度
构建在所有主流浏览器上都能正常运行的网站是开放网络生态系统的核心原则。但是,这意味着要确保您编写的所有代码在您计划定位的每个浏览器中都受支持的额外工作。如果您想使用新的 JavaScript 语言功能,您需要将这些功能转换为尚不支持它们的浏览器的向后兼容格式。
Babel是使用最广泛的工具,用于将包含较新语法的代码编译成不同浏览器和环境(例如 Node)可以理解的代码。本指南假设您正在使用 Babel,因此如果您还没有使用 Babel,则需要按照设置说明将其包含到您的应用程序中。如果您在应用程序中使用 webpack 作为模块捆绑器,请选择 webpackin 构建系统。
有趣的事实:Lebab 是一个独立的库,与 Babel 的功能相反。它将旧代码转换为新语法。
要使用 Babel 仅转换用户所需的内容,您需要:
- 确定您要定位的浏览器。
@babel/preset-env与适当的浏览器目标一起使用。- 用于
<script type="module">停止向不需要它的浏览器发送转译代码。
1. 确定您要定位的浏览器
在开始修改应用程序中代码的转译方式之前,您需要确定哪些浏览器访问您的应用程序。分析您的用户当前正在使用的浏览器以及您计划针对的浏览器以做出明智的决定。
2. 使用 @babel/preset-env
转译代码通常会导致文件大小比其原始形式大。通过最大限度地减少编译量,您可以减小包的大小,从而提高网页的性能。
Babel 没有包含特定的插件来选择性地编译您正在使用的某些语言功能,而是提供了许多将插件捆绑在一起的预设。使用@babel/preset-env仅包含您计划定位的浏览器所需的转换和 polyfill。
在 Babel 配置文件 @babel/preset-env 的数组中包含:presets.babelrc
{
"presets": [
[
"@babel/preset-env",
{
"targets": ">0.25%"
}
]
]
}
通过向该字段添加适当的查询,使用该 targets 字段指定要包括的浏览器版本 browsers。@babel/preset-env 与 browserslist 集成,这是针对浏览器的不同工具之间共享的开源配置。兼容查询的完整列表在browserslist 文档中。另一种选择是使用.browserslistrc文件列出您希望定位的环境。
该 ">0.25%" 值告诉 Babel 只包含支持占全球使用量超过 0.25% 的浏览器所需的转换。这可确保您的包不包含为极少数用户使用的浏览器的不必要的转译代码。
在大多数情况下,这是比使用以下配置更好的方法:
"targets": "last 2 versions"
该 "last 2 versions" 值会为每个浏览器的最后两个版本转换您的代码,这意味着为已停产的浏览器(例如 Internet Explorer)提供支持。如果您不希望这些浏览器用于访问您的应用程序,这可能会不必要地增加您的包的大小。
最终,您应该选择适当的查询组合,以仅针对适合您需求的浏览器。
3. 启用现代错误修复
@babel/preset-env 将多个 JavaScript 语法功能分组到集合中,并根据指定的目标浏览器启用/禁用它们。尽管这很有效,但是当目标浏览器包含一个只有一个特性的错误时,整个语法特性集合就会被转换。这通常会导致转换后的代码比必要的要多。
最初作为单独的预设开发,错误修复选项通过将在 @babel/preset-env 某些浏览器中损坏的现代语法转换为在这些浏览器中未损坏的最接近的等效语法来解决此问题。结果是几乎相同的现代代码,只是进行了一些小的语法调整,以保证在所有目标浏览器中的兼容性。要使用此优化,请确保您已@babel/preset-env安装 7.10 或更高版本,然后将bugfixes属性设置为true:
{
"presets": [
[
"@babel/preset-env",
{
"bugfixes": true
}
]
]
}
在 Babel 8 中,该 bugfixes 选项将默认启用。
4. 使用 <script type="module">
JavaScript 模块或 ES 模块是所有主流浏览器都支持的相对较新的功能。您可以使用模块来创建可以从其他模块导入和导出的脚本,但您也可以使用它们@babel/preset-env来仅针对支持它们的浏览器。
与其查询特定的浏览器版本或市场份额,不如考虑 "esmodules" : true 在 .babelrc 文件 targets 字段中指定。
{
"presets":[
[
"@babel/preset-env",
{
"targets":{
"esmodules": true
}
}
]
]
}
许多使用 Babel 编译的较新的 ECMAScript 功能已经在支持 JavaScript 模块的环境中得到支持。因此,通过这样做,您可以简化确保只有转译代码用于实际需要它的浏览器的过程。
支持模块的浏览器会忽略带有 nomodule 属性的脚本。相反,不支持模块的浏览器会忽略带有 type="module"。这意味着您可以包含一个模块以及一个已编译的后备。
理想情况下,应用程序的两个版本脚本包含如下:
<script type="module" src="main.mjs"></script>
<script nomodule src="compiled.js" defer></script>
支持模块的浏览器获取、执行 main.mjs 和忽略 compiled.js。不支持模块的浏览器则相反。
模块脚本默认是延迟的。该 defer 属性被添加到 nomodule 脚本中以实现相同的行为。
如果您使用 webpack,您可以在配置中为应用程序的两个不同版本设置不同的目标:
- 仅适用于支持模块的浏览器的版本。
- 包含可在任何旧版浏览器中运行的已编译脚本的版本。这具有更大的文件大小,因为转译需要支持更广泛的浏览器。
尽管这种 HTML 方法可以提供性能优势,但已发现某些浏览器在同时指定模块和 nomodule 脚本时会重复获取。Jason Miller 的 Modern Script Loading 对此进行了更详细的解释,并涵盖了一些可用于规避此问题的选项。
感谢 Connor Clark 和 Jason Miller 的评论。