页面资源预加载(preload)和预获取(prefetch)
用了Gatsby也有一段时间了,静态化博客速度也是让我很是满意。这几天在检查seo属性配置时,发现了几个奇怪的标签属性,见是见过,但没这么用过,link标签配了rel="preload"和rel="prefetch",我们常用的link不是用来加载css的么?rel属性不也是stylesheet么?
preload是直接显示在head中的,
而prefetch则是根据滚动条滚动后append的。
preload
预加载,即提前加载页面中需要的资源。他的机制是在页面加载的早期阶段就开始获取,在浏览器的主渲染机制介入前就进行预加载。这样使得资源可以更早的得到加载并可用,且更不易阻塞页面的初步渲染,进而提升性能。
除了机制优势外,对于隐藏在css或js中的资源(css背景图,js操作样式,html中视频资源等等),提前获取,可以避免资源加载造成对页面渲染的延迟。
<link rel="preload" href="./js/1.js" as="script" />
和普通加载css方式类似,不过rel为preload,href则是资源的地址。
as属性
相对于常用的link方式多了个as属性。as属性是用来指定将要预加载的内容的类型,这样可以让浏览器:
- 更精确地优化资源加载优先级。
- 匹配未来的加载需求,在适当的情况下,重复利用同一资源。
- 为资源应用正确的内容安全策略。
- 为资源设置正确的 Accept 请求头。
常用的as属性如下:
- audio: 音频文件。
- document: 一个将要被嵌入到<frame>或<iframe>内部的HTML文档。
- embed: 一个将要被嵌入到
- fetch: 那些将要通过fetch和XHR请求来获取的资源,比如一个ArrayBuffer或JSON文件。
- font: 字体文件。
- image: 图片文件。
- object: 一个将会被嵌入到
- script: JavaScript文件。
- style: 样式表。
- track: WebVTT文件。
- worker: 一个JavaScript的web worker或shared worker。
- video: 视频文件。
注:在chrome79下测试,as属性必须有,否则该link会被忽略。
type属性
type属性表示预加载资源的MIME类型,浏览器将根据type属性来判断它是否支持这一资源,若支持,则会下载,否则便忽略。
as属性只是用来表示资源优先级的,而type才是标记资源类型的,所以当为了更高优先级,我们可以这么写:
<link rel="preload" href="./js/1.js" as="style" />
js文件设置了style类型,提升了优先级。(至于资源优先级,请看这篇文章: Chrome中的资源优先级)
crossorigin属性
当提前预加载的是字体时,就需要该属性(即使不是跨域时)。
<link rel="preload" href="./font/iconfont.ttf" as="font" type="font/ttf" crossorigin="anonymous" />
增加了type后,不同浏览器将仅会对自己支持的字体进行下载。crossorigin则去处理CORS的问题。
更多信息可以查看Font fetching requirements。
media属性
link元素还支持media属性,他能够支持媒体查询。
<link rel="preload" href="./css/bg-1.css" as="style" media="(min-width: 1000px)">
<link rel="preload" href="./css/bg-2.css" as="style" media="(max-width: 1001px)">
他根据当前浏览器窗口的分辨率,预加载不同的css文件。(与media query不一样的是,resize窗口,并不会再去加载另一个样式文件)
兼容性
新特性虽是好用,但是兼容性查看都是绕不过去的,通过caniuse:
图上显示57以上是不支持的,实际是浏览器默认关闭了。实测chrome79是支持的,不过as属性不能省。
关于检测浏览器是否支持preload:
// 是否支持preload
const preloadSupported = () => {
const link = document.createElement('link');
const relList = link.relList;
if (!relList || !relList.supports) {
return false;
}
return relList.supports('preload');
}
脚本化
借助js操作dom,我们可以更灵活的实现资源的预加载和执行:
// 手动预加载
var preloadLink = document.createElement("link");
preloadLink.href = "myscript.js";
preloadLink.rel = "preload";
preloadLink.as = "script";
document.head.appendChild(preloadLink);
这样的话,浏览器仅仅只是加载了该脚本,并没有执行。
当需要执行时,只要执行:
// 执行预加载的资源
var preloadedScript = document.createElement("script");
preloadedScript.src = "myscript.js";
document.body.appendChild(preloadedScript);
preload会阻碍load事件么
实际测试当preload的文件在文档中有直接使用时,是会有影响的。
<head>
<link rel="preload" href="./js/2.js" as="script" />
</head>
<script src="./js/2.js"></script>
这样是会有影响的,当底部没有script使用时,则不会有影响。
prefetch
prefetch是用来告诉浏览器下载稍后用户会用到的资源,所以他的优先级很低,而且下载过程也是在浏览器空闲时。当然,这也意味着prefetch的资源可能是没有用的,因为用户并不会按照你的期望去进入某些页面。
<link rel="prefetch" href="page-data.json" />
prefetch提前加载的资源不是当前页面的,利用http缓存,当用户进入到既定页面时,请求资源通过http强缓存被命中后,达到加快显示的目的。
混用
1. prefetch的资源同时在当前页面被使用
<head>
<link rel="prefetch" href="./js/1.js">
</head>
<body></body>
<script src="./js/1.js"></script>
如此操作只会多次请求,得不偿失(脚本只会执行一次):
2. 与preload混用
<link rel="prefetch" href="./js/1.js">
<link rel="preload" href="./js/1.js" as="script">
同样的,该js文件会被获取两次,浪费了带宽。
兼容性
prefetch的兼容性相对于preload要好些。
总结
preload针对当前页面即将要使用的资源进行提前加载,prefetch对潜在页面的资源进行低优先级的加载。即,preload针对当前页面,prefetch是针对其他页面,两者完全不同,不可替代。
当资源被preload或者prefetch后,会根据其HTTP状态确定是否被缓存(cache-control 和 max-age),若可以,它将存储在HTTP缓存中,可用于当前和未来的会话。 如果资源不可缓存,则不会将其存储在HTTP缓存中。 相反,它会被缓存到内存缓存中并保持不变直到它被使用。
简单demo见:Github。