最近很久没有看前端相关书籍,有所生疏了。今天看到《高性能网站建设指南》,觉得还行,内容不多,不到200页,却将常见的前端提升网站性能的方法进行了罗列:
减少http请求
- 图片地图
- css spirits
- 内联图片:通过data:URL模式
- 合并脚本和样式表
使用内容发布网络
内容发布网络cdn: 分布于不同地理位置的服务器。
- 优点:可缩短响应时间,备份、扩展存储能力、缓存,可缓和流量峰值压力
- 缺点:响应时间受cdn服务提供商环境影响;无法直接控制组件服务器
cdn服务器一般用于静态内容,如图片、脚本、样式表、flash等。
添加expires头
- web服务器使用expires告诉web客户端可以使用当前副本,直到指定时间为止。要求服务器和客户端时间严格同步
- http1.1使用
cache-control
头来克服这一限制,使用max-age
指定缓存时间。两者同时出现时,若浏览器均支持,则max-age指令会重写expires - 为图片、样式表、脚本等不经常变化的组件使用expires
- 为了确保用户获取到最新版本,需要修改页面中的文件名
压缩组件
- 压缩有成本,服务器要花费额外的cpu周期完成压缩,客户端要对压缩文件进行解压缩
- 代理缓存压缩与未压缩版本:web服务器响应中添加
vary:Accept-Encoding
区别
将样式表放在顶部
ie中,将样式表放到文档底部,导致白屏的情形,其他情况承担fouc风险(firefox都是选择承担fouc风险)
- 新窗口打开时
- 重新加载
- 作为主页
- 将css放在文档顶部,可避免白屏
- 一个style块可以有多个import,但是必须放在其他规则之前。
- import规则可能会导致白屏现象,即便把import放在文档head中也是如此。
- 使用import会导致下载的无序性,使用link标签将样式表放到文档的head中
- 无样式的内容闪烁 flash of unstyled content (fouc):已经加载的文档重会导致的
- 白屏与fouc: 将样式表放到文档末尾,浏览器延迟呈现,避免fouc,则会导致白屏;反之,若浏览器逐步呈现,则有fouc的风险。解决办法:遵循html规范,将css放在顶部,即文档head中
- 样式表在页面并不会影响下载时间,但是会影响页面呈现。
将脚本放在底部
- 好处:页面可以逐步呈现,也可以提高下载并行度
- 使用css,页面逐步呈现会被组织,直到所有样式表下载完成。(放在head中的原因)
- 使用脚本,对于所有位于脚本以下的内容,逐步呈现都被阻塞了。
- 浏览器会并行的执行http请求,但是同一主机下的数量有限,因此会发生等待。
脚本阻塞下载原因:
-) 脚本可能使用document.write修改页面内容
-) 无法保证按照正确的顺序执行,因此导致报错无法将脚本移至底部的情形:使用document.write对页面进行了修改,作用域问题等。
- 延迟脚本defer: defer属性表明脚本不含document.write,浏览器可以继续呈现。如果一个脚本可以延迟,则可以移到页面顶部。在firefox中,即便是延迟脚本也会阻塞呈现和并行下载。
避免css表达式
ie支持expression,而其他浏览器不支持,如下:
width:expression(document.body.clientWidth<600?"600px":"auto"); //js表达式,仅适用IE,后果CSS性能低下
min-width:600px;//other
结论:避免适用CSS表达式
使用外部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,否则使用内部
减少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查找
精简javascript
- 精简: 从代码中移除不必要的字符以减少其大小,进而改善加载时间。如:空白符(空格、换行、制表符)
- 混淆: 与精简一样,会移除注释和空白,同时还会改写代码。改写:函数和变量名会变成更短的字符串,代码更加精炼,更难阅读,增加反向工程的难度。
- 混淆的缺点:缺陷 由于混淆更加复杂,混淆过程本身很有可能引入错误;维护 混淆会改变js符号,对于不能更改的需要进行标记 调试 代码难以阅读,增加调试难度
- gzip压缩产生影响最大,但精简能进一步减小文件大小
- 精简CSS带来的节省小于JS,因为通常css注释和空白,比js少。本质的优化在于合并相同的类,移除不使用的类
避免重定向
- 重定向原因:网站重新设计、跟踪流量、记录广告点击、建立易于记忆的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'
即可。)
移除重复脚本
- 导致脚本重复的原因: 团队大小和脚本数量
- 重复执行,重复下载(可通过缓存解决)
配置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.
使ajax可缓存
参考资料:
- 《高性能网站建设指南》 Steve Souders 2008年