预加载器(也称为推测或前瞻预解析器)可能是浏览器性能有史以来最大的改进。
那么什么是预加载器以及它如何提高性能呢?
浏览器如何加载网页
网页充满了依赖关系——在下载相关的CSS之前页面无法开始渲染,然后当遇到脚本时HTML解析器会暂停直到脚本执行(当然,如果脚本是外部的,它也需要下载)。
让我们考虑一下浏览器如何加载页面:
-
首先下载 HTML,浏览器开始解析它。它找到对外部 CSS 资源的引用并发出下载它的请求。
-
浏览器可以在下载 CSS 时继续解析 HTML,但随后它会找到带有外部 URL 的脚本标记,现在(除非脚本具有async或defer属性)它必须等到脚本下载并执行。
-
一旦脚本下载并执行,浏览器就可以继续解析 HTML,当它发现图像等非阻塞资源时,它会请求它们并继续解析,但是当它发现脚本时,它必须停止并等待脚本执行。被检索并执行。
尽管浏览器能够并行发出多个请求,但具有这种行为的浏览器通常不会与脚本并行下载任何资源。
测试页有两个样式表,头部有两个脚本,然后在正文中有两个图像、一个脚本,最后是另一个图像。
通过瀑布可以很容易地看到在下载脚本时并行下载停止。
如果浏览器仍然这样工作,那么页面加载速度会更慢,因为每次遇到脚本时,浏览器都需要等待脚本下载并执行,然后才能发现更多资源。
预加载器如何提高网络利用率
Internet Explorer、WebKit 和 Mozilla 都在 2008 年实施了预加载器,作为克服等待脚本下载和执行时网络利用率低的问题的一种方法。
当浏览器在脚本上被阻止时,第二个轻量级解析器会扫描标记的其余部分,查找也需要检索的其他资源,例如样式表、脚本、图像等。
然后,预加载器开始在后台检索这些资源,目的是当主 HTML 解析器到达这些资源时,它们可能已经被下载,从而减少页面稍后的阻塞。
(当然如果资源已经在缓存中那么浏览器就不需要下载它)
使用 IE8 重复之前的测试表明,其他资源现在与脚本并行下载,为此测试用例带来了巨大的性能改进:7 秒 vs 14 秒。
预加载器的行为因浏览器而异,并且仍然是一个实验领域,一些浏览器似乎有幼稚的实现,它们按照发现的顺序下载资源,但其他浏览器会优先考虑下载,例如 Safari 提供的样式表不适用于当前视口的优先级较低,Chrome 会以比页面上大多数图像更高的优先级安排脚本(即使是页面底部的脚本)。
预加载器陷阱
预加载器从标记中提取 URL,并且不会/无法执行 javascript,因此使用 javascript 插入的任何 URL 对它来说都是不可见的,并且这些资源的下载将被延迟,直到 HTML 解析器发现并执行加载它们的 javascript。
在某些情况下,使用 JavaScript 插入资源也会导致某些预加载器出错。
示例:
<html>
<head>
<script>
var file = window.innerWidth < 1000 ? "mobile.css" : "desktop.css";
document.write('<link rel="stylesheet" type="text/css" href="css/' + file + '"/>');
</script>
</head>
<body>
<img src="img/gallery-img1.jpg" />
<img src="img/gallery-img2.jpg" />
<img src="img/gallery-img3.jpg" />
<img src="img/gallery-img4.jpg" />
<img src="img/gallery-img5.jpg" />
<img src="img/gallery-img6.jpg" />
</body>
</html>
我不使用这种方法有几个原因,但即使是这个简单的例子也足以使 IE9 的预加载器出错——注意图像如何获取所有连接,并且 CSS 被延迟,直到其中一个图像完成并且连接变为可用的。
一些响应式图像方法使用后备图像,预加载器通常会在执行 javascript 以选择适当的图像之前启动后备图像下载,从而导致额外的下载。
影响预加载器
目前,我们影响预加载器优先级的方法有限(使用 javascript 隐藏资源就是其中之一),但W3C 资源优先级规范提出了两个属性来帮助表明我们的意图。
lazyload:在未标记为延迟加载的其他资源开始下载之前,不应下载资源
postpone:资源必须在用户可见(即在视口内并且显示不是无)之前才能下载。
预加载与预取
预取是向浏览器暗示将来肯定会或可能使用的资源的一种方式,一些提示适用于当前页面,其他提示适用于未来可能的页面。
在最简单的层面上,我们可以告诉浏览器将 DNS 解析为我们稍后将在页面上访问的另一个主机名:
<link rel="dns-prefetch" href="other.hostname.com">
Chrome 还允许我们暗示我们稍后将在当前页面中使用另一个资源,因此应该优先下载它:
<link rel="subresource" href="/some_other_resource.js">
(Chromium 的源代码表明它实际上下载的优先级低于样式表/脚本和字体,但优先级等于或高于图像)
还有两种链接类型允许我们推测性地暗示接下来会发生什么,并且将以比当前页面上的资源更低的优先级下载。
预取可能位于下一页的单个资源:
<link rel="prefetch" href="/some_other_resource.jpeg">
在后台选项卡中预取并渲染整个页面:
<link rel="prerender" href="//domain.com/next_page.html">
总结
预加载器并不是什么新鲜事,它提供了显着的性能提升,作为开发者,我们不需要做任何特殊的事情来利用它。
原文链接:https://andydavies.me/blog/2013/10/22/how-the-browser-pre-loader-makes-pages-load-faster/