使用 async/defer 属性在加载脚本的时候不阻塞 HTML 的解析,defer 加载脚本执行会在所有元素解析完成,DOMContentLoaded 事件触发之前完成执行。它的用途其实跟 preload 十分相似。你可以使用 defer 加载脚本在 head 末尾,这比将脚本放在 body 底部效果来的更好。
对于 Server Push 来说,如果服务端渲染 HTML 时间过长的话则很有效,因为这时候浏览器除了干等着,做不了其它操作,但是不好的地方是服务器需要支持 HTTP/2 协议并且服务端压力也会相应增大。对于更多 Server Push 和 preload 的对比可以参考这篇文章:HTTP/2 PUSH(推送)与HTTP Preload(预加载)大比拼
浏览器预解析:
现代浏览器很聪明,就如 Chrome 浏览器,它会在解析 HTML 时收集外链,并在后台并行下载,它也实现了提前加载以及加载和执行分离。
原文地址
衡量网站的性能的指标有很多,其中有项重要的指标就是网站的首屏时间,为此前端工程师们都是绞尽脑汁想尽办法进行优化自己的应用,诸如像服务端渲染,懒加载,CDN 加速,ServiceWorker 等等方法,今天介绍的 preload/prefetch 是一种简单,但却事半功倍的优化手段。
基本用法
在网络请求中,我们在使用到某些资源比如:图片,JS,CSS 等等,在执行之前总需要等待资源的下载,如果我们能做到预先加载资源,那在资源执行的时候就不必等待网络的开销,这时候就轮到 preload 大显身手的时候了。
preload 提前加载
preload 顾名思义就是一种预加载的方式,它通过声明向浏览器声明一个需要提交加载的资源,当资源真正被使用的时候立即执行,就无需等待网络的消耗。
它可以通过 Link 标签进行创建:
当浏览器解析到这行代码就会去加载 href 中对应的资源但不执行,待到真正使用到的时候再执行,另一种方式方式就是在 HTTP 响应头中加上 preload 字段:
这种方式比通过 Link 方式加载资源方式更快,请求在返回还没到解析页面的时候就已经开始预加载资源了。
讲完 preload 的用法再来看下它的浏览器兼容性,根据 caniuse.com 上的介绍:IE 和 Firefox 都是不支持的,兼容性覆盖面达到 73%。
prefetch 预判加载
prefetch 跟 preload 不同,它的作用是告诉浏览器未来可能会使用到的某个资源,浏览器就会在闲时去加载对应的资源,若能预测到用户的行为,比如懒加载,点击到其它页面等则相当于提前预加载了需要的资源。它的用法跟 preload 是一样的:
讲完用法再讲浏览器兼容性,prefetch 比 preload 的兼容性更好,覆盖面可以达到将近 80%。
更多细节点
当一个资源被 preload 或者 prefetch 获取后,它将被放在内存缓存中等待被使用,如果资源位存在有效的缓存极致(如 cache-control 或 max-age),它将被存储在 HTTP 缓存中可以被不同页面所使用。
正确使用 preload/prefetch 不会造成二次下载,也就说:当页面上使用到这个资源时候 preload 资源还没下载完,这时候不会造成二次下载,会等待第一次下载并执行脚本。
对于 preload 来说,一旦页面关闭了,它就会立即停止 preload 获取资源,而对于 prefetch 资源,即使页面关闭,prefetch 发起的请求仍会进行不会中断。
什么情况会导致二次获取?
preload 是告诉浏览器页面必定需要的资源,浏览器一定会加载这些资源,而 prefetch 是告诉浏览器页面可能需要的资源,浏览器不一定会加载这些资源。所以建议:对于当前页面很有必要的资源使用 preload,对于可能在将来的页面中使用的资源使用 prefetch。
这将会浪费用户的带宽吗?
用 “preload” 和 “prefetch” 情况下,如果资源不能被缓存,那么都有可能浪费一部分带宽,在移动端请慎用。
没有用到的 preload 资源在 Chrome 的 console 里会在 onload 事件 3s 后发生警告。
原因是你可能为了改善性能使用 preload 来缓存一定的资源,但是如果没有用到,你就做了无用功。在手机上,这相当于浪费了用户的流量,所以明确你要 preload 对象。
如何检测 preload 支持情况?
用下面的代码段可以检测
<link rel=”preload”>
是否被支持:不同资源浏览器优先级
在 Chrome 46 以后的版本中,不同的资源在浏览器渲染的不同阶段进行加载的优先级如下图所示:
一个资源的加载的优先级被分为五个级别,分别是:
从图中可以看出:(以 Blink 为例)
而 script 脚本资源就比较特殊,优先级不一,脚本根据它们在文件中的位置是否异步、延迟或阻塞获得不同的优先级:
自己网站资源优先级也可以通过 Chrome 控制台 Network 一栏进行查看.
与其它加载方式对比
async/defer:
使用 async/defer 属性在加载脚本的时候不阻塞 HTML 的解析,defer 加载脚本执行会在所有元素解析完成,DOMContentLoaded 事件触发之前完成执行。它的用途其实跟 preload 十分相似。你可以使用 defer 加载脚本在 head 末尾,这比将脚本放在 body 底部效果来的更好。
HTTP/2 Server Push:
HTTP/2 PUSH 功能可以让服务器在没有相应的请求情况下预先将资源推送到客户端。这个跟 preload/prefetch 预加载资源的思路类似,将下载和资源实际执行分离的方法,当脚本真正想要请求文件的时候,发现脚本就存在缓存中,就不需要去请求网络了。
我们假设浏览器正在加载一个页面,页面中有个 CSS 文件,CSS 文件又引用一个字体库,对于这样的场景,
若使用 HTTP/2 PUSH,当服务端获取到 HTML 文件后,知道以后客户端会需要字体文件,它就立即主动地推送这个文件给客户端,如下图:
而对于 preload,服务端就不会主动地推送字体文件,在浏览器获取到页面之后发现 preload 字体才会去获取,如下图:
对于 Server Push 来说,如果服务端渲染 HTML 时间过长的话则很有效,因为这时候浏览器除了干等着,做不了其它操作,但是不好的地方是服务器需要支持 HTTP/2 协议并且服务端压力也会相应增大。对于更多 Server Push 和 preload 的对比可以参考这篇文章:HTTP/2 PUSH(推送)与HTTP Preload(预加载)大比拼
浏览器预解析:
现代浏览器很聪明,就如 Chrome 浏览器,它会在解析 HTML 时收集外链,并在后台并行下载,它也实现了提前加载以及加载和执行分离。
它相比于 preload 方式而言:
使用案例
总结
preload/prefetch 是个好东西,能让浏览器提前加载需要的资源,将资源的下载和执行分离开来,运用得当的话可以对首屏渲染带来不小的提升,可以对页面交互上带来极致的体验。
参考链接