阅读 471

vite 打包中 <link rel="modulepreload">的深入理解

最近在试着用vite重新构建vue项目

看到index里面有一行 <link link rel="modulepreload" href="./assets/vendor.2a8f1291.js"> 没见过此种用法,翻译一篇文章Preloading modules

浏览器最近已经原生的支持JS modules, 包括静态和动态的import,这意味着现在们可以在浏览器内直接基于模块编写JS不用编译和打包。

基于模块的开发在可缓存性方面提供了一些真正的优势,帮助你减小代码体积。通过让您对应用程序中的关键代码进行优先级排序,代码的更细粒度也有助于解决加载过程。

但是,模块依赖项会带来加载问题,因为浏览器需要先等待模块加载,然后才能找到其依赖项。解决此问题的一种方法是预加载依赖项,以便浏览器提前知道所有文件并可以保持连接。

到目前为止,还没有真正好的方法以声明方式预加载模块。Chrome 64附带“实验性Web平台功能”标志后的<link rel =“ modulepreload”>。 <link rel =“ modulepreload”>是<link rel =“ preload”>特定于模块的版本,它解决了后者的许多问题。

what what's <link rel="preload">

Chrome在50版中添加了对<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>
复制代码

这尤其适用于字体等资源,这些资源通常隐藏在CSS文件中,有时深度很深。当浏览器本来可以利用这段时间开始下载并利用全部连接带宽时,它必须等待多次往返才能发现它需要获取大字体文件。

及其等效的HTTP标头提供了一种简单的声明式方法,使浏览器可以立即了解当前导航的一部分所需的关键文件。当浏览器看到预加载时,它将开始对该资源的高优先级下载,因此,在实际需要时,它已经被获取或部分存储在其中。

为什么 <link rel =“ preload”>对module不生效?

这是棘手的。源有多种凭证模式,为了获得高速缓存命中,它们必须匹配,否则最终将获取资源两次。不用说,双重获取是不好的,因为它浪费用户的带宽并使他们等待更长的时间,这没有充分的理由。

对于<script><link>标签,可以使用crossorigin属性设置凭据模式。但是,事实证明,没有crossorigin属性的<script type =“ module”>不具有凭据模式,对于<link rel =“ preload”>.这意味着您必须将

此外,获取文件只是实际运行代码的第一步。首先,浏览器必须解析和编译它。理想情况下,这也应该提前发生,以便在需要模块时,代码就可以运行了。但是,V8(Chrome的JavaScript引擎)解析和编译模块的方式与其他JavaScript不同.<link rel =“ preload”>没有提供任何方式表明正在加载的文件是模块,因此浏览器所能做的就是加载文件并将其放入缓存。使用<script type =“ module”>标记加载脚本(或由其他模块加载)后,浏览器将代码解析并编译为JavaScript模块。

那么对于模块来说,<link rel =“ modulepreload”>只是<link rel =“ preload”>吗?

简而言之,是的。通过为预加载模块使用特定的链接类型,我们可以编写简单的HTML,而不必担心我们使用的是哪种凭据模式。默认值可以正常工作。

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

而且由于Chrome现在知道您要预加载的是模块,因此它可以很聪明地在完成获取操作后立即解析并编译该模块,而不必等到它尝试运行为止。

但是模块的依赖关系又如何呢?

有趣的你应该问!实际上,我们还没有讨论过:递归。

实际上,<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>
复制代码

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

通过向浏览器告知需要获取的内容,预加载可以帮助最大程度地利用带宽,从而避免在长途往返期间无所事事。

如果您正在试验模块并由于深度依赖树而遇到性能问题,那么创建平面预加载列表肯定会有所帮助!

也就是说,模块性能仍在研究之中,因此请确保使用开发人员工具仔细查看应用程序中发生的情况,并考虑同时将应用程序捆绑为几个块。

文章分类
前端