新產品為了效果,做的比較炫,用了很多的圖片和JS,所以前端的性能是很大的問題,分篇記錄前端性能優化的一些小經驗。
第一篇:HTTP服務器
因tomcat處理靜態資源的速度比較慢,所以首先想到的就是把所有靜態資源(JS,CSS,image,swf)提到單獨的服務器,用更加快速的HTTP服務器,這里選擇了nginx 了,nginx相比apache,更加輕量級,配置更加簡單,而且nginx不僅僅是高性能的HTTP服務器,還是高性能的反向代理服務器。
目前很多大型網站都使用了nginx,新浪、網易、QQ等都使用了nginx,說明nginx的穩定性和性能還是非常不錯的。
1. nginx 安裝(linux)
http://nginx.org/en/download.html 下載最新穩定版本
根據自己需要的功能先下載對應模板,這里下載了下面幾個模塊:
openssl-0.9.8l,zlib-1.2.3,pcre-8.00
編譯安裝nginx:
./configure
--without-http_rewrite_module
--with-http_ssl_module
--with-openssl=../../lib/openssl-0.9.8l
--with-zlib=../../lib/zlib-1.2.3
--with-pcre=../../lib/pcre-8.00
--prefix=/usr/local/nginx
make
make installWEB性能測試工具主要分為三種,一種是測試頁面資源加載速度的,一種是測試頁面加載完畢后頁面呈現、JS操作速度的,還有一種是總體上對頁面進行評價分析,下面分別對這些工具進行介紹,如果誰有更好的工具也請一起分享下。
Firebug:
Firebug 是firefox中最為經典的開發工具,可以監控請求頭,響應頭,顯示資源加載瀑布圖:
HttpWatch :
httpwatch 功能類似firebug,可以監控請求頭,響應頭,顯示資源加載瀑布圖。但是httpwatch還能顯示GZIP壓縮信息,DNS查詢,TCP鏈接信息,個人在監控http請求比較喜歡使用httpwatch,httpwatch包含IE和firefox插件。不過httpwatch專業版本是收費的,免費版本有些功能限制。
DynaTrace's Ajax Edition:dynaTrace 是本人常使用的1個免費工具,該工具不但可以檢測資源加載瀑布圖,而且還能監控頁面呈現時間,CPU花銷,JS分析和執行時間,CSS解析時間的等。
Speed Tracer:
speed trace 是google chrome的1個插件,speed trace的優勢點是用于監控JS的解析執行時間,還可以監控頁面的重繪、回流,這個還是很強的(dynaTrace也能有這個功能)。 注:安裝這個插件,需要安裝 Google Chrome Developer Channel 版本,但是這個鏈接的地址在國內好像打不開,如果打不開,請大家直接到這個地址去下載:
http://www.google.com/chrome/eula.html?extra=devchannel
Page Speed :
Page speed 是基于firebug的1個工具,主要可以對頁面進行評分,總分100分,而且會顯示對各項的改進意見,Page Speed也能檢測到JS的解析時間。
yslow :
yslow跟pge speed一樣是基于 firefox/firebug的插件,功能與page speed類似,對各種影響網站性能的因素進行評分,yslow是yahoo的工具,本人也一直在使用,推薦一下。
webpagetest :
webpagetest 是1個在線進行性能測試的網站,在該網站輸入你的url,就會生成1個url加載的時間瀑布圖,對所有加載的資源(css,js,image等等)列出優化的清單,也是非常好用的工具。
縮小圖片大小
當圖片很多的時候,減少圖片大小是提高下載速度最直接的方法。
1. 使用PNG8代替GIF(非動畫圖片),因為PNG8在效果一樣的情況,圖片大小比GIF要小。2. 用fireworks處理PNG圖片,在我們產品中很多PNG圖片是美工直接用photoshop導出的,后來讓美工用fireworks處理PNG(大概的方式是選擇保存為PNG8,刪除背景色)。處理后100K的圖片大小基本減少了3/4,但圖片質量也會有少許降低,要看自己是否能接受。
3. 使用Smush.it(http://www.smushit.com/ysmush.it/) 壓縮圖片,Smush.it是YUI團隊做1個在線壓縮圖片的網站,該網站在不影響原圖片的質量下去掉圖片中一些元數據,所以可以放心使用該網站進行壓縮,但這個壓縮比例也是比較有限的。
合并圖片和拆分圖片
1. CSS Sprites合并圖片以減少請求數來提高性能大家都知道。但不要把圖片合并太多,太多太大了,就會因為這1個圖片影響這個頁面的顯示了。
2. 有時候我們需要把1個大圖片拆分成多個小圖片,比如產品首頁圖片比較少,就1個很大的banner圖片,因瀏覽器都可以并發下載圖片,所以如果不拆分,只使用1個大圖片的話,下載速度反而會比較慢。
透明圖片處理
IE6不能顯示透明的PNG圖片,是很多開發人員特別頭疼的事,分別介紹下幾種方式的優缺點。
1.使用AlphaImageLoader,IE6支持filter,使用下面的CSS代碼,可以讓IE6支持PNG
#some-element {
background: url(image.png);
_background: none;
_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='image.png',
sizingMethod='crop');
}
外部JS的阻塞下載
所有瀏覽器在下載JS的時候,會阻止一切其他活動,比如其他資源的下載,內容的呈現等等。至到JS下載、解析、執行完畢后才開始繼續并行下載其他資源并呈現內容。
有人會問:為什么JS不能像CSS、image一樣并行下載了?這里需要簡單介紹一下瀏覽器構造頁面的原理,當瀏覽器從服務器接收到了HTML文檔,并把HTML在內存中轉換成DOM樹,在轉換的過程中如果發現某個節點(node)上引用了CSS或者 IMAGE,就會再發1個request去請求CSS或image,然后繼續執行下面的轉換,而不需要等待request的返回,當request返回后,只需要把返回的內容放入到DOM樹中對應的位置就OK。但當引用了JS的時候,瀏覽器發送1個js request就會一直等待該request的返回。因為瀏覽器需要1個穩定的DOM樹結構,而JS中很有可能有代碼直接改變了DOM樹結構,比如使用 document.write 或 appendChild,甚至是直接使用的location.href進行跳轉,瀏覽器為了防止出現JS修改DOM樹,需要重新構建DOM樹的情況,所以就會阻塞其他的下載和呈現。
阻塞下載圖:下圖是訪問blogJava首頁的時間瀑布圖,可以看出來開始的2個image都是并行下載的,而后面的2個JS都是阻塞下載的(1個1個下載)。
嵌入JS的阻塞下載
嵌入JS是指直接寫在HTML文檔中的JS代碼。上面說了引用外部的JS會阻塞其后的資源下載和其后的內容呈現,哪嵌入的JS又會是怎樣阻塞的了,看下面的列2個代碼:
代碼
<div>
<ul>
<li>blogJava<span style="color: #800000;"/></li>
<li>CSDN<span style="color: #800000;"/></li>
<li>博客園<span style="color: #800000;"/></li>
<li>ABC<span style="color: #800000;"/></li>
<li>AAA<span style="color: #800000;"/></li>
</ul>
</div>
<script type="text/Javascript">
// 循環5秒鐘
{ var n = Number(new Date());
var n2 = Number(new Date());
while((n2 - n) (6*1000)){
n2 = Number(new Date());
}
</script>
<div>
<ul>
<li>MSN</li>
<li>GOOGLE</li>
<li>YAHOO</li>
</ul>
</div>http請求頭的數據量
我們先分析下請求頭,看看每次請求都帶了那些額外的數據.下面是監控的google的請求頭
代碼
Host www.google.com.hk
User-Agent Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.9.2.3) Gecko/20100401
Firefox/3.6.3 GTBDFff GTB7.0
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language zh-cn,en-us;q=0.7,en;q=0.3
Accept-Encoding gzip,deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 115
Proxy-Connection keep-alive
上一篇中我們說到了 如何減少請求數,這次說說如何減少請求、響應的數據量(即在網絡中傳輸的數據量),減少傳輸的數據量不僅僅可以加快頁面加載速度,更可以節約服務器帶寬,為你剩不少錢(好像很多機房托管都是按流量算錢的)。
GZIP壓縮
gzip是目前所有瀏覽器都支持的一種壓縮格式,IE6需要SP1及以上才支持(別說你還在用IE5,~_~)。gzip可以說是最方便而且也是最大減少響應數據量的1種方法。
說它方便,是因為你不需要為它寫任何額外的代碼,只需要在http服務器上加上配置都行了,現在主流的http 服務器都支持gzip,各種服務器的配置這里就不一一介紹(其實是我不知道怎么配),
nginx的配置可以參考我這篇文章:www.blogJava.NET/BearRui/archive/2010/01/29/web_performance_server.html
我們先看看gzip的壓縮比率能達到多少,這里用jquery 1.4.2的min和src2個版本進行測試,使用nginx服務器,gzip壓縮級別使用的是:
注意看上圖的紅色部分,jquery src文件在啟用gzip后大小減少了70%
這張圖片可以看出就算是已經壓縮過min.js在啟用gzip后大小也減少了65%。
別對圖片啟用gzip
在知道了gzip強大的壓縮能力后,你是否想對服務器上的所有文件啟用gzip了,先讓我們看看圖片中啟用gzip后會是什么情況。
hoho,1個gif圖片經過gzip壓縮后反而變大了???這是因為圖片本來就是一種壓縮格式,gzip不能再進行壓縮,反而會添加1額外的頭部信息,所以圖片會變大。
在測試過程中,發現jpg的圖片經過gzip壓縮后會變小,不知道為何,可能跟圖片壓縮方式有關。不過壓縮比率也比較小,所以就算是jpg,建議也不要開啟gzip壓縮。
比較適合啟用gzip壓縮的文件有如下這些:
1. Javascript
2. CSS
3. HTML,xml
4. plain text
別亂用cookie
現在幾乎沒有哪個網站不使用cookie了,可是該怎么使用cookie比較合適了,cookie有幾個重要的屬性:path(路徑),domain(域),expires(過期時間)。瀏覽器就是根據這3個屬性來判斷在發送請求的時候是否需要帶上這個cookie。cookie使用最好的方式,就是當請求的資源需要cookie的時候才帶上該cookie。其他任何請求都不帶上cookie。但事實上很多人在使用 cookie的時候已經習慣性的設置成:path=/ domain=.domain.com。這樣的結果就是不管任何請求都會帶上cookie,就算你是請求的圖片(img.domain.com)、靜態資源服務器(res.domain.com)這些根本不需要cookie的資源,瀏覽器照樣會帶上這些沒用的cookie。咱們一起來看現實中的1個列子,博客園(www.cnblogs.com):
先看看博客園的cookie是怎么設置的,下面是firefox查看博客園cookie的截圖:
cnblogs總共有5個cookie值,而且全部設置都是 path=/ domain=.cnblogs.com。知道了cookie的設置后,我們再來監控下博客園首頁的請求,監控的統計信息如下:
總請求數:39(其中圖片22個,JS7個,css2個)。
其中js、css、image 主要來自3個靜態資源服務器: common.cnblogs.com , pic.cnblogs.com ,static.cnblogs.com
再看其中1個請求圖片(http://static.cnblogs.com/images/a4/banner_job.gif)的請求頭:
Host static.cnblogs.com
User-Agent Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.9.2.3) Gecko/20100401
Firefox/3.6.3 GTBDFff GTB7.0
Accept image/png,image/*;q=0.8,*/*;q=0.5
Accept-Language zh-cn,en-us;q=0.7,en;q=0.3
Accept-Encoding gzip,deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 115
Proxy-Connection keep-alive
Referer http://www.cnblogs.com/
Cookie __gads=ID=a15d7cb5c3413e56:T=1272278620:S=ALNI_MZNMr6_d_PCjgkJNJeEQXkmZ3bxTQ;
__utma=226521935.1697566422.1272278366.1272278366.1272278366.1;
__utmb=226521935.2.10.1272278366; __utmc=226521935;
__utmz=226521935.1272278367.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)
本篇文章主要討論下目前JS,CSS 合并、壓縮、緩存管理存在的一些問題,然后分享下自己項目中用到的1個處理方案,并提供1個實例下載。
存在的問題:合并、壓縮文件主要有2方面的問題:
1. 每次發布的時候需要運行一下自己寫的bat文件或者其他程序把文件按照自己的配置合并和壓縮。
2. 因生產環境和開發環境需要加載的文件不一樣,生產環境為了需要加載合并、壓縮后的文件,而開發環境為了修改、調試方便,需要加載非合并、壓縮的文件,所以我們常常需要在JSP中類似與下面的判斷代碼:
代碼
<c:if test="${env=='prod'}">
<script type="text/Javascript" src="/js/all.js"></script>
</c:if>
<c:if test="${env=='dev'}">
<script type="text/Javascript" src="/js/1.js"></script>
<script type="text/Javascript" src="/js/2.js"></script>
<script type="text/Javascript" src="/js/3.js"></script>
</c:if>頁面呈現流程
在討論頁面重繪、回流之前。需要對頁面的呈現流程有些了解,頁面是怎么把html結合css等顯示到瀏覽器上的,下面的流程圖顯示了瀏覽器對頁面的呈現的處理流程??赡懿煌臑g覽器略微會有些不同。但基本上都是類似的。
1. 瀏覽器把獲取到的html代碼解析成1個Dom樹,html中的每個tag都是Dom樹中的1個節點,根節點就是我們常用的document對象 (<html> tag)。dom樹就是我們用firebug或者IE Developer Toolbar等工具看到的html結構,里面包含了所有的html tag,包括display:none隱藏,還有用JS動態添加的元素等。
2. 瀏覽器把所有樣式(主要包括css和瀏覽器的樣式設置)解析成樣式結構體,在解析的過程中會去掉瀏覽器不能識別的樣式,比如IE會去掉-moz開頭的樣式,而firefox會去掉_開頭的樣式。
3、dom tree和樣式結構體結合后構建呈現樹(render tree),render tree有點類似于dom tree,但其實區別有很大,render tree能識別樣式,render tree中每個node都有自己的style,而且render tree不包含隱藏的節點(比如display:none的節點,還有head節點),因為這些節點不會用于呈現,而且不會影響呈現的,所以就不會包含到 render tree中。注意 visibility:hidden隱藏的元素還是會包含到render tree中的,因為visibility:hidden 會影響布局(layout),會占有空間。根據css2的標準,render tree中的每個節點都稱為box(Box dimensions),box所有屬性:width,height,margin,padding,left,top,border等。
4. 一旦render tree構建完畢后,瀏覽器就可以根據render tree來繪制頁面了。
回流與重繪
1. 當render tree中的一部分(或全部)因為元素的規模尺寸,布局,隱藏等改變而需要重新構建。這就稱為回流(其實我覺得叫重新布局更簡單明了些)。每個頁面至少需要一次回流,就是在頁面第一次加載的時候。
2. 當render tree中的一些元素需要更新屬性,而這些屬性只是影響元素的外觀,風格,而不會影響布局的,比如background-color。則就叫稱為重繪。
注:從上面可以看出,回流必將引起重繪,而重繪不一定會引起回流。
什么操作會引起重繪、回流
其實任何對render tree中元素的操作都會引起回流或者重繪,比如:
1. 添加、刪除元素(回流+重繪)
2. 隱藏元素,display:none(回流+重繪),visibility:hidden(只重繪,不回流)
3. 移動元素,比如改變top,left(jquery的animate方法就是,改變top,left不一定會影響回流),或者移動元素到另外1個父元素中。(重繪+回流)
4. 對style的操作(對不同的屬性操作,影響不一樣)
5. 還有一種是用戶的操作,比如改變瀏覽器大小,改變瀏覽器的字體大小等(回流+重繪)
讓我們看看下面的代碼是如何影響回流和重繪的:
代碼
var s = document.body.style;
s.padding = "2px"; // 回流+重繪
s.border = "1px solid red"; // 再一次 回流+重繪
s.color = "blue"; // 再一次重繪
s.backgroundColor = "#ccc"; // 再一次 重繪
s.fontSize = "14px"; // 再一次 回流+重繪
// 添加node,再一次 回流+重繪
document.body.appendChild(document.createTextNode('abc!'));
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。