前一阵,给我一个学生做培训,讲到一点点性能优化,然后我很后悔我误导了她.我给她讲减少http请求是多么重要,如何重要,为什么重要,还举了例子来说明这对于大型网站来说是多么重要,讲的头头是道,有理有据,最后把她说的服服帖帖,一切看起来理所当然.但是….

事情没有那么简单,就像做技术一样,其实去了解一个技术很简单,但是要懂得如何运用这个技术就不是每个人都可以做到的了.

1.理所当然我们是这样想的

关于减少http请求数,是前端开发性能优化的一个非常重要方面,所以在基本所有的优化原则里,都有这一条原则:减少http请求数.

先不考虑其他的,我们先考虑为什么减少http请求可以优化性能.

减少http请求有这样几个优点:

(1)   减少DNS请求所耗费的时间.

且不说对错,因为从基本来说,减少http请求数的确可以减少DNS请求和解析耗费的时间.

(2)   减少服务器压力.

这个通常是被考虑最多的,也是我用来讲解给别人听的最大理由,因为每个http请求都会耗费服务器资源,特别是一些需要计算合并等操作的服务器,耗费服务器的cpu资源可不是开玩笑的事情,硬盘可以用钱买来,cpu资源可就没那么廉价了.

(3)   减少http请求头.

当我们对服务器发起一个请求的时候,我们会携带着这个域名下的cookie和一些其他的信息在http头部里,然后服务器响应请求的时候也会带回一些cookie之类的头部信息.这些信息有的时候会很大,在这种请求和响应的时候会影响带宽性能.

2.让我们来一一道来

(1) 什么是DNS请求和解析呢?

简单来说,例如:www.taobao.com这样一个url,其中www部分被称为主机名(hostname),taobao这部分则是二级域,com则是一级域,如果是这样一个网址:www.ali.taobao.com.那么ali就是三级域.

当我们去请求一个url的时候,首先会到本地服务器里去寻找缓存中是否有解析结果,如果没有解析结果,就去根域名服务器请求,根域名服务器返回给本 地域名服务器一个所查询的域的主域名服务器的ip地址,然后我们再去请求刚才返回的ip地址的域名服务器,然后返回下一级域名的ip地址,直到我们找到域 名中所指的服务器ip,然后将结果缓存起来供下次使用,并返回此结果.

一个第一次请求的url的DNS解析过程可能耗费是很高的.但是解析一次之后,结果就会被缓存起来,之后再请求的时候就不用走上面这一套复杂的解析过程了.

关于一个正常的DNS请求到底会耗费多少时间,这个没有定论,要看网速状况和地域,但是考虑一个dns解析解析过后会被缓存起来,像淘宝这样的大网站,来的都是回头客,我们是否可以忽略DNS解析花费的时间呢?

在前端优化里还有一个优化方法,那就是增加hostname,例如,淘宝图片服务器,分为img01,img02,img03等主机名,这样做是因 为考虑到浏览器对同一个域名下同时进行的http连接数的限制,具体可以见下表: .然后我们在一个页面里的图片放在不同的hostname下,这样就可以同时下载多个图片了,浏览器http连接数的限制可以被缓解一下,但是这样做的后 果就是yslow评分会下降,因为yslow将DNS请求看的比较重要.

Browser   HTTP/1.1       HTTP/1.0
IE 6,7           2                     4
IE 8               6                    6
Firefox 2      2                     8
Firefox 3      6                     6
Safari 3,4     4                     4
Opera 9.61   8                     2

在此,就要引出我们想讨论的东西,为什么yslow评分在合理的情况下分数反而会低,因为yslow只是一个机器程序,它并不知道 我们的网站是什么类型的,是何种规模的,是内容型还是应用型,所以我们可以用异步加载的方式来欺骗yslow对于dom节点的评分,但是异步加载真的适合 我们的网站么?同样,别人写了一篇文章,出了一个原则,一个最佳实践,但是你确定他说的情况适合你的情况么?网站有大有小,大小不同,需要考虑的东西也就 不同.

为什么对于淘宝的图片来说,使用不同的hostname是个更优的方案呢?

首先,因为淘宝网的特殊性,淘宝网大多数访问者都是回头客,他们电脑里大多缓存着dns记录.这种情况,如果是小网站或者新兴网站可能要考虑,因为新用户比较多,可能dns请求的消耗更大一些,而且第一印象对于这些网站来说更为重要.

再者,淘宝里的图片很多,一个页面里通常会用到几十张甚至上百张图片,在这种情况下,我们更需要突破浏览器的http连接数的限制,以便加快加载速 度,这时候加载速度的考虑优先级远远高于DNS的影响,而yslow中对于DNS的着重考虑可能更偏向中小网站,图片比较少的网站.

对于DNS请求或者tcp(tcp握手之类的也会消耗请求时间)请求之类的分配和解析的消耗,还有一个办法是keeplive,让你的链接保持keeplive,这样可以只建立一次链接,然后传输多个文件,可以有效减少建立连接的时间.

(2) 减少服务器压力

过多的http请求对于服务器来说是很危险的,如果你的服务器不是很强,请把这一条考虑放在第一位,其他的优化策略都只是优化,而这里涉及到的是服务器,你要保证你的服务器能正常运转.

