掙脫瀏覽器的束縛(3) - 兩個連接還不夠“并行”
在討論這次的主題之前,我們現在看一下腳本
優化的另一個問題,就是“優化難度”。在這里我所說的“優化難度”是指優化一張頁面時的修改難度。例如在前一片文章中,使用document.write
來引入腳本的話,其“優化難度”會非常的低——沒有任何副作用,不用修改其它任何代碼。不過它的效果似乎還不太理想,因為僅僅優化了IE下的體驗,在
FireFox里卻沒有任何作用。
很可惜,我回想了幾乎所有的優化方式,再也
沒有找到優化難度如此低的做法了。對于其它的方式,我們都必須在頁面的別處進行修改,優化效果越好,修改量越大。對于這些優化方式,我們就必須編寫合適的
組件,將一些邏輯封裝起來。這樣可以在一定程度上方便使用,降低優化難度。
比較document.write與defer
那么這又何document.write或者defer有什么關系?且聽我慢慢道來。
<script />的defer屬性在標準里的定義是這樣的:
When set, this
boolean attribute provides a hint to the user agent that the script is
not going to generate any document content (e.g., no "document.write"
in javascript) and thus, the user agent can continue parsing and
rendering.
我們當時遇到JS無法并行下載的原因就是瀏
覽器認為在腳本中可能會輸出HTML內容。defer屬性的作用就是告訴瀏覽器,腳本里不會輸出任何信息。果然,當我們在IE里使用defer屬性時,腳
本沒有被阻塞,其效果和document.write一樣。不過在FireFox里依舊不行,這樣的實現實在讓人費解。
都說FireFox標準,看來在細節上也不盡然。
那么為什么我們在之前使用了document.write而不是defer屬性呢?兩者效果相同,但是明顯使用defer屬性更加直觀啊。
defer屬性使用起來的確直觀和方便。不過,效果真的相同嗎?我們可以通過以下的例子試試看。
document.write < html xmlns ="http://www.w3.org/1999/xhtml" > < head runat ="server" > < 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>'); </ script > </ head > < body > < input type ="button" value ="Click" /> < script type ="text/javascript" language ="javascript" src ="Scripts.ashx?a" > alert ('Hello World'); </ script > </ body > </ html >
然后再使用<script
defer="defer"></script>的方式引入一下。打開兩個頁面進行比較就會發現,如果使用
document.write的話,在腳本加載完畢之前按鈕不會顯示,也不會出現提示框;而如果使用defer屬性的話,按鈕就立即出現了,也會馬上出現
提示。
這可麻煩了。如果頁面上的元素過早出現,用
戶在腳本加載完之前進行操作是否會有問題?如果頁面里存在直接執行的腳本(如上例的alert調用),在腳本文件加載完之前是否能夠執行?如果上面兩個問
題的答案有任何一個是肯定的話,那么恭喜您,使用defer屬性就會造成錯誤了。而且這個問題的解決方案實在不太容易找到,這大大增加了“優化難度”。
而且更為關鍵的是,FireFox同樣不支持defer屬性的效果。這直接導致了defer屬性全面落后于使用document.write的優化方式。既然這樣,我們為什么要用它?事實上defer屬性用的實在不多,這是個非常典型的的“雞肋” 特性。
那么,哪里有使用defer屬性的應用呢?我想應該是有的吧,雖然我不知道。
突破兩個連接的限制
在上一片文章里我們可以看到,雖然
document.write方法可以讓腳本文件并行加載,但是它依舊受到瀏覽器的限制。根據HTTP協議的標準,對于同一個Domain,只能同時存在
兩個連接。在這點上,親愛的瀏覽器們都乖乖的實現了。我們如果想要突破這種限制,就要增加域名。不過其實瀏覽器判斷域名的方式是非常嚴格的,同一域名下的
子域名,同一域名不同端口,都不算相同。一般來說,使用子域名來增加并行加載的連接數是比較常用的做法。
應該已經有不少朋友知道這個方法,它的應用
實在太普遍了。不過請注意,請求任意資源時都會建立連接,瀏覽器對于某一域名的連接并不區分其作用。因此,無論下載圖片,CSS文件,
JavaScript文件,或者是XMLHttpRequest對象建立的AJAX連接,都屬于“兩個連接”之內,在優化時往往需要注意這一點。另外,一
個瀏覽器里同時建立的連接數也不是越多越好,根據實驗資料顯示,瀏覽器可以同時建立6到7連接最為合適。因此,我們使用3到4個子域名是比較妥當的。
我們現在就來看一下使用效果。在開發時要出現這個效果,我們可以修改C:\WINDOWS\system32\drivers\etc\hosts文件來設置本地的
DNS 映射。如下:
在Hosts文件里添加如下映射 127.0.0.1 [url]www.test.com[/url] 127.0.0.1 sub0.test.com 127.0.0.1 sub1.test.com 127.0.0.1 sub2.test.com 127.0.0.1 sub3.test.com 127.0.0.1 sub4.test.com 127.0.0.1 sub5.test.com
我們可以多加一些子域名,方便以后使用。
接下來我們就可以在頁面里從多個不同的子域名加載腳本文件,如下:
從不同子域名加載腳本文件 < html xmlns ="http://www.w3.org/1999/xhtml" > < head runat ="server" > < title > Untitled Page</ title > < script type ="text/javascript" language ="javascript" > document .write('<script type="text/javascript " language="javascript "' + ' src="[url]http://sub0.test.com/Scripts.ashx?a[/url] "><' + '/script>'); document .write('<script type="text/javascript " language="javascript "' + ' src="[url]http://sub0.test.com/Scripts.ashx?b[/url] "><' + '/script>'); document .write('<script type="text/javascript " language="javascript "' + ' src="[url]http://sub1.test.com/Scripts.ashx?c[/url] "><' + '/script>'); document .write('<script type="text/javascript " language="javascript "' + ' src="[url]http://sub1.test.com/Scripts.ashx?d[/url] "><' + '/script>'); document .write('<script type="text/javascript " language="javascript "' + ' src="[url]http://sub2.test.com/Scripts.ashx?e[/url] "><' + '/script>'); </ script > </ head > < body > ...</ body > </ html >
在瀏覽器打開頁面試試看?還記得當初我們加載頁面用了多少時間嗎?8秒多!而現在已經能夠在不到2秒內加載完畢了(如圖2)。
圖8:使用多個子域名進行并行加載
可惜我們還要優化FireFox瀏覽器里的情況,下次我們就來討論這個問題。接下來的優化方案會有一定的難度,不過只要我們利用得當,將會大大提高Perceived Performance。