有一种优化方法叫做preload
在程序开发中,优化是必不可少的,今天学习的优化方法叫做preload。
奇技淫巧 --- Preload
- 在网页加载时,为了提高网页加载的性能,我们一般会选择延迟加载一部分资源和执行。
- 另一种情况是想要尽早的加载资源,但是等到合适的时机在执行。影响因素包括了依赖条件,执行条件,执行顺序等等。 通常我们会这样做:
- 通过插入一个页面元素来声明一个资源(如img、link、script),这种方式会将资源的加载和执行耦合
- 通过AJAX来加载资源。这种方式只有在时机成熟时才会加载资源,解决了执行时机问题。但是浏览器无法预解析,也就无法提前加载。另外如果页面有大量的阻塞脚本,就会造成延迟。
那么有没有办法既提前加载资源,又能解耦加载和执行呢?这时候就有人提出了Preload解决方案
什么是Preload
preload是一个预加载关键字它显式地向浏览器声明一个需要提前加载的资源。使用方式如下:
- 在head标签中插入这行代码,也可以用js生成再插入head标签中。
<link rel="preload" href="resource-url" as="xxx">
- 在HTTP请求头中加上
Link:<resource-url>;rel=preload;as=xx,当浏览器“看”到这样的声明后,就会以一定的优先级在后台加载资源,加载完的资源放在 HTTP缓存中。而等到要真正执行时,再按照正常方式用标签或者代码加载,即可从 HTTP 缓存取出资源。
Preload的特点
- 提前加载资源
- 资源的加载和执行分离
- 不会延迟网页的load事件(除非 Preload 资源刚好是阻塞 window 加载的资源)
Preload 跟其他提前加载资源以及加载和执行分离的方案有什么区别?
- 预测解析方案
在浏览器解析HTML资源时,可以同时收集外链资源,并且添加到一个列表,在后台并行下载。预测解析也实现了提前加载以及加载和执行分离。
和preload的区别:
- 仅限于解析 HTML 时收集的外链资源。如果是程序里异步加载的资源则是无法提前收集到的。
- 浏览器不暴露类似于 Preload 中的 onload 事件,也就无法更细粒度的控制资源的执行。
- async
async脚本是一加载完就立即执行,会阻塞window的onload事件,而且目前async仅限于脚本资源的加载。
而Preload可以实现和async一样的异步加载,并且不局限于脚本。比如以下代码会将CSS代码加载完就立即执行,作用到网页中。
<link rel="preload" href="./style.css" as="style" onload="this.rel='stylesheet'">
// 注:如果页面存在同步阻塞脚本,等脚本执行完后,样式才会作用到网页。
// 这样是因为 Preload 的资源不会阻塞 window 的 onload 事件。
- defer
defer实现了资源的加载和执行分离,并且能保证defer的资源按照HTML里的出现顺序执行。但是它和async一样,目前只能作用于脚本资源。
Preload 则适用多种资源类型。Preload 的资源也能像 defer 的资源一样延迟执行并保证执行顺序。
- server push
HTTP/2的 Server Push 也实现了资源的提前加载以及加载执行的分离。不过 Server Push 节省了一个网络来回,我们可以结合 Server Push 优化 Preload,比如服务器识别到文档里的Preload的资源就主动推送Preload的资源。如果不希望服务器推送,则增加 nopush 属性: Link: <./style.css>; rel=preload; as=style; nopush
另外 Server Push 只能推送同域资源。而 Preload 则可以支持跨域资源。
什么时候使用Preload
任何需要,想要先预加载再执行的资源,想要提高页面渲染性能的场景都可以使用Preload。
例如:
- 在单页面应用中,提前加载路由文件,提高切换路由时的渲染速度。现在大型的单页面应用通常都会使用异步加载路由文件。当用户切换路由时再异步加载相应的模块可能会存在性能问题。这时就可以用 Preload 提前加载,提升性能。
- 提前加载字体文件。众所周知,字体文件需要等到CSSOM构建完成,并且作用到页面元素之后,才会开始加载,这样就会导致页面的字体闪动(FOUT),所以这时就可以使用Preload提前加载字体,消除FOUT。
Preload兼容性问题
在 PC 上实现了 Preload 的浏览器包括: Chrome 50+,Saferi 11.1+ 。Edge 17+支持 HTML 方式,不支持 HTTP header 方式。移动端:iOS Safari 11.4+,Android Chrome 67
不支持 Preload的浏览器会自动忽略它,并采用普通的加载方式。因此可以将此功能作为一种渐进增强方式用到我们的网页应用中。