TAT.Johnny Preload:有什么好处?(上)
In 未分类 on 2016年05月18日 by view: 1,894
0

原文:https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/

作者:Yoav Weiss

译者按:网络优化一直是译者长期研究的方向,除了近期热门的HTTP/2之外,还是要关注浏览器在加载策略上的一些改进,从不同层面提升用户的访问体验。prefetch这些HTML5的新特性,虽然很新鲜,但并未在生产环境中得到广泛使用,其中的原因是什么?preload有什么改进?本文将娓娓道来~

========================译文分割线===========================

Preload规范)是一项新的Web标准,旨在提升性能,让Web开发者对加载的控制更加粒度化。它让开发者有自定义加载逻辑的能力,免受基于脚本的loader所带来的性能损耗。

几周前,我在Chrome Canary提交了对preload的支持,解决了一些bug,预计将在四月中旬合入Chrome稳定版。但preload到底是什么?它有什么用处?对你有什么好处呢?

好吧,<link rel="preload">它是一种声明式的获取(fetch)指令。

用人话来讲,它是一种告诉浏览器开始获取一项确定资源的方式,因为我们是作者(或者服务器管理员,或者聪明的服务器开发),我们是知道浏览器将很快需要那一项资源的。

不是已经有这种能力了吗?

嗯,也不算是真有。<link rel="prefetch">已经被支持一段时间了,也有不错的兼容性。在此基础上,Chrome也对<link rel="subresource">支持好一段时间了,所以preload有什么新特性?它跟前面这几个指令有什么区别?它们都告知浏览器去获取资源,是吧?

确实是的,但是它们有一些重要的区别,这些区别使得preload成为一项全新的指令,做出之前指令所不能做到的事情。

<link rel="prefetch">是一种告诉浏览器获取一项可能被下一页访问所需要的资源方式。这意味着资源将以较低优先级地获取(因为浏览器知道当前页面所需要的资源,要比我们猜测在下一页访问所需资源更重要)。这意味着prefetch的主要用途是加速下一页访问速度,而不是当前页面的速度。

<link rel="subresource">原本是计划处理当前页面的,但是在一些特别的场景失败了。由于Web开发者无法定义资源的优先级,所以浏览器(实际只有Chrome和基于Chromium的浏览器)使用同等较低的优先级加载资源,也就是说大多数情况,即使没有subresource,请求也是同一时机发出的。

Preload如何做得更好?

Preload像subresource一样,注定是为当前页面服务的,但具备一项小而重要的区别。它有一个 as 属性,这可以让浏览器做到很多subresource和prefetch做不到的事情:

  • 浏览器可以设置正确的资源优先级,使得资源可以被正确地加载,重要的资源不再会被延迟,不再被不重要的资源阻塞。
  • 浏览器会保证请求对应正确的内容安全策略(Content-Security-Policy )指令,不会发起非法请求。
  • 浏览器会基于资源类型发送正确的 Accept 首部。(比如获取图片时指定对“image/webp”的支持)
  • 浏览器知道资源的类型,所以可以稍后决定资源是否在后续请求中保持可重用。

Preload的另外一个不同是,它有onload事件(至少在Chrome中,对另外两种 rel 取值并没作用)。

Preload不阻塞window的onload事件,除非该资源是被一个阻塞该事件的资源请求的。

将这些特性结合在一起,我们可以做到一些新的事情。

加载较晚发现的资源

preload最基本的使用方式是提前加载较晚发现的资源。虽然大部分基于标签的资源会被浏览器内部的预加载器(preloader)提早发现,但并非所有资源都是基于标签的。有些资源是隐藏在CSS和JavaScript中的,浏览器不知道页面即将需要这些资源,而等到发现它们时已经为时已晚。所以在有些情况,这些资源延缓了首屏渲染,或是延缓了页面关键部分的加载。

而现在你有途径告诉浏览器,“嘿,浏览器!这个是你一会所需要的资源,现在就开始加载吧。”

做法可能如下

as 属性告诉浏览器什么类型的资源将被下载。as 可能的取值有:

  • “script”
  • “style”
  • “image”
  • “media”
  • “document”

(参考fetch规范的完整列表)

如果省略 as 属性,或者给定一个非法值,将等同发起一个XHR请求,因为浏览器不知道将要获取的是什么,并且加载的优先级也会很低。

较早加载字体

一种流行的“较晚发现关键资源”的代表是Web字体。一方面,它对页面渲染字体很关键(除非你在使用最新的font-display)。另一方面,它们被埋在CSS很深的地方,即使浏览器的预加载器解析了CSS,也不能保证在它们选择器应用到DOM节点之前,就是被需要的。理论上讲,浏览器应该可以发觉这种情况,但实际上都没有,而就算可以,也会造成无谓的下载,因为后续的CSS规则可能会覆盖现有的字体样式规则。

简而言之,这很复杂。

但是,你可以对一定需要的字体使用preload指令,摆脱上述的复杂问题。像这样:

有一点需要指明,获取字体时必须加上crossorigin属性,就如使用CORS的匿名模式获取一样。是的,即使你的字体与页面同域,抱歉……

同时,type属性确保了浏览器只预加载其支持的类型文件。现在,只有Chrome支持preload,同时支持WOFF2。未来会有越来越多浏览器支持preload,但我们不敢确保它们都会支持WOFF2。同理,不同浏览器对不同类型的资源支持也可能是不一样的。

动态加载,不做运行

另一个突然变得现实的有趣场景是,你想下载一个资源因为你觉得它是被需要的,但不想立即运行它。举个例子,想象这个场景,你想在页面生命周期的一个特定时间点执行代码,而不用通过脚本来控制(不用添加一个runNow()的方法)。

如今,要做到上面的事情非常受限。如果只是在需要运行的地方注入脚本,浏览器会在执行前下载这段脚本,耗费一点时间。也可以先使用XHR下载脚本,但浏览器会拒绝使用它,因为资源不是以同样type下载的。

所以该怎么办呢?

在没有preload之前,也没什么办法了。(在一些场景可以使用eval()脚本的内容,但那不太切合实际并且伴有副作用。)但可以配合preload来实现!

可以在页面加载的早期运行它,也就是在希望脚本运行的早些时间(但你要确信这个脚本不会对其他需要加载的关键资源有所干扰)。然后但你想运行它们的时候,只要简单注入一段脚本就好了。

基于标签的异步加载器

另一个酷炫的技巧,是使用onload处理函数来创建一些基于标签的异步加载器。Scott Jehl做了这方面的第一个实验,作为他的loadCSS库。简单说,你可以这么使用:

在标签里获取异步的样式表!Scott还有一个该特性的demo页面。

该特性同样可以应用在脚本上。

话说我们不是已经有<script async>了吗?嗯,<script async>确实不错,但它会阻塞window的onload事件。在一些情况,这可能是你希望的,而有些情况并不是。

比如说你想下载一个上报分析脚本,希望尽快去加载它(以避免分析脚本漏掉一些访客上报),但不想它对用户体验有任何影响,也就是不希望它对onload造成延迟。(你可以说onload不是影响用户的唯一因素,但缩短转菊花的时间总是一件好事)。

有了preload,实现起来就很容易了:

(onload属性中包含太长JS可能不是个好主意,可以在inline的代码中来定义。)

 

========================译文分割线===========================

由于本文篇幅较长,便于大家吸收消化,更多网络细节以及HTTP/2相关内容,将于下一篇博客与各位见面~

原创文章转载请注明:

转载自AlloyTeam:http://www.alloyteam.com/2016/05/preload-what-is-it-good-for-part1/

发表评论