美文网首页web前端技术分享
弄清preload、prefetch、dns-prefetch、

弄清preload、prefetch、dns-prefetch、

作者: microkof | 来源:发表于2021-01-15 13:54 被阅读0次

前言

preload和prefetch是现代浏览器对<link>标签新增的rel值,用来加快页面资源(通常是css和js)的加载速度,改善用户体验。比如<link href="./2.js" rel="preload" as="script" />

网络上对于preload和prefetch的讲解其实很多,看起来也很专业,但是看完了还是一头雾水。这次我来讲解一下。

与<link>常见用法的区别

之前常见的<link>是引用css,比如<link rel="stylesheet" href="https://xxx/ooo.css" type="text/css">,现在rel值由stylesheet换成了preload或prefetch,会有什么区别呢?

  1. <link>在常用用法里无法加载js,只能加载css,但是如果使用prefetch和preload,就可以加载js。

  2. 无论是preload还是prefetch,都是只下载资源,不执行资源,可以简单测试一下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link href="./1.css" rel="preload" as="style" />
  <link href="./2.js" rel="preload" as="script" />
  <link href="./1.css" rel="prefetch" as="style" />
  <link href="./2.js" rel="prefetch" as="script" />
  <title>Document</title>
</head>
<body>
  <div class="xx">ABCDEFG</div>
</body>
</html>

另准备css文件:.xx{color: red;}和js文件:alert(123)。在服务器或本地环境打开html,你会发现,尽管浏览器下载了文件,但是并没有执行。

  1. prefetch和preload不影响window.onload事件

也就是说,window.onload事件并不会去管prefetch和preload的资源有没有下载完毕,就当prefetch和preload的资源不存在。

prefetch和preload实际范例

比如我创建一个Vue CLI项目,一字不改,直接build项目,index.html会有这种代码:

    <link href="/js/about.90b284f2.js" rel="prefetch" />
    <link href="/css/app.b00e02bd.css" rel="preload" as="style" />
    <link href="/js/app.c5cc92e4.js" rel="preload" as="script" />
    <link href="/js/chunk-vendors.c161a506.js" rel="preload" as="script" />
    <link href="/css/app.b00e02bd.css" rel="stylesheet" />

其中前4行都是预加载的资源,只下载不执行,只有第5行是正常引入css并执行。而且有一个现象是,第2行和第5行的css资源是一样的,区别是第2行是preload。为什么这样写?下文我介绍。

preload

写法

一个典型的preload是这样写的:

<link rel="preload" as="script" href="./2.js" crossorigin="anonymous" onload="handleOnload()" onerror="handlepreloadError()">

  • rel属性值就是preload。
  • as属性用于规定资源的类型,浏览器据此设置请求头里的Accept字段,以便能够使用正常的策略去请求对应的资源;as的值可以取style、script、image、font、fetch、document、audio、video等;如果忽略as属性,或者错误的as属性会使preload等同于XHR请求,浏览器不知道加载的是什么,因此会赋予此类资源非常低的加载优先级,所以一般不应省略as属性。
  • href不多说。
  • crossorigin属性用于请求跨域资源,不加可能会导致资源的二次加载(尤其是font资源)。
  • onload和onerror则分别是资源加载成功和失败后的回调函数,如今网络环境下,通常不用考虑。

网上的错误介绍

有文章介绍是:

Chrome有四种缓存:http cache、memory cache、Service Worker cache和Push cache。在preload或prefetch的资源加载时,两者均存储在http cache。

说法有问题。http cache是总概念,包括disk cache和其他3个缓存。preload或prefetch存储其实是在disk cache,也就是硬盘缓存。

优点

preload可以强行修改资源加载顺序。这是什么意思?先看一张图:

这个表格来自于国外网站,从左到右是最高优先级、高优先级、中、低、最低优先级,这不是硬性标准,各大浏览器的实现不一定一致,但大致是这样。

优先级

这个表格也有让人费解的地方,不过无所谓,我们现在只需要知道,想让资源不按照书写顺序加载,想让某些资源插队加载,就可以用preload。

比如现在,正常资源3.css写在第一位,但是我想让其他资源插队,早于3.css,怎么办?给其他资源设preload即可。

  <link href="./3.css" rel="stylesheet" />
  <link href="./1.css" rel="preload" as="style" />
  <link href="./2.js" rel="preload" as="script" />
  <link href="./1.css" rel="prefetch" as="style" />
  <link href="./2.js" rel="prefetch" as="script" />

实际下载顺序:

image.png

使用场景

  1. 我们依然看上文说的Vue项目,事实上在<body>里还有些代码:
    <div id="app"></div>
    <script src="/js/chunk-vendors.c161a506.js"></script>
    <script src="/js/app.c5cc92e4.js"></script>

这两个资源在<head>里面都曾preload,这样做的好处:提早加载、DOM尾部立即执行。如果没有preload,那么在DOM加载完、js下载完成前会有一瞬间的界面垮掉。

