現在哪裡還找得到不引入JavaScript指令碼檔案的Web應用?使用指令碼檔案的好處多多,其中最重要的可能就是提供快取能力了。使用指令碼檔案之後再加上快取,可以大大降低資料傳輸量,提高頁面開啟的速度。不過指令碼檔案的引入也不是簡單得不值一提,我們完全有能力來優化它。
小心傳統的指令碼引入方式帶來的效能問題
現在的Web應用所需的指令碼越來越多,一張頁面下載幾百K的指令碼也不再是難以想象的事情了,這就直接導致頁面需要更長的時間來載入指令碼。不過傳統的指令碼引入方式(使用<script />)會造成什麼問題?再檢視這點之前,我們先寫一個HttpHandler來模擬一個需要較長時間才能載入的指令碼。這很簡單,我們只要建立一個Http Handler來做到這一點,如下:
public class Scripts : IHttpHandler { public void ProcessRequest (HttpContext context) { context.Response.ContentType = "application/x-javascript"; System.Threading.Thread.Sleep(1500); context.Response.Write("//"); } public bool IsReusable { get { return false; } } }
我使用Thread.Sleep函式使執行緒休眠1.5秒,然後輸出一個註釋符。這樣就保證了頁面載入該檔案需要比較長的時間,也可以將指令碼的執行時間降到最低。
然後我們就寫個最簡單的頁面,來測試一下載入這些檔案的結果:
<html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server" id="aaa"> <title>Untitled Page</title> <script type="text/javascript" language="javascript" src="Scripts.ashx?a"></script> <script type="text/javascript" language="javascript" src="Scripts.ashx?b"></script> <script type="text/javascript" language="javascript" src="Scripts.ashx?c"></script> <script type="text/javascript" language="javascript" src="Scripts.ashx?d"></script> <script type="text/javascript" language="javascript" src="Scripts.ashx?e"></script> </head> <body> ... </body> </html>
在IE裡開啟頁面,看看這些指令碼載入的情況。請注意,您可以使用IE Dev Toolbar來禁用Cache(圖5)。
圖5:IE中傳統方式載入指令碼的情況
真可謂是相當的整齊。不過整齊的背後是較低的效能:指令碼檔案一個一個被載入,所有指令碼檔案被載入完需要用8秒多時間。
那麼FireFox的表現又如何?我們使用同樣的頁面來測試一下(圖6)。
圖6:FireFox中傳統方式載入指令碼的情況
嘿,情況差不多。
其實出現這個狀況是By Design的。從上面這個簡單的例子裡可能還無法看出,事實上,當瀏覽器遇到<script />標籤時,它會開始載入指令碼檔案,而此時頁面的其它載入行為則會全部停止,包括HTML的呈現,頁面或圖片的下載等等。這是因為瀏覽器“懷疑”這些指令碼檔案中的一些行為可能會再頁面中輸出HTML。自然,我們可以使用document.write方法這麼做。而很多可以放在網站中的第三方小部件,都是靠指令碼檔案裡的document.write方法來生成HTML的。
這就讓使用者不太好受了。為什麼我的瀏覽器只能建立一個連線?為什麼不能一起下載?我們的頻寬不是浪費了很多嗎?這些都沒錯。還記得前一段時間臺灣地震使一些Blog無法開啟或者開啟很慢嗎?這很可能就是在頁面中使用<script />引入指令碼檔案時造成的問題:檔案下載特別慢,甚至會超時。而且當時我的blog也遇到這個問題。解決方案很簡單,把<script />去掉便是。或者,您可以將<script />元素放置在“頁尾”程式碼中,這樣,頁面就會開啟地比較快了——不過當然,那個檔案很可能還在繼續載入指令碼中。
這就是提高了所謂的“感知效能(Perceived Performance)”,簡單的說,就是使用者“感受”到的效能。使用者會發現頁面已經開啟了,雖然還沒有完全載入完,例如Snap Preview還無法工作。
嘗試打破傳統指令碼引入的瓶頸
現在的指令碼越做越大了,一個200K的檔案,如果以20K每秒的速度下載也要10秒。如果這十秒結束之後又來個十秒……這樣的網頁載入速度太可怕了。我們必須嘗試著打破這個瓶頸。
很有趣的是,如果您在頁面中使用document.write來寫一個<script />元素的話,這些指令碼就可以並行下載了。我們就用下面的程式碼進行嘗試吧:
<html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server" id="aaa"> <title>Untitled Page</title> <script type="text/javascript" language="javascript"> document.write( '<script type="text/javascript" language="javascript"' + ' src="Scripts.ashx?a"><' + '/script>'); document.write( '<script type="text/javascript" language="javascript"' + ' src="Scripts.ashx?b"><' + '/script>'); document.write( '<script type="text/javascript" language="javascript"' + ' src="Scripts.ashx?c"><' + '/script>'); document.write( '<script type="text/javascript" language="javascript"' + ' src="Scripts.ashx?d"><' + '/script>'); document.write( '<script type="text/javascript" language="javascript"' + ' src="Scripts.ashx?e"><' + '/script>'); </script> </head> <body> ... </body> </html>
這樣的做法似乎有些複雜,不過應該還算直觀。上面程式碼的目的就是在頁面中“寫入”<script />元素,以達到引入指令碼檔案的目的。還是用事實說話,先來看一下IE中開啟頁面的效果吧(圖6):
圖7:IE中使用document.write載入指令碼的情況
狀況好多了。可以看出總是有兩個指令碼檔案在同時下載,雖然還是受制於瀏覽器對於每個Domain只有2個連線的限制,但是頁面載入時間已經從8秒多銳減到不到5秒了。這實在是一個絕好的訊息。那麼再公佈一個好訊息,使用這種方式引入指令碼檔案的話,指令碼檔案的執行順序與指令碼檔案出現的順序相同。我們只要安排好指令碼檔案的順序,這樣就可以保證指令碼執行的正確性了。
嘿嘿,不管怎麼說這個方法還是非常容易使用的,不是嗎?那麼讓我們歡呼雀躍吧,因為優化就是這麼簡單!
很可惜事情的發展並不如我們想象的那麼單純。我們還沒有試過FireFox下的狀況呢。看了FireFox載入頁面的資料統計圖,可能就會知道,我們離目標還有很大的距離——因為它的狀況和圖6的顯示狀況完全相同,document.write這種做法在FireFox裡沒有起到任何作用。
為什麼IE的表現和FireFox的表現不同呢?可能這就要問一下瀏覽器的開發者了,我們現在要做的,可能只是根據結果來為我們的應用想出更好的解決方案。
路漫漫其修遠兮。