网站性能优化

这是关于《高性能网站建设指南》的读书笔记。

黄金法则

只有10%-20%的最终用户响应时间花在了下载HTML文档上,其余80%-90%的时间花在下载页面中所有的组件上。

规则一:减少HTTP请求数

1. CSS Sprites

将多个图片定位到整合成为一个图片,然后在用CSS来定位显示。同时还提到了图片热点(map标签)和data: URL模式。(实际生产中,应该极少会用到后边两种)

2. 合并脚本和样式表

顾名思义,就是将外联的所有脚本和样式表都整合成一个脚本和样式表。但是,需要注意的是,对于模块化的代码,将所有的JavaScript和CSS分别合并成为一个文件在开发环境中很难完成。例如,一个页面可能需要script1、script2和script3,而另一个文件可能需要script1、script3和script5。解决的方法是遵守编译型语言的模式,保持JavaScript模块化,而在生成过程中从一组特定的模块中生成一个目标文件。

伴随的代价是代码的复用率降低了(书中并没有提到),比如第一个页面将script1、script2和script3组合成为一个js文件,第二个页面将script1、script3和script5组合成为一个文件,显然代码script1和script3都被重新加载了一边。当然,作为文本的js文件,尤其是在压缩之后,一般情况下,重复加载的模块所带来的文件大小的增加应该是可以忽略的。

规则二:使用内容发布网络

你的用户肯能遍布世界的各个角落,即使同一台服务器,不同用户的页面响应时间也是不一样的,有些情况下会明显的影响到用户体验。一个解决方法是,采取分布式的构架重新设计你的web程序,像谷歌在全球的不同地域部署服务器一样。但是,这样巨大的开销和任务。另一个简单的解决方法就是,将组件web服务器从应用服务器中分离开来,托管于一些内容发布网络。

缺点也是显而易见,你的页面响应时间会受到托管服务器的影响,如果托管服务器的性能下降,你的网站性能也会下降。并且你也无法直接控制服务器组件,例如修改HTTP响应头必须通过服务提供商来完成,而不是你的团队。

规则三:添加Expires头

web服务器通过Expires头来告诉web客户端它可以使用一个组件的当前副本,直到指定的时间为止。如下:

Expires Thu, 15 Apr 2010 20:00:00 GTM

它告诉浏览器可以使用改组件的副本一直持续到2010年4月15号。

Expires 的缺点是过期的日期需要经常检查,一旦这一天到来以后,需要在服务器中配置一个新的日期。不过,在HTTP1.1中提供了另外的字段来完成同样的功能。在HTTP1.1中,Cache-Control使用max-age来指定组件被缓存多久,它以秒为单位。同样避免了额外的HTTP请求。例如:

Cache-Control: max-age=315360000

一个最佳的实践是,长久的Expires应该包含任何不经常变化的组件,包括脚本、样式表和Flash组件。

同时,我们也应该注意到,当我们配置组件更新时,在直到过期日期之间,浏览器都不会检查任何更新。为了确保用户能够获取最新的版本,需要在所有的HTML页面中修改组件的文件名。

规则四:压缩组件

对样式和样式表等组件进行压缩将会减少传输时间。压缩组件的一种方式是,删除文档之间的空格和注释,甚至缩短JS文件中的变量名等,现在有很多工具都可以做到这一点。另一种效果显著的方法是压缩传输的文件,从HTTP1.1开始,客户端可以通过Accept-Encoding头来标识对压缩的支持:

Accept-Encoding: gzip, deflate

web服务器通过Content-Encoding头来通知客户端采用了何种压缩:

Content-Encoding: gzip

其中,gzip是目前最有效和最流行的压缩方式。

规则五:将样式表放在顶部

通常组件的加载是按照文档中的出现的顺序来加载的,所以将不需要立刻使用的样式(比如用户点击弹出对话框的样式)放在文档的末尾来加载,就可以获得一个较好的体验效果。但是,这个结论是错误的。

在IE8及更早的IE浏览器中,页面的逐步呈现会在样式表下载完成前被阻止,因为在样式表加载完成之前就构建呈现树是没有必要的。这样做带来的结果是,HTML在加载时不会提供任何视觉反馈,一直等到样式加载完成后,然后才呈现出整个页面,也就是所说的“白屏”,会然用户感觉到“缓慢”。

在IE9和其他浏览器已经没有“白屏”问题,即使样式放在底部,页面也可以逐步呈现,但是样式加载和解析之后,呈现的文字和图片就需要用新的样式进行重绘,这同样造成了闪烁,依旧是一种不友好的用户体验。

另外,W3C标准明确说明应该把样式放在HTML文档的头部。

规则六:将脚本放在尾部

脚本在加载时会阻塞所有的并行下载,因此所有位于脚本以下的内容的逐步呈现都会被阻止,因此需要将脚本放在文档的尾部。

脚本会阻塞并行下载原因有二。其一,脚本可能使用了document.write来修改页面的内容,因此浏览器不得不等待,以确保页面的正确布局。其二,为了保证脚本能够按照正确的顺序执行。如果同时并行下载多个脚本,就无法保证响应是按照特定的顺序到浏览器的。如果它们之间存在着依赖关系,那么就可能导致JavaScript错误。

另外一种建议就是使用延迟(Defferred)脚本。Defer 属性表明脚本可以延迟加载,因此浏览器可以继续进行呈现。不过,在Firefox中,即便是延迟脚本也会阻塞呈现和并行下载。

规则七:避免使用CSS表达式

IE浏览器支持CSS表达式,而其他浏览器只会简单的忽略。CSS表单式的样式如下:

with: expreesion(document.body.clientWidth < 600 ? "600px" : "auto");