当然如果你是在淘宝的话,你就可以安心坐下来跟一群牛人谈论为什么要忽略http对服务器的影响,因为我们要记住:我们是前端开发工程师,我们是在 做前端优化,后台和我们无关,因为我们有足够强大的技术支持和硬件支持,当网站的技术发展到一定程度的时候,我们的关注点应该向用户那里偏重,因为用户看 到的才是我们最终要展示的,用户感受到的体验和速度才是我们要达到的速度,后台我们做的再快,前台呈现慢了,我们的服务器消耗少了,省钱了,但是用户却因 此抛弃了我们,一切都是白费.所以,当后台足够支撑你不用你去考虑后台压力的时候,那就安心考虑如何做好前台的工作吧.

Yslow真的是一个误导人的工具,只要我们按照它的原则对网站进行优化,肯定最后可以拿到高分来欺骗老板,但是对于有些场景,这些优化往往是一种 对性能的破坏,例如淘宝网的商城首页,为了提高Yslow评分,所有的图片都采用了一个hostname,分数提高了,但是并行加载少了,不过商城首页都 是异步渲染和异步加载的,所以这种影响看起来并不明显.

商城首页有很多针对yslow的优化点,当然大多数优化是正确的,例如:导航那里,本来是全部写在页面里的,不要小看那个导航,里面有N个链接节 点,以至于从浏览器里复制源代码的时候浏览器会卡死,因为字节数太多了,这里yslow肯定不会饶过的,后来我们把导航做成了异步加载的,评分理所当然上 去了.但是这是淘宝网,我们有足够的速度来提供足够的用户体验.如果你的服务器提供不了这种速度,也承受不了这种频繁的异步请求的话,这种优化就要慎重 了,延迟可能造成导航不可用.这也是针对场景来协调的.

淘宝现在在广泛部署CDN,CDN可以给我们提供足够的后台资源保障,在CDN和后台环境不断万善的情况下,考虑重点应该更加专注于前台传输速度和展现解析速度的提升.

(3)   合并脚本和样式?

其实在前一篇文章里的那段讨论也是对于不同应用场景的不同考虑,

减少http请求数的一个方法,对于前端来说,那就是合并脚本和样式文件,称为combo,通过将多个文件合并成一个文件,然后一次性传输到客户 端,这样可以减少http请求,的确是个有效的方法,甚至对于一些特殊的页面,例如首页,我们把样式和脚本都写在了页面里,根本没有分离出来,他们不会产 生http请求,当然,也不会被缓存,这是被牺牲的代价.

为什么我们要这么做,因为首页的访问量很大?这样可以有效减少http请求数?恩,这只是一部分原因,的确这样做有这样的好处,而且对于assets服务器不够强大的网站来说,在并发量大的首页上实行这一套是很有效的.

但是,淘宝访问量最大的页面并不是首页,而是detail页面,也就是商品详情页!

这才是我们讨论的重点,为什么首页采用combo甚至写在页面里,而detail则按照正常的样式和脚本来引用.首页是类似静态的页面,detail则是应用型的.首页没有脚本,依然可以起到导向的作用,但是detail页脚本没有运行起来的话,甚至无法购买商品.

其实在这里这样讨论并不能明显看出问题所在,因为淘宝在这些方面也不是很成熟,detail页引用了大量脚本和样式,很多内容是多余的过期的.

这从本质上来说代表两种网页类型,一种是内容型,一种则是应用型.对于内容型的网站,脚本并不是很重要(甚至样式),因为没有脚本,用户仍然可以浏 览页面,只是可能有些效果看不到而已,所以我们可以把脚本合并起来,一起放在body底部,在页面内容都加载完后,再一次性加载进来.而对于应用型的网 页,让应用跑起来才是最重要的,因为没有应用这个网页就变得没有意义了,这时候,按需加载脚本是一种趋势,我们需要先把应用的基本框架和功能按需加载进 来,让它们分别运行起来,而不是一起等脚本加载完再一起初始化,我们需要应用能够快速响应用户,

而且还是说到CDN,当CDN变得足够强的时候,连接数已经不是瓶颈,我们应该更多考虑怎么让网页更快的展现给用户,对于无需脚本也可以提供服务的 内容型的网页,将脚本放在页面底部,合并起来(减少连接数,我们仍然需要减少连接数,在不需要太快的使用脚本的情况下),而对于应用型的网页,我们需要尽 快让功能运转,甚至让他们一部分一部分按优先级初始化,这时候就要将脚本分开,按需加载.

(4)   减少http请求头

http头是个庞大的家伙,你打开taobao.com的首页,alert一下document.cookie,会发现淘宝网的cookie是如此 庞大,甚至比小型网页都大,每次你请求淘宝的服务器都会往返一次这些数据,还有一些其他的头部信息,占用的空间也不小,可想而知这种消耗有多大.

然后其实自从用了CDN,这一切都无需考虑太多,因为CDN和淘宝主站不在一个域名下,cookie不会互相污染,而CDN的域名下基本是没有 cookie和头部信息的,所以每次请求静态资源的时候,不会带着主站的cookie到处跑,而只是传输资源的主题内容,所以这对于性能的影响在使用 cdn之后会变得很小.但是如果你的静态资源服务器和主服务器在一个域名下,那就要控制好cookie和其他头部信息的大小了,因为每次传送都会传送他 们.

最后

总之,优化原则不是绝对的,对于不同的场景应该考虑不同的侧重点,别人的解决方案对于你来说不一定是最优的,应该针对自己的网站规模和类型进行适度的优化,不能盲目追求标准和最佳实践.