前言
最近在整理学习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-prefetch
和preconnect
能让我们在请求资源时加快TCP的链接速度;preload
和prefetch
提前加载资源;prerender
提前渲染页面。