前言
網站設計的優化是一個很大的話題,有一些通用的原則,也有針對不同開發平台的一些建議。這方面的研究一直沒有停止過,我在不同的場合也分享過這樣的話題。
作為通用的原則,雅虎的工程師團隊曾經給出過35個最佳實踐。這個列表請參考
Best Practices for Speeding Up Your Web Site http://developer.yahoo.com/performance/rules.html
同時,他們還發布了一個相應的測試工具Yslow http://developer.yahoo.com/yslow/
我強烈推薦所有的網站開發人員都應該學習這些最佳實踐,並結合自己的實際項目情況進行應用。
接下來的一段時間,我將結合ASP.NET這個開發平台,針對這些原則,通過一個系列文章的形式,做些講解和演繹,以幫助大家更好地理解這些原則,並且更好地使用他們。
准備工作
為了跟隨我進行后續的學習,你需要准備如下的開發環境和工具
- Google Chrome 或者firefox ,並且安裝 Yslow這個擴展組件.請注意,這個組件是雅虎提供的,但目前沒有針對IE的版本。
- https://chrome.google.com/webstore/detail/yslow/ninejjcohidippngpapiilnmkgllmakh
Technorati Tags: Performance,Web design,ASP.NET
- https://addons.mozilla.org/en-US/firefox/addon/yslow/
- 你應該對這些瀏覽器的開發人員工具有所了解,你可以通過按下F12鍵調出這個工具。
- https://chrome.google.com/webstore/detail/yslow/ninejjcohidippngpapiilnmkgllmakh
- Visaul Studio 2010 SP1 或更高版本,推薦使用Visual Studio 2012
- 你需要對ASP.NET的開發基本流程和核心技術有相當的了解,本系列文章很難對基礎知識做普及。
本文要討論的話題
這一篇我來和大家討論第六個原則:Put Scripts at Bottom (在文檔底部放置腳本定義或引用)。
我在上一篇和大家探討到了優化網站設計(五):在頂部放置樣式定義 ,那是一個給我留下深刻印象的原則,之所以深刻的原因並不是因為這個原則定義有多深刻,而在於以前都這么多,但並沒有意識到這是一個不錯的做法。
那么,這一篇所談到的“在文檔底部放置腳本定義或引用”這個原則,則是在相當長一段時間內,我都沒有注意到的一條原則。換句話說,我以前更多的是將腳本或腳本引用放在HEAD里面,例如下面這樣:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1.Default" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> <script src="Scripts/jquery-2.0.0.min.js"></script> <script src="Scripts/knockout-2.2.1.js"></script> <script src="Scripts/modernizr-2.6.2.js"></script> <script src="default.js"></script> </head> <body> <form id="form1" runat="server"> <div> </div> </form> </body> </html>
相當長一段時間里,我都沒有意識到這可能會成為一個問題。后來也是在研究有關性能有關的問題時,我逐漸注意到了它,原來將腳本定義或引用放在文檔底部有助於提高頁面加載時的並行度,從而實現提速的目的。
HTTP /1.1 文檔中有下面這一段描述:
Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy. A proxy SHOULD use up to 2*N connections to another server or proxy, where N is the number of simultaneously active users. These guidelines are intended to improve HTTP response times and avoid congestion
我們知道HTTP本身是無狀態的,這意味着每個請求都需要建立獨立的連接。但從HTTP /1.1開始對此作了改進,允許在客戶端和服務器之間維持連接(Keep-Alive),這樣做的目的是因為在頁面加載的時候,往往需要加載很多附加的資源(例如腳本,樣式表,圖片等),如果每個請求都需要建立連接,顯然是不合適的。維持連接(Keep-Alive)的特性,使得連接可以重用,即一段時間內,對同一域的訪問請求可以不需要創建新的連接,而是使用之前的連接。
但這里有一個問題,如果維持過多的連接,顯然對於客戶端和服務器來說都不是好事(這需要耗用資源),所以HTTP /1.1 中建議針對同一個域不要維護超過2個連接。實際上這就是限制了並行度。雖然看起來我們可以通過將這些腳本放在不同的域,來繞開這個並行的問題,但要命的是,只要瀏覽器開始下載腳本文件,那么它就不可能使用超過2個連接進行同時工作,哪怕這些資源是放在不同的域的。
但據我所知,現代的瀏覽器對此做了一些突破(可能不止2個連接),但無論如何,這總是有限的。有一些方法可以手工修改瀏覽器的設置,但我不建議這么做。
如果腳本文件放在HEAD里面,而且如果腳本很多的話,那么下載這些腳本文件就將占用本來就不多的幾個連接,那么就必須等這些腳本全部下載完之后,才有可能下載頁面其他的部分。這可能是造成頁面停滯的一個用戶體驗。
那么,我們可以怎么使用這個原則呢?很簡單,修改頁面代碼如下
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1.Default" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> </div> </form> <!--將腳本移動到文檔的底部有助於提高頁面加載速度--> <script src="Scripts/jquery-2.0.0.min.js"></script> <script src="Scripts/knockout-2.2.1.js"></script> <script src="Scripts/modernizr-2.6.2.js"></script> <script src="default.js"></script> </body> </html>
有意思的是,雖然這樣的移動並不難,但真的有人不願意做這樣的改變(有一部分理由是因為這樣做了之后,文檔結構不夠清晰了)。作為妥協的一種方案,現在允許在添加腳本的時候,設置一個DEFER屬性,標識這個腳本可以延遲加載。
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1.Default" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> <!--使用defer屬性延遲加載腳本--> <script src="Scripts/jquery-2.0.0.min.js" defer="defer"></script> <script src="Scripts/knockout-2.2.1.js" defer="defer"></script> <script src="Scripts/modernizr-2.6.2.js" defer="defer"></script> <script src="default.js" defer="defer"></script> </head> <body> <form id="form1" runat="server"> <div> </div> </form> </body> </html>
看起來是不錯的,但不幸的是,並非所有瀏覽器都支持這個特性。網絡開發的復雜性就在於此,我幾乎看到你在微笑着點頭了。那么,為什么不可以接受將腳本放在文檔底部的這一點穩妥的做法呢?
值得一說的是,並不是所有代碼都適合從頂部移動到文檔底部。例如這些腳本需要在加載過程中動態添加HTML的元素(可能會調用document.write方法),那么就會存在問題。看起來這是不少人固守着原先做法的原理,如果你沒有用過類似JQuery這一類框架的話,你是情有可原的,反過來說你就不應該這么想了。而如果你真的沒有聽過和用過JQuery,那么你就離大部隊越來越遠了。
其實,我們在實踐中逐漸發現,絕大部分情況下,我們使用Javascript的時候,都應該等到頁面已經准備好之后才開始工作,因為如果你在頁面都沒有全部呈現出來(並不是代表用戶看到,而是呈現)之前就開始操作,你將無法預知會發生什么事情。JQuery創造性地引入了document.ready這樣一個事件,而我可以負責任地說,只要你的腳本是在document.ready之后才應該執行的,那么就都可以移動到文檔底部。
