预加载模块

Sérgio Gomes

基于模块的开发在可缓存性、 帮助您减少需要向用户配送的字节数。 更细粒度的代码也有助于故事加载, 确定应用中关键代码的优先级。

不过,模块依赖关系会导致加载问题,因为浏览器需要 在模块确定其依赖项之前,先等待模块加载。单程 方法是预加载依赖项 文件,并可使连接保持忙碌状态。

<link rel="preload"> 是一种提前以声明方式请求资源的方法, 从而避免浏览器需要这些标记

<head>
  <link rel="preload" as="style" href="critical-styles.css">
  <link rel="preload" as="font" crossorigin type="font/woff2" href="myfont.woff2">
</head>

浏览器支持

  • Chrome:50。 <ph type="x-smartling-placeholder">
  • 边缘:≤ 79。 <ph type="x-smartling-placeholder">
  • Firefox:85。 <ph type="x-smartling-placeholder">
  • Safari:11.1. <ph type="x-smartling-placeholder">

来源

这对字体等资源特别有用,因为资源通常处于隐藏状态 (有时会有好几级深度)在这种情况下 就必须等待多次往返才会发现 当它本来可以利用该时间开始下载时,会提取大型字体文件 并充分利用全部连接带宽

<link rel="preload"> 及其 HTTP 标头等效项提供了一种简单的声明式 让浏览器立刻知道需要的关键文件 作为当前导航的一部分当浏览器发现预加载内容时,会启动一个高 这样,当资源真正需要时,资源就会 已提取或已部分提取到该位置。但不适用于模块。

这正是棘手的地方。我们提供多种凭据模式, 它们必须匹配才能获得缓存命中,否则 提取该资源两次。毋庸置疑,双重提取是非常糟糕的 会浪费用户的带宽,导致他们无端等待更长时间。

对于 <script><link> 标记,您可以使用 crossorigin 设置凭据模式 属性。不过,结果是没有指定字段的 <script type="module"> crossorigin 属性指示 omit 的凭据模式,该模式不存在 价格为 <link rel="preload">。这意味着 将 <script><link> 中的 crossorigin 属性都更改为一 但如果您的目标对象是其他值, 依赖于其他模块的依赖项。

此外,获取文件只是实际运行代码的第一步。 首先,浏览器必须解析并编译该文件。理想情况下 也应提前进行此操作 运行状态。不过,V8(Chrome 的 JavaScript 引擎)可解析和编译模块, 与其他 JavaScript 不同。<link rel="preload"> 不可以 提供任何方式来指明正在加载的文件是一个模块,因此所有浏览器 加载文件并将其放入缓存使用 <script type="module"> 标记(或由其他模块加载),则浏览器会解析 并将代码编译为 JavaScript 模块。

简而言之,是的。通过为预加载模块使用特定的 link 类型,我们能够 编写简单的 HTML 时,无需担心我们使用的是何种凭据模式。通过 使用默认设置。

<head>
  <link rel="modulepreload" href="super-critical-stuff.mjs">
</head>
[...]
<script type="module" src="super-critical-stuff.mjs">

由于浏览器现在知道您要预加载的是模块,因此 加载完成后立即解析和编译模块,而不是等待 直到应用尝试运行为止

浏览器支持

  • Chrome:66。 <ph type="x-smartling-placeholder">
  • 边缘:≤ 79。 <ph type="x-smartling-placeholder">
  • Firefox:115。 <ph type="x-smartling-placeholder">
  • Safari:17. <ph type="x-smartling-placeholder">

来源

那么模块的依赖项?

真有趣,因为你应该这么问!确实有一个本文未涉及的内容:递归。

实际上,<link rel="modulepreload"> 规范允许视需要加载,而不仅仅是 请求的模块及其所有的依赖关系树。浏览器不必 但可以。

那么,预加载模块的最佳跨浏览器解决方案是什么? 因为您需要完整的依赖关系树来运行应用?

选择以递归方式预加载依赖项的浏览器应具有强大的去重功能 模块,因此一般而言,最佳实践是声明模块和扁平列表 并确保浏览器不会重复提取同一个模块。

<head>
  <!-- dog.js imports dog-head.js, which in turn imports
       dog-head-mouth.js, which imports dog-head-mouth-tongue.js. -->
  <link rel="modulepreload" href="dog-head-mouth-tongue.mjs">
  <link rel="modulepreload" href="dog-head-mouth.mjs">
  <link rel="modulepreload" href="dog-head.mjs">
  <link rel="modulepreload" href="dog.mjs">
</head>

预加载模块是否有助于提升性能?

预加载可以告知浏览器 因此它需要获取数据,以免在如此长的往返期间无任何操作 如果您正在测试模块并因深度优化而遇到性能问题 依赖项树,那么创建一个扁平的预加载列表肯定会有所帮助。

尽管如此,模块性能仍在开发中,因此请确保 您可以使用开发者工具来仔细观察应用中发生的情况 在此期间,不妨考虑将您的应用捆绑成几个块。这里有很多 但 Chrome 中正在进行的模块工作 因此,我们离提供 捆绑工人赚取丰厚的休息时间!