同时你可以发现,app.b00e02bd.css在<head>里出现了2次,这样做是必须的吗?是必须的。因为preload的js会早于正常的css,无论书写顺序,所以为了保证css一定在js之前尽早下载,那么css也必须使用preload。

  1. 介绍一种preload之后立即执行css资源的办法

<link href="./1.css" rel="preload" as="style" onload="this.rel=stylesheet" />这样的话,这个css会当场执行,可以省略下方那行正常引用的代码。

  1. 是不是所有资源都应无脑preload?

肯定不是,在刚进入网站/应用的时候,preload是有必要的,但是后续的页面跳转,就不再需要preload,而是使用另一个rel属性:prefetch。

prefetch

写法

上面有介绍:<link href="/js/about.90b284f2.js" rel="prefetch" />

网上的错误介绍

有文章对于prefetch的介绍是:

prefetch是告诉浏览器页面可能需要的资源,浏览器不一定会加载这些资源。

这话说的就很莫名其妙,浏览器毕竟不是人工智能,所谓的“不一定”,是个什么衡量标准呢?事实上,即便网页被关闭,浏览器对该资源的下载也不会中止,所以,浏览器是一定会加载该资源的。

prefetch到底有什么作用?

它用于加载“未来需要的”资源,而且只有5分钟的生命期,也就是说,浏览器会低优先级下载该资源,拿到资源之后会存入disk cache,未来真的需要它的时候,会从disk cache中读取,以加快页面资源加载速度。

虽然Vue把一个prefetch的资源(也就是about开头的那个js)写在了第一位,但是可以看到,加载它的时候是最后一位(不考虑图片):

下载顺序

当我点击Vue项目的“About”按钮,进入About路由之后,又会请求一次这个资源,这次浏览器会从disk cache拿资源:

2.png

如果页面切换足够快,会不会重复获取资源?

并不会,这是浏览器起码的优化机制。

低优先级是多低?

还是看上面那个表格,看最右侧一列的最下一个单元格,正是prefetch,说明优先级非常之低。

prefetch为什么比logo文件靠前加载?原因我不清楚,毕竟这个图只是民间总结的,而且各个浏览器的实现也不一定相同。

不要对同一个资源使用preload和prefetch

会造成2遍下载。

使用场景

用户极可能继续访问的页面需要的资源,都应该prefetch。所谓“极有可能”需要你根据各种统计数据来猜测,又或者页面是一个列表页,那么用户只有一个选择:点击进入详情页,这时候详情页需要的资源肯定要prefetch。

dns-prefetch

dns-prefetch又是什么?它跟上面两个做的事情不太一样,实际上是强迫浏览器对其他域名(通常就是DNS域名)进行提前解析,以便加快DNS域名的解析速度。

比如你的网站域名叫www.wangbadan.com,你用的很多资源来自于cdn.jsdelivr.net,那么你应该在index.html里最顶部位置写上:

<link rel="dns-prefetch" href="//cdn.jsdelivr.net">

会有一点效果。

defer和async

defer和async都是用在<script>标签上的属性,可以合起来讲解。

无属性 defer async
是否阻塞HTML解析 下载和执行都阻塞 都不阻塞 下载不阻塞,仅在执行时阻塞
按书写顺序下载 否,跟之后的js并行下载 否,跟之后的js并行下载
下载后立即执行 否,在HTML全部解析后、DOMContentLoaded执行前的节点执行
按书写顺序执行 如果后续js没有“无属性”和“async”,则是,否则不是 先下载完的先执行,所以一定是乱序
按下载顺序执行 如果后续js没有“无属性”和“async”,则是,否则不是 是,但是下载是乱序的,所以总体乱序
image.png

使用场景

在如今2021年的前端开发中,事实上defer和async并没有什么使用场景。说的直白点,它们全过时了。为什么?

  1. 上方写的规则其实是靠不住的,因为毕竟依赖浏览器的实现,而实现不完全根据上方写的规则来执行。

  2. 如今前端开发都是脚手架开发,按需加载,由js控制加载顺序才是最可靠的。

  3. defer的初衷是让你可以把js写在<head>里,而无需担心无法操作DOM。但是,一贯的最佳实践都是把js写在</body>之前,所以不存在这个问题。

  4. async虽然下载时不阻塞HTML解析,但是引来了很大的问题就是无法掌握执行时机,async在早年前端开发足够简单的时候是有用的,如今开发是各种依赖包,必须掌握执行时机,所以async根本无法考虑。

  5. 有人说async可以用在完全无依赖的js上,比如Google分析,但事实上永远把Google分析放在最后一位就能解决问题。

相关文章

网友评论

    本文标题:弄清preload、prefetch、dns-prefetch、

    本文链接:https://www.haomeiwen.com/subject/usxtlktx.html