expreesion接受一个JavaScript表达式。expression真正的问题在于表达式可能会被极度频繁的求职,例如上边的例子,求值不仅发生在浏览器窗口大小变化时,而每一次鼠标的移动都会造成表达式求值计算,如果一个文本输入框获得焦点,那么甚至会因为反复求值造成浏览器奔溃。

规则八:使用外部JavaScript和CSS

纯粹而言,内联样式更快一些,因为不仅合并后文件大小更小一些,更重要的是有效减少了HTTP请求。但是考虑到JavaScript文件和CSS文件会被缓存,这时外部JS和CSS文件就会带来性能收益。

规则九: 减少DNS查询

Internet是通过IP地址来查找服务器的,这意味着浏览器中输入URL在发送请求前会有一次域名的解析过程。

浏览器和操作系统各自都有自己的DNS缓存,如果请求的DNS存在在缓存中,那么便不需要向上级DNS解析器发起请求。而且每一个DNS记录都有一个缓存时间(Time-to0live, TTL)值。

减少页面中主机名的数量会减少DNS查找,但带来的另一个潜在的问题是,同时会减[图片]少了页面中并行下载的数量。书中给出的建议是,如果页面中有大量的组件,那么将这些组件放到至少2个,但不要超过4个主机名下。这是在减少DNS查找和允许高度并行下载之间做出很好的权衡。

规则十:精简JavaScript

精简是从代码中移除不必[图片]要的字符以减小其大小,而混淆则在精简的基础上,还修改部分代码,一般而言,函数和变量名会被转换成为更短的字符。

混淆的目的是增加对代码进行反向工程的难度,对于精简而言,优点是可以进一步减小代码。其缺点是,混淆过程本身可能引入错误,维护和调试也会变得更加困难。

因此,采取混淆还是精简,最终的决定需要考虑混淆能够带来额外的代码大小的减少量。一般情况下,建议采用精简而不是混淆。规则四提到的压缩能够极大的减小传输代码的大小,大约70%,所以压缩会被精简更加有效。在采用了压缩的情况下,精简和混淆之间的区别会进一步减小,几乎一样。

规则十一:避免重定向

重定向是用于将用户从一个URL重新路由到另一个URL。重定向会延迟整个HTML文档的传输。下面介绍几种常见的重定向以及解决方案。

  • 缺少结尾的斜线

在URL必须出现斜线时没有出现就可能导致重定向,例如,访问http://astrology.yahoo.com/astrology 时,就会产生一个301响应,其中包好了一个到 http://astrology.yahoo.com/astrology/ 的重定向。这是一种最为浪费、发生的也很贫乏的重定向。

解决的方法,一般通过配置web服务器就能解决这个问题。

  • 连接网站

这种情况一般发生在网站被重写导致新的URL和旧的URL不一样,另外也包括将一个网站的不同部分连接起来等。重定向让连接两个网站很简单,而且只需要很少的额外代码。

解决的方法也较多,比如,如果后端位于同一台服务器上,则它们的代码可能自己均能够连接,如果域名变了,则可以使用一个CNAME让两个主机名指向相同的服务器。

  • 跟踪内部流量

重定向经常用于跟踪内部的流量。解决方案比较复杂,可以参考书中。

规则十二:移除重复的脚本

导致一个脚本的重复有连个重要的因素——团队的大小和脚本的数量。而重复脚本损伤性能的方式有两种——不必要的HTTP请求和执行JavaScript所浪费的时间。

规则十三:配置ETag

实体标签(Entity Tag)是 web 服务器和浏览器用于确认缓存组件的有效性的一种机制。例如,请求如下:

GET /i/yahoo.gif HTTP 1.1
Host: us.yimg.com

服务器响应如下:

HTTP 1.1 200 OK
Last-Modified: Tue, 12 Dec  2006 03:03:59 GMT
ETag: "10c24bc-4ab-457e1c1f"
Content-Length: 1195

Etag 的加入为验证组件提供了比最新修改日期更为灵活的机制。例如,如果实体依据 User-Agent 或 Accept-Language 头而改变,实体的状态可以反映在 Etag 中。

此后,如果浏览器必须验证一个组件,他会使用 If-None-Match 头将 ETag 传回原始服务器。

GET /I/YAHOO.GIF HTTP 1.1
Host: us.yimg.com
If-Modified-Since: Tue, 12 dec 2006 03:03:59 GMT
If-None-Match: "10c24bc-4ab-45e1c1f"

如果 ETag 匹配的,就会返回 304 状态码。

HTTP 1.1 304 Not Modified

ETage 的问题在于,通常使用组件的某些特性来构造它,这些属性对于特定的、寄宿了网站的服务器来说是唯一的。当浏览器从一台服务器上获取了原始组件,之后,又向另一台不同的服务器发起条件 GET 请求时, ETag 是不会匹配的——而对于使用服务器集群来处理请求的网站来说,这是很常见的一种情况。依据 HTTP1.1 规范,如果请求中同时出现了 If-Modified-Since 和 If-None-Match 这两个头,除非请求中的条件头字段全部一致,否则服务器禁止返回 304。

解决的方法是配置或者移除ETag。通过配置 web 服务器或者通过后端应用程序直接控制,使得访问不同服务器上相同的组件时,返回的 ETag 是相同的。当然更加直接的方法是,不使用 ETag。

规则十四:使 Ajax 可缓存

确保 Ajax 请求遵循性能指导,尤其应具有长久的 Expires 头。

后记

本片文章只是简单的笔记,原书中提供了大量的实例从数据上去论证这些规则,如果需要有深刻的了解,应该去阅读原书。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容