高性能网站建设指南
参考 https://blog.csdn.net/datou0529/article/details/80490180
参考 https://blog.csdn.net/CHS007chs/article/details/81628668
规则1:减少HTTP请求
- 图片地图:
在一个图片上关联多个url,目标url的选择取决于用户点击了图片上的哪个位置。
- css spirits
也可以合并图片,但更为灵活。
<style type="text/css"> #navbar span { width: 31px; height: 31px; display: inline; float:left; background-image: url(/img/sprite.gif); } .home {background-position: 0 0; margin-right: 4px; margin-left: 4px;} .gifts {background-position: -32px 0; margin-right: 4px;} .cart {background-position: -64px 0; margin-right: 4px;} .settings {background-position: -96px 0; margin-right: 4px;} .help {background-position: -128px 0; margin-right: 0;} </style> <div id="navbar" style="background-color: #F4F5EB; border: 2px ridge #333; width:180px; height: 32px; padding: 4px 0 4px 0"> <a href="javascript:alert('Home')"><span class="home"></span></a> <a href="javascript:alert('Gifts')"><span class="gifts"></span></a> <a href="javascript:alert('Cart')"><span class="cart"></span></a> <a href="javascript:alert('Settings')"><span class="settings"></span></a> <a href="javascript:alert('Help')"><span class="help"></span></a> </div>css sprites还可以通过合并图片减少http请求,还降低了下载量,虽然有人会认为合并后的图片比分离的图片总和要大,但实际上合并后的图片会比分离的图片总和要小,因为它除低了图片自身的开销(颜色表,格式信息等)。
- 内联图片:通过data:URL模式可以在Web页面中包含图片但无需任何额外的http请求,同时可能存在数据大小上的限制。由于data:URL是内联在页面中的,在跨越不同页面时不会被缓存,所以不要去内联公司的logo。
- 合并脚本和样式表
规则2:使用内容发布网络(CDN, Content Delivery Network)
内容发布网络cdn: 分布于不同地理位置的服务器。
- 优点:可缩短响应时间,备份、扩展存储能力、缓存,可缓和流量峰值压力
- 缺点:响应时间受cdn服务提供商环境影响;无法直接控制组件服务器
cdn服务器一般用于静态内容,如图片、脚本、样式表、flash等。
规则3:添加Expires头
【Expires: Thu,15 Apr 2010 20:00:00 GMT】
1.作用
web服务器使用Expires头来告诉web客户端它可以使用一个组件的当前副本,直到指定的时间为止。HTTP规范中简要的称该头为“在这一日期之后,响应被认为是无效的”,它在HTTP响应中发送。
如果页面中的一个图片返回了这个头,浏览器在后续的页面中会使用缓存的图片,将HTTP请求的数量减少一个。
2.缺点
它要求服务器和客户的时钟严格同步,另外,过期日期需要经常检查,如果过期后,还需要在服务器配置中提供一个新的日期。
3.另一种选择
Cache-Control: 【Cache-Control:max-age=315360000】
Cache-control使用max-age指令指定组件被缓存多久,它以秒为单位定义了一个更新窗。
4.更新的方法
Expires头会使用浏览器直接从硬盘上读取组件而无需生成新的http流量。因此,即使在服务器上更新了组件,已经访问过网站的用户也不大可能获取新的组件。为了确保用户能获取组件的最新版本,需要在所有html页面中修改组件的文件名:最有效的方案是修改所有链接,比如可以嵌入版本号。
规则4:压缩组件
web客户端可以通过http请求的Accept-Encoding头来表示对压缩的支持:
Accept-Encoding: gzip,deflate
如果web服务器在请求信息中看到这个头信息,它就可以通过响应的Content-Encoding头信息来返回服务器可用的压缩方式。
Content-Encoding: gzip
1.压缩什么
基于文本的资源如html,js ,css, xml都适用于压缩,然后对于图片而言,却不应该对图片进行压缩,因为图片本身已经被压缩过了,如果再进行gzip压缩,有可能得到的结果是和图片本身大小相差不大或更大,这样就浪费了服务器的CPU资源来做无用功了。
2.代理缓存的问题
浏览器直接与服务器通信,基于Accept-Encoding都可以很好的工作。但如果通过代理,这种情况下,压缩就要考虑额外的东西。
首先,假设到达代理的是一个来自不支持gzip的浏览器的请求,代理会将请求转发到web服务器,此时web服务器的响应是未经压缩的,这个响应会把代理服务器缓存起来并发给浏览器,现在,假设到达代理的第二个请求来自一个支持gzip浏览器,请求的是与之前相同的URL,代理会直接使用未经压缩的缓存响应,那么就失去了进行压缩的机会。考虑更糟糕的情况,第一个请求来自支持gzip的浏览器,第二个请求来自不支持gzip的浏览器,这样第二个请求得到的缓存响应将无法被解码,导致出错。
解决这一问题的方法就是在Web服务器的响应中添加Vary头
Vary:Accept-Encoding
表示web服务器告诉缓存服务器分别为每一个Accept-Encoding请求头缓存。
规则5: 将样式表放在顶部
-
ie中,将样式表放到文档底部,导致白屏的情形,其他情况承担fouc风险(firefox都是选择承担fouc风险)
- 新窗口打开时
- 重新加载
- 作为主页
- 将css放在文档顶部,可避免白屏
- 一个style块可以有多个import,但是必须放在其他规则之前。
- import规则可能会导致白屏现象,即便把import放在文档head中也是如此。
- 使用import会导致下载的无序性,使用link标签将样式表放到文档的head中
- 无样式的内容闪烁 flash of unstyled content (fouc):已经加载的文档重会导致的
- 白屏与fouc: 将样式表放到文档末尾,浏览器延迟呈现,避免fouc,则会导致白屏;反之,若浏览器逐步呈现,则有fouc的风险。解决办法:遵循html规范,将css放在顶部,即文档head中
- 样式表在页面并不会影响下载时间,但是会影响页面呈现。
- 将样式表放在文档底部会导制在浏览器中阻止内容逐步呈现,为避免当样式变化时重绘页面中的元素,浏览器会阻塞内容逐步呈现。
1. link标签: <link rel="stylesheet" href="style.css"> 2. @import规则: <style> @import url("style.css"); </style>
规则6: 将脚本放在底部
- 好处:页面可以逐步呈现,也可以提高下载并行度
- 使用css,页面逐步呈现会被组织,直到所有样式表下载完成。(放在head中的原因)
- 使用脚本,对于所有位于脚本以下的内容,逐步呈现都被阻塞了。
- 浏览器会并行的执行http请求,但是同一主机下的数量有限,因此会发生等待。
-
脚本阻塞下载原因:
-) 脚本可能使用document.write修改页面内容
-) 无法保证按照正确的顺序执行,因此导致报错 -
无法将脚本移至底部的情形:使用document.write对页面进行了修改,作用域问题等。
- 延迟脚本defer: defer属性表明脚本不含document.write,浏览器可以继续呈现。如果一个脚本可以延迟,则可以移到页面顶部。在firefox中,即便是延迟脚本也会阻塞呈现和并行下载。
规则7: 避免css表达式
ie支持expression,而其他浏览器不支持,如下:
width:expression(document.body.clientWidth<600?"600px":"auto"); //js表达式,仅适用IE,后果CSS性能低下
min-width:600px;//other
表达式的求值频率太高了,它们不止在页面呈现和大小改变时求值,当页面滚动、甚至鼠标拖拽都要求值。
解决方法:
1.一次性表达式
2.事件处理器。
规则8:使用外部Javascript和CSS
外部CSS/JS和内联CSS/JS的区别:
- 外部CSS/JS可以缓存;
- 内部CSS/JS可以减少HTTP开销,更快加载;
- 经常访问,使用外部CSS/JS更佳;
- 高完整缓存率,使用外部CSS/JS更佳;
- 每个页面都使用相同的CSS/JS,则使用外部CSS/JS更佳;
- 主页: 仅有一个页面查看,每次都是空缓存,组件重用率低 ==》内联更佳
- 加载后下载 post-onload download: 文档onload事件1s延迟之后,确保页面呈现完毕后才会下载js/css文件
- 动态加载技术 dynamic inlining: 利用cookie是否存在,判断是否有缓存,有cookie则存在缓存,使用外部CSS/JS,如果没有,则使用内联CSS/JS。
结论:重用度很高,使用外部CSS/JS,否则使用内部
规则9:减少DNS查找(通过Keep-Alive和较少的域名来减少DNS查找)
- DNS开销 查找一个给定服务器的IP地址要花费20~120毫秒,DNS查找完成前,浏览器不能从主机那里下载任何东西
- DNS缓存:ISP/局域网的特殊缓存服务器,操作系统的DNS缓存、浏览器缓存。当浏览器丢失了记录时,才会请求操作系统询问地址;
- 存活时间TTL:返回的DNS请求包括这个值,告诉客户端可以缓存多久。但浏览器通常忽略该值,通过keep-Alive覆盖时间限制,同时DNS记录的数量也有限制,而不管缓存记录的时间。超过了限制,早期的访问记录会被删除。客户端收到的平均TTL值一般为最大TTL值的一半,由于DNS解析器自身也拥有与DNS记录相关的TTL.
- IE中记录和查看dns的:
ipconfig /displaydns;ipconfig /flushdns - 减少DNS查找与增加并行下载的平衡: 至少两个主机,不超过4个
结论:通过使用keep-alive和较少的域名,减少dns查找
规则10:精简JavaScript
- 精简(Minification): 从代码中移除不必要的字符以减少其大小,进而改善加载时间。如:空白符(空格、换行、制表符)
- 混淆(Obfiscation): 与精简一样,会移除注释和空白,同时还会改写代码。改写:函数和变量名会变成更短的字符串,代码更加精炼,更难阅读,增加反向工程的难度。
- 混淆的缺点:缺陷 由于混淆更加复杂,混淆过程本身很有可能引入错误;维护 混淆会改变js符号,对于不能更改的需要进行标记 调试 代码难以阅读,增加调试难度
- gzip压缩产生影响最大,但精简能进一步减小文件大小
- 精简CSS带来的节省小于JS,因为通常css注释和空白,比js少。本质的优化在于合并相同的类,移除不使用的类
规则11:避免重定向
- 重定向原因:网站重新设计、跟踪流量、记录广告点击、建立易于记忆的URL。 重定向会使页面变慢
- 浏览器会自动将用户带到location字段的url,通常301和302响应不会被缓存,除非有附加头expires或cache-control要求这么做;
- meta重定向
<meta http-equiv="refresh" content="0; url=http://www.baidu.com">0s后重定向 - js重定向,将document.location设置为期望的url即可。
- 为确保后退按钮正确工作,最好使用3XX HTTP状态码
- 缺少结尾的斜线
http://www.baidu.com/,很多web服务器会发送重定向,包括apache。原因为:允许自动索引,自动转到默认的index.html上。 - 跟踪内部流量:若同一家网站之间的流量,通过建立Refer日志来避免重定向。
-
跟踪出站流量:
http://www.baidu.com***http%3a//en.wikipedia.org会返回302. 使用信标 beacon,信标响应通常是1px*1px的透明图片,并在图片的url中包含要跟踪的信息(实际只需使用xmlHttpRequest readyState=2时确认已发送即可响应请求,或者使用新打开标签页或者弹出页target='_blank'即可。)
300 Multiple Choices (基于Content-type)
301 Moved Permancently
302 Moved Temporarity(亦作Found)
303 See Other(对302的说明)
304 Not Modified 并非真的重定向,用来响应条件GET请求,避免下载已经存在于浏览器缓存中的数据
305 Use Proxy
306 (不再使用)
307 Temporary Redirect(对302的说明)
规则12:删除重复脚本
- 导致脚本重复的原因: 团队大小和脚本数量
- 重复执行,重复下载(可通过缓存解决)
规则13:配置ETag
- ETag: Entity Tag 实体标签,是web服务器和浏览器用于确认缓存组件的有效性的一种机制。浏览器通过返回的expires判断缓存是否新鲜;如果过期了,浏览器在重用它之前首先得检查它是否仍然有效,这称作 条件get请求。如果浏览器缓存的组件是有效的,则服务器不会返回整个组件,只会返回304。
- 服务器在检测缓存组件是否和原服务器组件一致时,有两种方式:比较最新修改日期(服务器响应last-modified,浏览器使用if-modified-since回传服务器进行比较,若比较一致,则返回304)、比较实体标签(http1.1中引入,唯一标识一个组件的一个版本的字符串,需要以引号引起来,服务器响应ETag, 浏览器if-none-match回传到服务器进行比较,若匹配,返回304)。
- etag的意义:提供了一个比最新修改时间更灵活的机制。
- etag的问题:对服务器集群、代理来说,etag会不匹配,降低缓存效率
- if-none-match比if-modified-since有更高的优先级。
- 即便配置了很长的expires头,当用户reload或者refresh时,仍会产生条件get请求。如果需要通过除最新修改日期之外的一些东西来验证,则etag是一种强大的方法,否则最好简单的移除(etag性能问题)。
结论:需要配置或者直接移除etag.

浙公网安备 33010602011771号