** 性能黄金法则 **
只有10%20%的最终用户响应时间花在了下载HTML文档上。其余的80%90%时间花在了下载页面中的所有组件上。
** 前端处理规则 **
- 减少HTTP请求
- 使用内容发布网络
- 添加Expires头
- 压缩组件
- 将样式表放在顶部
- 将脚本放在底部
- 避免CSS表达式
- 使用外部JavaScript和CSS
- 减少DNS查找
- 精简JavaScript
- 避免重定向
- 移除重复脚本
- 配置ETag
- 使Ajax可缓存
HTTP相关概念
- HTTP请求类型:
GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE
- 压缩:
浏览器使用Accept-Encoding头来声明支持压缩:Accept-Encoding:gzip,deflate
服务器使用Content-Encoding头确认响应已被压缩:Content-Encoding:gzip - 条件GET请求:浏览器在缓存中保存了一个组件的副本,当If-Modified-Since头询问组件副本是否可用,服务器若返回304响应,浏览器得到一个更小更快的响应(没有响应体)。
- Expires:Expires:Wed,05 Oct 2016 19:16:20 GMT
Expires头明确指出浏览器是否可以使用组件副本。浏览器将过期时间和组件副本一同存入缓存,不再询问服务器组件副本是否可用。 - Keep-Alive:浏览器和服务器均使用Connection:keep-alive头指出对Keep-Alive的支持。
浏览器或服务器通过发出Connection:close关闭连接。
注:在HTTP1.1中定义的管道技术普及之前,Keep-Alive依然是必要手段。对于HTTPS来说更重要,因为建立新的安全socket连接需要更多时间。
规则笔记
规则1.减少HTTP请求
技术:图片地图、CSS Sprites、内联图片和脚本、样式表的合并。
【图片地图】
图片地图(Image Map):将多个带有链接的图片合并为一张图片,通过图片地图,点击不同位置链接到不同位置。
分类:服务器端图片地图&客户端图片地图(常用)
示例代码(客户端):
<img usemap="#map1" src="/img/example.png">
<map name="map1">
<area shape="rect" coords="0,0,31,31" href="location1" title="ToLocation1">
<area shape="rect" coords="0,12,121,213" href="location2" title="ToLocation2">
……
<area shape="rect" coords="141,0,171,31" href="locationN" title="ToLocationN">
</map>
缺点:
- 手工完成困难容易出错
- 除了矩形之外几乎无法定义其他形状
- 通过DHML创建的图片地图无法再IE中工作
【CSS Sprites】
即雪碧图。将多个图片合并成一张图片,通过background-position控制显示。
【内联图片】
使用data:URL模式可以在Web页面中包含图片但无需任何的HTTP请求。
常见的URl模式:http:、ftp:、file:、mailto:、smtp:、pop:、dns:、whois:、finger:、daytime:、news:、urn:。
data:URL模式格式:
data:[<mediatype>][;base64],<data>
data一般被用于内联图片,但它可以用在任何指定URL的地方。
注:不要内联公司logo。做法:CSS将内联图片作为背景,将CSS放在外部样式表中(达到浏览器缓存功能)。
经验:普通图片就不要采用内联图片了,数据量巨大,严重增大HTML文档的大小。
【合并脚本和样式表】
规则2.使用内容发布网络
- 在多个地理位置不同的服务器上部署内容
- 实现地理位置分离的第一步,不要尝试使用分布式架构重新设计你的应用程序
- 使用CDN(缺点:CDN服务挂掉将直接影响应用,不可控因素;解决方法:使用多家CDN服务)
- 测试CDN服务的效果和测试所在地理位置有关
- 有助于完成测试的两个网站:Keynote System(www.keynote.com)和Gomez(www.gomez.com)
规则3.添加Expires头
在这一日期/时间之后,响应将被认为是无效的。
- HTTP1.1引入Cache-Control头克服Expires头限制(要求服务器客户端的始终严格同步,还需要配置维护)
Cache-Control:max-age:315360000
注:时间单位为秒
- mod_expires Apache模块使你在使用Expires头时能够像max-age那样以相对的方式设置时间。
注:此部分示例代码由于和MarkDown冲突会显示错误,详细内容请自行查询资料
【空缓存VS完整缓存】
使用长久的Expires头
【修订文件名】
为使用户获取更新,将组件的文件名加上版本号。更新文件时修改文件版本号将会使用户重新从服务器上获取组件。
规则4.压缩组件
减小页面大小的方法:
- 压缩HTTP响应包(效果最显著)
- 删除注释
- 缩短URL
- Web客户端通过HTTP请求中的Accept-Encoding头来标识对压缩的支持
Accept-Encoding:gzip,default
- Web服务器通过客户端列出方法中的一种来压缩响应。
- gzip是最理想的压缩方式。
- 需要压缩的内容:HTML文档、脚本、样式表、XML和JSON在内的任何文本响应。
- 不应该压缩的内容:图片和PDF(浪费CPU,增加大小)
- 压缩成本:服务器CPU开销和客户端解压缩耗费的资源
- 检测收益是否大于开销需要考虑的因素:响应的大小,连接的带宽,客户端与服务端Internet距离
- 根据经验,通常对大于1KB或2KB的文件进行压缩
- mod_gzip_mininum_file_size控制着希望压缩文件的最小值,默认为500B
【代理缓存】
存在两种方式上的问题:
问题1:
不支持gzip的浏览器先访问了代理服务器,代理服务器转发请求,服务器返回未经压缩的响应包给代理服务器,代理服务器将未经压缩的响应包缓存后转发给该浏览器;
支持gzip的浏览器请求代理服务器,代理服务器发现缓存,直接将缓存的未经压缩的响应包返回给该支持gzip的浏览器。
结果:虽然支持gzip的浏览器收到的是未经压缩的响应包,未提高效率,但最终还能完成基本的请求。
问题2:
支持gizp的浏览器先访问了代理服务器,代理服务器转发请求,服务器返回经过压缩的响应包给代理服务器,代理服务器将经过压缩的响应包缓存后转发给该浏览器;
不支持gzip的浏览器请求代理服务器,代理服务器发现缓存,直接将缓存的经过压缩的响应包返回给该不支持gzip的浏览器。
结果:第二个不支持gzip的浏览器收到的是经过压缩的响应包,无法正常解析。
解决方法:在Web服务器中添加Vary头
Vary:Accept-Encoding
规则5.将样式表放在顶部
违反规则将样式表放在顶部或HEAD标签外,虽然不影响加载组件的时间,但会影响文档呈现效果,这有不同的浏览器决定(白屏或无样式内容闪烁)。
规则6.将脚本放在底部
规则7.避免CSS表达式
规则8.使用外部的JavaScript和CSS
如果要实现主页快速访问且后续网页也能较快使用组件,可以这样:
1.在主页加载完成后下载后续网页所需组件(加载后下载)
2.动态内联(利用Cookie作指示器,如果有缓存,则生成一个使用外链组件的主页,并在主页加载完成后下载后续组件;如果没有缓存,则生成一个使用内联组件的主页,同样在加载完成后下载后续组件)
规则9.减少DNS查找
当客户端的DNS缓存为空(浏览器和操作系统都是)时,DNS查找的数量与Web页面中唯一主机名的数量相等。
建议:将组件分别放到2~4个主机名下(减少主机名潜在减少了组件并行下载的数量)。
规则10.精简JavaScript
从代码中移除不必要的字符,包含注释、不必要的空白字符(空格、换行和制表符)
【混淆】
精简 + 改写函数和变量名
通常目标:增加反向工程难度
缺点:
- 缺陷:由于混淆更加复杂,混淆过程本身很可能引入错误。
- 维护:由于混淆会改变JavaScript符号,因此需要对任何不能改变的符号(例如API函数)进行标记,防止混淆修改它们。
- 经过混淆的代码很难阅读。这使得在产品环境调试问题更加困难。
精简JavaScript的工具:JSMin和DojoCompressor(ShrinkSafe)
【精简CSS】
收益通常小于精简JavaScript
优化CSS:
- 合并相同的类、移除不使用的类
- 移除注释和空白
- 直观优化(如缩写#606代替#660066)
- 移除不必要字符(例如0代替0px)
规则11.避免重定向
规则12.移除重复脚本
规则13.配置ETag
ETag是唯一标识了一个组件的一个特定的字符串。
- 唯一的格式约束是该字符串必须用引号引起来。
- 如果你无需自定义ETag,最好简单地将其移除。
规则14.使Ajax可缓存
应具有长久的Expires头
此篇笔记是我阅读《高性能网站建设指南》这篇文章记录的笔记,写出来与大家共享。
如果需要阅读原文,请下载PDF文档:《高性能网站建设指南》