- 原文地址:Preload, Prefetch And Priorities in Chrome
- 原文做者:Addy Osmani
- 译文出自:掘金翻译计划
- 译者:gy134340
- 校对者:IridescentMia,vuuihc
今天咱们来深刻研究一下 Chrome 的网络协议栈,来更清晰的描述早期网络加载(像 <link rel=“preload"
和 <link rel=“prefetch”>
)背后的工做原理,让你对其更加了解。javascript
像其余文章描述的那样,preload 是声明式的 fetch,能够强制浏览器请求资源,同时不阻塞文档 onload 事件。css
Prefetch 提示浏览器这个资源未来可能须要,可是把决定是否和什么时间加载这个资源的决定权交给浏览器。html
Preload 将 load 事件与脚本解析过程解耦,若是你尚未用过它,看看 Yoav Weiss 的文章 Preload: What is it Good For?。前端
在咱们深刻细节以前,下面是上一年发现的使用 proload 并对加载产生积极影响的案例总结:java
Housing.com 在对他们的渐进式 Web 应用程序的脚本转用 proload 看到大约缩短了10%的可交互时间。react
Shopify 在转用 preload 加载字体后在 Chrome 桌面版得到了 50%(1.2s) 的文字渲染优化,这彻底解决了他们的文字闪动问题。android
左边:使用 preload,右边:不使用 preload(视频)webpack
使用<link rel=”preload”>
加载字体。ios
Treebo,印度最大的旅馆网站之一,在 3G 网络下对其桌面版试验,在对其顶部图片和主要的 Webpack 打包文件使用 preload 以后,在首屏绘制和可交互延迟分别减小了 1s。git
一样的,在对本身的渐进式 Web 应用程序主要打包文件使用 preload 以后,Flipkart 在路由解析以前 节省了大量的主线程空闲时间(在 3G 网络下的低性能手机下)。
上面:未使用 preload,下面:使用 preload
Chrome 数据保护团队在对脚本和 CSS 样式表使用 preload 以后,发现页面首次绘制时间得到平均 12% 的速度提高。
对于 prefetch ,它被普遍使用,在 Google 咱们仍用它来获取能够加快 搜索结果页面 的渲染的关键资源。
Preload 在不少大型网站都有实际应用,这点你在接下来的文章里也能够看到,让咱们来仔细探讨下网络协议栈其实是如何对待 preload 和 prefetch 的。
<link rel=”preload”>
? 何时又该用 <link rel=”prefetch”>
?建议:对于当前页面颇有必要的资源使用 preload,对于可能在未来的页面中使用的资源使用 prefetch。
preload 是对浏览器指示预先请求当前页须要的资源(关键的脚本,字体,主要图片)。
prefetch 应用场景稍微又些不一样 —— 用户未来可能在其余部分(好比视图或页面)使用到的资源。若是 A 页面发起一个 B 页面的 prefetch 请求,这个资源获取过程和导航请求多是同步进行的,而若是咱们用 preload 的话,页面 A 离开时它会当即中止。
使用 preload 和 prefetch,咱们有了对当前页面和未来页面加载关键资源的解决办法。
<link rel="preload">
和 <link rel="prefetch">
的缓存行为Chrome 有四种缓存: HTTP 缓存,内存缓存,Service Worker 缓存和 Push 缓存。preload 和 prefetch 都被存储在 HTTP 缓存中。
当一个资源被 preload 或者 prefetch 获取后,它能够从 HTTP 缓存移动至渲染器的内存缓存中。若是资源能够被缓存(好比说存在有效的cache-control 和 max-age),它被存储在 HTTP 缓存中能够被如今或未来的任务使用,若是资源不能被缓存在 HTTP 缓存中,做为代替,它被放在内存缓存中直到被使用。
下面是在 Blink 内核的 Chrome 46 及更高版本中不一样资源的加载优先级状况( Pat Meenan)
preload 用 “as” 或者用 “type” 属性来表示他们请求资源的优先级(好比说 preload 使用 as="style" 属性将得到最高的优先级)。没有 “as” 属性的将被看做异步请求,“Early”意味着在全部未被预加载的图片请求以前被请求(“late”意味着以后),感谢 Paul Irish 更新这张关于开发者工具以及网络层上各类请求优先级的表。
咱们来谈一下这张表。
脚本根据它们在文件中的位置是否异步、延迟或阻塞得到不一样的优先级:
图片(视口可见)将会得到相对于视口不可见图片(低级)的更高的优先级(中级),因此某些程度上 Chrome 将会尽可能懒加载这些图片。低优先级的图片在布局完成被视口发现时,将会得到优先级提高(可是注意已经在布局完成后的图片将不会更改优先级)。
preload 使用 “as” 属性加载的资源将会得到与资源 “type” 属性所拥有的相同的优先级。好比说,preload as="style" 将会得到比 as=“script” 更高的优先级。这些资源一样会受内容安全策略的影响(好比说,脚本会受到其 “src” 属性的影响)。
不带 “as” 属性的 preload 的优先级将会等同于异步请求。
若是你想了解各类资源加载时的优先级属性,从开发者工具的 Timeline/Performance 区域的 Network 区域都能看到相关信息:
在 Network 面板下的“Priority”部分
这就要说看状况了,但一般来讲,会是比较好的状况 —— 若是资源没有超出 HTTP 缓存时间或者 Service Worker 没有主动从新发起请求,那么浏览器就不会再去请求这个资源了。
若是资源在 HTTP 缓存( Service Worker 缓存和网络中),那么 preload 将会得到一次缓存命中。
**用“preload”和“prefetch”状况下,若是资源不能被缓存,那么都有可能浪费一部分带宽。
没有用到的 preload 资源在 Chrome 的 console 里会在 onload 事件 3s 后发生警告。
缘由是你可能为了改善性能使用 preload 来缓存必定的资源,可是若是没有用到,你就作了无用功。在手机上,这至关于浪费了用户的流量,因此明确你要 preload 对象。
preload 和 prefetch 是很简单的工具,你很容易不当心二次获取。
不要用 “prefetch” 做为 “preload” 的后备,它们适用于不一样的场景,经常会致使不符合预期的二次获取。使用 preload 来获取当前须要任务不然使用 prefetch 来获取未来的任务,不要一块儿用。
不要期望 preload 和 fetch() 配合使用,在 Chrome 中这样使用将会致使二次的下载。这并不仅发生在异步请求的状况咱们有一个关于这个问题公开的 bug。
对 preload 使用 “as” 属性,否则将不会从中获益。
若是你对你所 preload 的资源使用明确的 “as” 属性,好比说,脚本,你将会致使二次获取。
preload 字体不带 crossorigin 也将会二次获取! 确保你对 preload 的字体添加 crossorigin 属性,不然他会被下载两次,这个请求使用匿名的跨域模式。这个建议也适用于字体文件在相同域名下,也适用于其余域名的获取(好比说默认的异步获取)。
存在 intergrity 属性的资源不能使用 preload 属性(目前)也会致使二次获取。连接元素的 [integrity](https://bugs.chromium.org/p/chromium/issues/detail?id=677022)
属性目前尚未被支持,目前有一个关于它的开放issue,这意味着存在 integrity 的元素将会丢弃 preload 的资源。往宽了讲,这会致使重复的请求,你须要在安全和性能之间做出权衡。
最后,虽然它不会致使二次获取,仍是有下面的建议:
不要 preload 全部东西! 做为替代的,用 preload 来告诉浏览器一些原本不能被提前发现的资源,以便提前获取它们。
这是工具而不是规则的好例子。你 preload 的文件数量取决于加载其余资源时网络内容、用户的带宽和其余网络情况。
尽早 preload 页面中可能须要的文件,对于脚本文件,preload 关键打包文件颇有用由于它将加载与执行分离开来,script async
很差由于它会阻塞 window 的 onload 事件。你能够尽早加载图片、样式、字体和媒体资源。大部分的 —— 最重要的是,你做为做者是能够清晰的知道哪些东西是页面目前须要的。
在 Chrome 中,若是用户从一个页面跳转到另外一个页面,prefetch 发起的请求仍会进行不会中断。
另外,prefetch 的资源在网络堆栈中至少缓存 5 分钟,不管它是否是能够缓存的。
preload 将资源获取与执行解耦,像这样,preload 在标记中声明以被 Chrome preload 扫描器扫描。这意味着,在许多案例中,在 HTML 解析器获取到标签以前,preload 就会被获取(用它声明的优先级)。这将会比自定义的 preload 更增强大。
当你知道资源加载的正确顺序时使用推送,用 service worker 来拦截那些可能须要会致使二次获取的资源请求,用 preload 来加快第一个请求的开始时间 —— 这对全部的资源获取都有用。
再次说一下,这都要看状况,咱们试想一下位 Google Play 商店作购物车,对于一个向购物车的请求:
用 preload 来加载页面的主要的模块须要浏览器等待 play.google.com/cart 有效载荷以便 preload 扫描器发现依赖,但这以后会浸透网络管道能够更好的像资源发起请求,这可能不是最理想的冷启动,但对于高速缓存和带宽的后续请求很是友好。
使用 HTTP/2 的服务器推送,当请求 play.google.com/cart 咱们能够快速浸透网络管道,但若是资源已经在 HTTP 或者 Service Worker 缓存中的话咱们就浪费了带宽,两种方法都须要作出权衡。
虽然推送颇有效,但它不像 preload 那样对全部的状况都适应。
preload 利于下载与执行的解耦,多亏其对文档 onload 事件的支持咱们如今能够控制其加载完毕后的事件,获取 JS 包文件在空闲快执行或者获取 CSS 模块在正确的时间点执行,能够说是很是强大的。
推送不能用于第三方资源的内容,经过当即发送资源,它还有效地缩短浏览器自身的资源优先级状况。在你明确的知道在作什么时,这应该会提升你的应用性能,若是不是很清晰的话,你也许会损失掉部分的性能。
跟其余连接不一样,preload 连接便可以放在 HTML 标签里也能够放在 HTTP 头部(preload HTTP 头),每种状况下,都会直接使浏览器加载资源并缓存在内存里,代表页面有很高的可能性用这些资源而且不想等待 preload 扫描器或者解析器去发现它。
当金融时报在它们的网站使用 preload HTTP 头时,他们节约了大约 1s 的显示片头图片时间。
下面的:使用 preload,上面:使用 preload。在 3G 网络下的 Moto G4 测试。
原来:www.webpagetest.org/result/1703…,以后: www.webpagetest.org/result/1703…。你可使用两种形式的 preload,但应当知道很重要的一点:根据规范,许多服务器当它们遇到 preload HTTP 头会发起 HTTP/2 推送,HTTP/2 推送的性能影响不一样于普通的预加载,因此你要确保没有发起没必要要的推送。
你可使用 preload 标签来代替 preload 头以免没必要要的推送,或者在你的 HTTP 头上加一个 “nopush” 属性。
用下面的代码段能够检测<link rel=”preload”>
是否被支持:
const preloadSupported = () => {
const link = document.createElement('link');
const relList = link.relList;
if (!relList || !relList.supports)
return false;
return relList.supports('preload');
};复制代码
FilamentGroup 也有一个 preload 检测器 ,做为他们的异步 CSS 加载库 loadCSS 的一部分。
固然,preload 支持基于异步加载的标记,使用 <link rel=”preload”>
的样式表使用 onload
事件当即应用到文档:
<link rel="preload" href="style.css" onload="this.rel=stylesheet">复制代码
更多相关的例子,看一下 Yoav Weiss 很棒的使用实例。
根据 HTTPArchive,不少网站应用 <link rel=”preload”>
来加载字体,包括 Teen Vogue 和以上提到的其余网站:
其余一些网站,好比 LifeHacker 和 JCPenny 用 FilamentGroup 的 loadCSS 来异步加载 CSS:
有愈来愈多的渐进式 Web 应用程序(好比 Twitter.com 移动端, Flipkart 和 Housing)使用它来加载当前连接须要的脚本:
基本的观点是要保持高粒度而不是单片,因此任何应用均可以按需加载依赖或者预加载资源并放在缓存中。
根据 CanIUse 在 Safari Tech Preview的调查看,<link rel="preload">
大约有 50% 的支持度,<link rel="prefetch">
大约有 70% 的支持度。<link rel="preload">
is available to ~50% of the global population according to CanIUse and is implemented in the Safari Tech Preview. <link rel="prefetch">
is available to 71% of global users.
感谢 @ShopifyEng、@AdityaPunjani、@HousingEngg、@adgad、@wheresrhys 和 @__lakshya 分享的统计信息。
很是感谢下列技术审核与建议人员: Ilya Grigorik, Gray Norton, Yoav Weiss, Pat Meenan, Kenji Baheux, Surma, Sam Saccone, Charles Harrison, Paul Irish, Matt Gaunt, Dru Knox, Scott Jehl.
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、React、前端、后端、产品、设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划。