简析link对加载速度的优化--从请求URL到页面加载

582 阅读5分钟

前言

最近在整理学习html元素的相关知识点,发现了link元素的一些平时不太了解的地方,很多和项目加载优化有关。

这里按照请求URL到页面加载的顺序来简单对这些知识点做一些梳理,便于记忆,如有问题,欢迎指出。

请求URL

当浏览器请求一个URL时,通过开发者工具我们可以看见大概有域名解析、等待TCP队列、建立连接、发送数据、等待响应、接收数据等过程。

域名解析

当我们需要访问某个URL时,需要浏览器将请求DNS返回域名对应的IP。这个过程一般都很快的,但也会引起延迟。那么,我们这里可以告诉浏览器提前对某些域名进行解析,减少等待时间。

另外,默认情况下,通过 HTTPS 加载的页面上内嵌链接的域名并不会执行预加载。在 Firefox 浏览器中,可以通过 about:config 设置 network.dns.disablePrefetchFromHTTPS 值为 false 来改变这一默认行为。

  • DNS预解析

DNS预解析其范围包括文档的所有链接,无论是图片的,CSS 的,还是 JavaScript 等其他用户能够点击的URL。我们可以通过在服务器端发送 X-DNS-Prefetch-Control 报头,或是在文档中使用值为http-equiv<meta>标签来控制:

<!-- 开启DNS预解析 -->
<meta http-equiv="x-dns-prefetch-control" content="on">

<!-- 关闭DNS预解析 -->
<meta http-equiv="x-dns-prefetch-control" content="off"> 

其中 Chrome 和 Firefox 3.5+ 内置了 DNS Prefetching 技术并对DNS预解析做了相应优化设置。所以,即使不设置此属性,Chrome 和 Firefox 3.5+ 也能自动在后台进行预解析 。

  • 强制查询特定主机名

我们也可以通过使用rel属性值为dns-prefetch<link>标签来对特定域名进行预读取:

<link rel="dns-prefetch" href="//domain.com">

等待TCP队列

在不同的浏览器中,同一个域名能同时连接的TCP连接是有数量限制的。以Chrome为例,同一个域名能同时最多连接6个。那么,当某个域名下有10个请求时,仅会先执行6个,剩下的4个请求会进行等待。

为了解决这一个问题,我们对某些URL的域名进行分散处理,比如讲图片的URL域名设置为img.cdn.cn,js和css同样这样拆分。当然,我们也需要对这些域名进行DNS预解析。

这里还需要注意到的一点就是,使用不同域名,可以减少cookie污染,减少带宽。

建立连接

排队等待完毕后,会开始建立TCP连接。经过3次握手后,就可以和服务端进行愉快的通信。但是,这些步骤也是存在耗时的,我们可以使用rel属性值为preconnect<link>标签来预先建立一个连接。

启动早期连接,包括DNS查找,TCP握手和可选的TLS协商,使用户代理能够掩盖建立连接的高延迟成本。

<link rel="preconnect" href="//example.com">
<link rel="preconnect" href="//cdn.example.com" crossorigin>

页面加载

浏览器获取到相关的资源后,会按照一定的顺序和逻辑进行页面的页面渲染。这都是需要耗费时间的,我们可以对用户可能的操作做出判断,从而预先执行这些操作,减少用户打开页面时间。

预读取

在对资源进行下载获取时,涉及到资源大小、网络带宽、服务器性能等问题,一般耗时很长。对此,我们可以对这些资源预读取,当需要这些资源时可以直接使用。

preload

我们使用rel属性值为preload<link>标签来预先加载当前页面需要的资源,但是并不马上执行,需要执行时再执行

这里有一点需要注意,当我们需要执行预加载资源,若该资源未下载完毕,则会等待资源下载完毕后再执行,不会再次请求资源。

<link rel="preload" href="style.css" as="style">

在该<link>标签中还可以设置一些其他属性:

  • as属性规定了<link>标签加载的内容的类型,可以用于内容的优先级、请求匹配、正确的内容安全策略的选择以及正确的Accept请求头的设置。
  • type属性可以包含所指向资源的MIME类型,浏览器用此来判断是否支持这个资源。
  • crossorigin属性用于预加载那些跨域资源。需要注意的是,若获取字体文件,那么即使是非跨域的情况下,也需要应用这一属性
  • media属性能让我们响应式的预加载资源。

prefetch

<link>标签中使用prefetch,也可以进行资源的预加载(可以点击这里了解更多内容)。

<link rel="prefetch" href="//example.com/next-page.html" as="document" crossorigin="use-credentials">
<link rel="prefetch" href="/library.js" as="script">

但是,相对preload浏览器加载prefetch资源的优先级较低,主要为下一个导航/页面使用。并且,当我们需要执行prefetch预加载资源,若该资源未下载完毕,则会再次请求资源

subresource

在Chrome中,我们也可以在<link>标签中使用subresource来预加载资源。但是,它没有办法处理所获取内容的优先级,所以浏览器加载其资源的优先级也很低。

预渲染

针对下一个可能打开的页面,我们可以使用rel属性值为prerender<link>标签来预先加载。

<link rel="prerender" href="//example.com/next-page.html">

浏览器会针对情况对预渲染调整(具体可以查看这里):

  • 将更少的CPU,GPU或内存资源分配给预渲染的内容。
  • 当可用资源有限时,防止启动预渲染。
  • 由于高成本或资源要求(例如,高CPU或内存使用率,昂贵的数据访问等等)而放弃预渲染。
  • 。。。

总结

为了更好的性能和更好的用户体验,我们可以使用<link>标签实现一些提前操作。

dns-prefetchpreconnect能让我们在请求资源时加快TCP的链接速度;preloadprefetch提前加载资源;prerender提前渲染页面。

参考资源