优化指标:
页面打开速度(Fully Loaded)
- 网站首页(或列表页)之 First View :打开速度应在 3秒+0.5秒 内;
- 对 Repeat View 时的各项指标暂不作要求;
首屏打开时间(Start Render)
- 网站首页(或列表页) 之 First View :首屏渲染速度应在 1秒+0.5秒 内;
文档解析完毕时间(Document Complete):
提纲:
- 遵循常规优化建议
- 外联内联js/css的位置摆放建议
- combo handler的引入
- 图片无损压缩的优化
- 减少 dom elements 的数量
- 引入 textarea/script 元素做延迟解析异步渲染
第一阶段
1、js文件压缩,尽量放在页面底部,引入js文件代码放在css引入代码后
2、css 外联文件引用在 html 文档头部:位于 header 内;
3、http 静态资源尽量用多个子域名:充分利用现代浏览器的多线程并发下载能力。浏览器的多线程下载能力
- JS、CSS、CSS背景图片、CSSSprite图片分散在 s0~s2.55tuanimg.com 下;
- 业务类图片分散在 p0~p3.55tuanimg.com 下;
Browser | HTTP 1.1 | HTTP 1.0 |
---|---|---|
IE 6、7 | 2 | 4 |
IE 8 | 6 | 6 |
Firefox 3+ | 6 | 6 |
Safari 3+ | 4 | 4 |
Chrome 3+ | 4 | 4 |
Chrome 11+ | 6 | ? |
Opera 10+ | 4 | 4 |
Opera 11+ | 16 | ? |
4、减少不必要的 301/302 跳转:别让页面打开时间浪费在302多次跳转上(每次可能几十毫秒);
5、请大量使用 CSS Sprites:这样做可以大大地减少CSS背景图片的HTTP请求次数;
6、首屏不需要展示的较大尺寸图片,请使用 LazyLoad ;
7、避免404错误:请求一个外联 js 失败时获得的404错误,不但会堵塞并行的下载,而且浏览器会尝试分析404响应的内容,来辨识它是否是有用的Javascript代码;
8、避免使用 javascript 来定位布局;
9.减少 DOM Elements 的数量,有人测试,每增加一个 DOM 节点,会导致首屏渲染时间延迟约 0.5ms。
第二阶段
1、服务器端提供 html 文档和 http 静态资源时,尽量开启 gzip 压缩;
-
json/xml 等格式的文本响应,也建议开启 gzip ;
-
jepg/png 等图片,可以选择不开启 gzip,因为可能服务器端图片无损压缩算法已经很强悍了,不需要依赖于 gzip;
2、在 js、css、image 等资源响应的 http headers 里,设置 expires、last-modified;
1)Expires
给出的日期/时间后,被响应认为是过时。如Expires: Thu, 02 Apr 2009 05:14:08 GMT 需和Last-Modified结合使用。用于控制请求文件的有效时间,当请求数据在有效期内时客户端浏览器从缓存请求数据而不是服务器端. 当缓存中数据失效或过期,才决定从服务器更新数据。
2)Last-Modified和Expires
Last-Modified标识能够节省一点带宽,但是还是逃不掉发一个HTTP请求出去,而且要和Expires一起用。而Expires标识却使得浏览器干脆连HTTP请求都不用发,比如当用户F5或者点击Refresh按钮的时候就算对于有Expires的URI,一样也会发一个HTTP请求出去,所以,Last-Modified还是要用的,而 且要和Expires一起用。
nginx的设置
1 2 3 4 5 6 |
location ~ .*\.(js|css)${ expires 30d; } location ~ .*\.(gif|jpg|jpeg|png|bmp)${ expires 1M; } |
3、减少 cookies 的大小:尽量减少 cookies 的体积对减少用户获得响应的时间十分重要;
-
去除不必要的 cookie ; – 尽量减少 cookie 的大小;
-
留心将 cookie 设置在适当的域名下,避免影响到其他网站;
-
设置适当的过期时间。一个较早的过期时间或者不设置过期时间会更快地删除 cookie,从而减少响应时间。
4、使用无 cookies 的域:
-
当浏览其请求一个静态图片并一同发送 cookie 时,服务器并不需要这些 cookies 。这样只是毫无益处地创建了多余的网络流量。应当保证静态资源在请求时没有携带 cookies,所以需要把你的静态资源放在另一个子域名下。
-
如果你的域名是 www.example.org,你可以将你的静态资源放在 static.example.org 中。如果你把 cookie 设置在顶级域名 example.org 上而不是 www.example.org,那么所有发送至 static.example.org 的请求会包括那些 cookies。在 这种情况下,你可以用一个全新的没有 cookie 的域名来放置你的静态资源。
5、注意加载和阻塞的三条定律
- 资源是否下载依赖 JS 执行结果——JS 有可能会修改 DOM。典型的,可能会有 document.write。这意味着,在当前 JS 加载和执行完成前,后续所有资源的下载有可能是没必要的。这是 JS 阻塞后续资源下载的根本原因。
- JS 执行依赖 CSS 最新渲染——JS 的执行有可能依赖最新样式。比如,可能会有 var width = $(‘#id’).width(). 这意味着,JS 代码在执行前,浏览器必须保证在此 JS 之前的所有 css(无论外链还是内嵌)都已下载和解析完成。这是 CSS 阻塞后续 JS 执行的根本原因。
- 现代浏览器存在 prefetch 优化—— 现代浏览器在竞争中,在 UI update 线程之外,还会开启另一个线程,对后续 JS 和 CSS 提前下载(注意,仅提前下载,并不执行)。有了 prefetch 优化,这意味着,在不存在任何阻塞的情况下,理论上 JS 和 CSS 的下载时机都非常优先,和位置无关。
第三阶段 Combo Handle
Combo Handler 是 Yahoo! 开发的一个 Apache 模块,它实现了开发人员简单方便地通过URL来合并JavaScript和CSS文件,从而大大减少文件请求数。
它满足 Yahoo! 前端优化第一条原则:Minimize HTTP Requests,来减少三路握手和HTTP请求的发送次数。
实例:淘宝网首页meta里多个js合并的声明:
1 |
<script src="http://a.tbcdn.cn/??s/kissy/1.2.0/kissy-min.js,p/global/1.0/`global-min.js`,p/fp/2012/`core.js`,p/fp/2012/fp/`module.js`,p/fp/2012/fp/util.js,p/fp/2012/fp/`directpromo.js`?t=2012062320120712.js" data-fp-timestamp="20120703"></script> |
js之间用英文逗号或&符号分隔。此src的Response是多个js文件的内容拼装。
国内的 Combo Script 支持 淘宝李晶-拔赤在 https://github.com/jayli/combo 下发布了combo.php和minfy.php,能够做到合并文件(不压缩),以及合并且压缩。
文件列表:
combo.php 合并文件,不压缩
minify.php 合并压缩文件
cssmin.php 压缩css
jsmin.php 压缩js
cb.php 淘宝CDN合并文件策略的模拟
脚本使用: – 要求php5及以上版本 – 程序在找不到本地文件的情况下,会去指定的cdn上找同名文件 – 程序会自动转义-min文件为源文件,因此要约定-min文件和原文件要成对出现 – 需要定义combo.php和minify.php中的$YOUR_CDN变量 – 如果只是合并压缩local文件,则不必重置$YOUR_CDN变量 – 这里提供cb.php,用来实现tbcdn的开发环境的模拟,apache的配置在cb.php中
CDN上的 Combo Handler支持
1)2008年7月YUI Team宣布在YAHOO! CDN上对YUI JavaScript组件提供Combo Handler服务。
2)淘宝CDN支持Combo Handler,用逗号分隔js/css,用两个问号来触发combo特性:
- http://a.tbcdn.cn/??1.js,2.js
- http://a.tbcdn.cn/subdir/??1/js,2.js
用一个问号来添加时间戳,如:http://a.tbcdn.cn/??fp/directpromo.js?t=2012062320120712
为了避免 CDN 缓存错误的版本,combo上线的访问策略是:
1)静态文件传到服务器端;
2)部署人员使用线上静态文件服务器的IP地址直接请求combo服务,挨个儿combo请求一次;
3)部署人员确认上面的请求都成功、内容无误后,再换成CDN地址再次请求,确保CDN缓存正确的文件内容。
第四阶段:图片无损压缩的优化
页面上的各种图片是否有优化的余地,推荐使用 PageSpeed 检测一下。
1)jpegtran和jpegoptim的压缩效果几乎完全相同。 但jpegtran有progressive编码(渐进式的展示,先显示模糊的,再逐步清晰),而且通常(84%的概率)对于大图片(10k+)压缩比更高。 虽然我们的大部分页面已经改成延迟加载了,但对于非延迟加载的页面,效果明显更好。 经测试,pagespeed 并没有按照 progressive 方式提供建议。
2)测试了png的几种压缩方式,压缩效果各异。测试的一张图片 optipng 只压缩了约5%,但其他几种达到了20%+ 经测试,pagespeed上给出的可压缩比例是按照optipng给出的。 pngout据说采用了不同的编码,因此对小图片压缩效果更好。用imageoptim测试确实略优,但命令行上还没找到合适的调用参数。 目前决定采用pngcrush。 对于采用png8,以大幅压缩的方法,我们不做技术处理。
3)gif就采用gifsicle做压缩。 大部分情况下,我们不建议采用gif图片。对于单帧gif更应该用png格式替代。 这里我们暂不考虑通过技术处理来吧单帧gif转换成png。
4)采用php的exec调用shell脚本的方式来执行这些bin文件。
第六阶段:引入 textarea/script 元素做延迟解析异步渲染
textarea 延迟渲染原理:
HTML 元素中有一种 RCDATA elements,含 textarea 和 title 。 RCDATA指的是,Replaceable Character Data。 如果用隐藏的 textarea 来存放 html 代码,textarea 中的内容会按照 RCDATA 规则来解析: 遇到 & 时,会尽可能得到实体字符。 遇到 </textarea(\s||>) 时,会结束解析。 其他都直接作为 textarea 的内容。
据yiminghe介绍,对于屏幕外延迟渲染的 html 存放在隐藏(visibility:hidden)的 textarea 中,并且该 textarea 占据本该渲染的位置,监控窗体滚动,当textarea进入可见区域,将该 textarea 内的 value, 插入到 textarea 之前,并删除掉 textarea 。
这样,把大量不需要在首屏展示的html代码分模块放入一个一个的 textarea 里,大大减少了DOM节点数,从而给浏览器合理的喘息(UI Update)时间,等首屏真正在显示器上绘制出来后,再得到 textarea.value ,填充回 DOM Tree。
textarea+datalazyload,相对于其他延迟加载异步渲染解决方案,最大好处,还是减少首屏绘制时的DOM节点总数。
script 延迟渲染原理 玉伯在《淘宝详情页的 BigRender 优化与存放大块 HTML 内容的最佳方式》中提到, 与前面说的 textarea 存放 html 代码一样,你也可以用 script 来存放,目的都是减少 DOM 节点数。 浏览器在拿到 html 代码时,首次 Tokenization — Tree Construction 的速度就会大大加快。
用JavaScript来存放html代码
在rew text elements里,可以用script存放数据:
1 2 3 4 5 |
<script type="text/html" id="script-data"> 这里是一些内容 </script> /*获取*/ var htmlCode = document.getElementById('script-data').innerHTML; |
缺点:1、服务器端,要讲script里面html中的script
标签替换为某种特殊标记;2、浏览器端,得到htmlCode后要将上面的特殊标记替换回原值。
另外:特殊标记的问题
1 2 3 4 5 |
<script type="text/html" id="script-data"> <script> var str = '<\/script>';/*特殊标记不能是这个啊*/ </script> </script> |
这样替换原值的时候,会误伤到str字符串
拿出一个实例
注意这些 script 的 type 是 text/x-template ,这是YUI类库自己定义的元素type。
你可以注意到,LABjs 也玩过这个小技巧,也是自己定义了一个元素 type“text/cache”, 由于浏览器不认识这种 type,就会主动忽略这个 HTML元素。
什么时候取出这些隐藏HTML代码呢?
那就要用到这些 script 的 id 了。
YUI的教程上是这么获得 HTML 代码:
1 |
template: Y.one('#todo-item-template').getHTML() |
某网的做法是:
比如id=search-path-markup的script,
也是用Y.one来拿:
另一个实践
在商品详情页上,我们把很多不需要首屏渲染的 html 代码放入了类似于
1 |
<textarea id="goodsAll_info" style="display:none;"></textarea> |
的隐藏 textarea 里了。
然后在 html 文档底部,放内联 js 来读取:
1 2 3 4 5 6 |
<script type="text/javascript"> $(function(){ var area = document.getElementById("goodsAll_info").value; document.getElementById("goodsAll_info_div").innerHTML = area; }); </script> |
推荐工具:
- 推荐使用 http://www.webpagetest.org/ 评测,由于它受限于并发测试和带宽,所以资源下载速度较差,只能作为与竞争对手对比测试的依据;
- Test Location 请选择亚洲的中国江苏节点;
- Browser 请选择 Firefox、IE9、Chrome等现代浏览器;
运维部的每周博睿检测数据报告,我们以博睿的数据为准; 博睿从它的各地监测节点以及不同电信链路访问,得到一个响应速度的平均值; Google PageSpeed https://developers.google.com/speed/pagespeed/insights 的 Critical Path Explorer;
参考:
http://www.cnblogs.com/zhengyun_ustc/archive/2013/05/09/frontendoptimize.html