网页设计师和前端开发者看的前端性能优化
更少的请求
另一个明显而基本的性能优化方法是少下载。页面需要的每一个资源就是一次额外的HTTP请求;浏览器不得不停下来去获取每一个用于渲染页面所需的资源。每一次HTTP请求都可能引发DNS查询,重定向,404,等等。每一次HTTP请求,无论为了样式表,图片,web字体,JS文件还是其它你能想到的,都可能是一次非常昂贵的操作。尽量减少这些请求是你可以做的最快的优化方法中的一种.
再谈到浏览器和并行;大多数浏览器一次只从每个引用的域下载一些资源,而JS会阻塞这些下载。所以,你做的每一个HTTP请求都应该仔细考虑,而不是随便随便做的。
尽可能并行
为了让浏览器能并行的下载更多资源,你可以由不同的域名提供服务。如果说,浏览器只能一次从一个域名获取两个资源,那么由两个域名提供服务意味着它可以一次性获取四个资源;三个域名意味着六个并行下载。
许多网站有静态/资源 域名;你可以发现, Twitter, 用 si0.twimg.com 来做静态资源:
1 | < link rel = "stylesheet" href = "https://si0.twimg.com/a/1358386289/t1/css/t1_core.bundle.css" type = "text/css" media = "screen" > |
Facebook 用fbstatic-a.akamaihd.net:
1 | < link rel = "stylesheet" href = "https://fbstatic-a.akamaihd.net/rsrc.php/v2/yi/r/76f893pcD3j.css" > |
通过这些静态的资源域名, Twitter与Facebook能提供更多的并行资源服务;来自twitter.com和si0.twimg.com的资源可以协作方式下载。这真的是使你的页面上获得更多并发下载的简单方法,如果再加上实际的CDN技术就会更好,CDN技术通过从一个更加合适的物理位置提供资源服务的方法来减少延迟。
这全部都很好,但后面我们将讨论在特定环境下,怎样从子域名提供服务却会实际上对性能有害。
因此,现在有了我们关于性能的基础知识:
- 将样式表放在文档的顶部
- 将JavaScript放在底部(可能的地方)
- 尽可能减少HTTP请求
- 从多个域名提供资源服务能增加浏览器并行下载的资源数量。
HTTP 请求与 DNS 查询
每当你从任何域名请求一个资源,会发出一个带有相关头部,被访问资源的 HTTP请求,并且会返回一个响应。这是对该过程的一个极端简化,但它基本就是事实上你需要知道的。这是一个HTTP请求,而且所有涉及的资源都从属于这个往返的旅行。当提到前端性能,这些请求正是主要的瓶颈所在,因为如我们谈到的,浏览器受限于有多少请求可以并行发生。这也是为什么我们经常要使用子域名;以便允许这些请求在数个域名上发生,允许同时发生多得多数量的请求。
然而关于这还有个问题,DNS查询。每次(从一个空缓存)一个新的域名被引用,HTTP请求会受制于一个耗时的DNS查询(某个介于20到120毫秒之间的值),在DNS查询中,发出的请求会查询资源实际存在的地点;互联网通过IP地址被绑定在一起,这些地址由DNS管理的主机名引用。
如果每个引用的新域名具有DNS查询的前端代价,你必须确保这个代价确实是值得的。如果是一个小网站(例如像CSS魔法),那么由子域名提供资源可能并不值得;相比执行多个域名的DNS查询并将其并行化来说,从一个域名非并行的获取若干资源,浏览器可能更快。
如果你或许有一打资源,你可能会考虑从一个子域名提供它们的资源服务;为了更好的并行化那许多资源,额外的DNS查询可能是值得的。如果说你有40个资源,可能将那些资源切分到两个子域名是值得的;为了由总数为三个的域名提供你的网站服务,两个额外的DNS查询会是值得的。
DNS查询代价很高,因此你需要决定什么才是对你的网站更合适的;承担查询的消耗或者只是由一个域名提供所有服务。
很重要的需要记得的是,比方说一旦HTML被请求于foo.com,对那个主机的DNS查询就立即发生了,所以后续的任何对foo.com的请求不再受制于DNS查询。
DNS 预取
如果你像我一样想在网站上有一个Twitter小程序,还有网站分析,再也许一些网页字体,那么你必须要链接到一些其它域名,这意味着你将不得不引发DNS查询。我的建议通常是,不要还没有先适当的考虑性能影响就使用某个或任何一个小程序,但对于你认为确实需要的,下面的将很有用……
因为这些东西都存在于其它域名,比方说这就意味着你的网站字体CSS将会同你自己的CSS并行下载,从某种意义上说是一种好处,但是脚本将仍会阻塞(除非它们是异步的)
事实上,这里的问题是DNS查询牵涉到了第三方域名。幸运的是,有一个相当快又简单的办法来加速这个过程:DNS预取。
DNS预取所做的恰恰就是凭证领餐(on the tin),它不能被简单实现。比方说,如果你需要请求来自widget.foo.com的资源,那么你可以通过简单的在页面的<head>里先增加下面这个来预取那个主机的DNS:
1 2 3 4 5 | < head > ... < link rel = "dns-prefetch" href = "//widget.foo.com" > ... </ head > |
那行简单的内容将会告诉支持的浏览器去开始预取那个域名的DNS,这要稍稍早于它实际需要的时刻。它意味着DNS查询过程,在浏览器<script>元素真正请求小程序的时候就已经在进行中了。这仅仅给浏览器增加了一个很小的开头。
这种简单的链接元素(就是我在CSS魔法上用到的)完全后向兼容,而且不会忽略性能影响。将它看作是性能提升增强吧!