技巧 10:使用顯式選項
在 .asp 檔案中應使用 Option Explicit。此指令放在 .asp 檔案的最上面,它強制開發人員聲明要使用到的所有變數。許多程式員認為這種方法對於調試應用程式很有協助,因為這種方法避免了鍵錯變數名和誤建新變數的可能性(例如,將 MyXMLString=) 錯寫成 MyXLMString=...。
更重要的一點也許是,聲明的變數比未聲明的變數速度更快。由此,指令碼在運行時每次用到未聲明的變數時,按名稱引用它。另一方面,聲明的變數是有順序的,要麼以編譯時間,要麼以已耗用時間。以後,聲明的變數都按此順序引用。因為 Option Explicit 強制變數聲明,它能確保聲明所有變數,因此訪問的速度也很快。
技巧 11:在子常式和函數中使用局部變數
局部變數是那些在子常式和函數內聲明的變數。在函數或子常式內,局部變數訪問比全域變數訪問更快。局部變數的使用也會使代碼更清晰,因此應盡量使用局部變數。
技巧 12:將經常使用的資料複製到指令碼變數中
當訪問 ASP 中的 COM 物件時,應將經常使用的對象資料複製到指令碼變數中。這樣做可減少 COM 方法調用,因為 COM 方法調用與訪問指令碼變數相比,開銷相對較大。當訪問 Collection 和 Dictionary 對象時,這種技術也會減少開銷很大的尋找。
一般來說,如果您打算不止一次訪問對象資料,那麼就應將資料放到指令碼變數中。這種最佳化的主要目標是 Request 變數(Form 和 QueryString 變數)。例如,您的網站可傳遞一個名為 UserID 的 QueryString 變數。假設此 UserID 在特定頁面上被引用 12 次。可以無須調用 Request(?UserID?) 12 次,而是在 ASP 頁面最上面將 UserID 指派到一個變數。然後在該頁面自始至終使用該變數。這樣就省去了 11 次 COM 方法調用。
實際上,訪問 COM 屬性或方法的開銷並沒有那麼大。下面舉一個例子,說明某相當常見的代碼(從文法上講):
Foo.bar.blah.baz = Foo.bar.blah.qaz(1)
If Foo.bar.blah.zaq = Foo.bar.blah.abc Then ' ...
當此代碼運行時,下面是發生的情況:
變數 Foo 被解析為全域對象。
變數 bar 被解析為 Foo 的成員。這實際就是一次 COM 方法調用。
變數 blah 被解析為 Foo.bar 的成員。這又是一次 COM 方法調用。
變數 qaz 被解析為 foo.bar.blah 的成員。沒有錯,這還是一次 COM 方法調用。
調用 Foo.bar.blah.quaz(1)。再一次 COM 方法調用。懂了嗎?
再次執行步驟 1 至步驟 3 以解析 baz。系統並不知道調用 qaz 是否改變物件模型,因此必須再次執行步驟 1 至 3 以解析 baz。
將 baz 解析為 Foo.bar.blah 的成員。賦予屬性。
再次執行步驟 1 至步驟 3 以解析 zaq。
再次執行步驟 1 至步驟 3 以解析 abc。
正如您可看到的,效率相當差(且慢)。以 VBScript 寫此代碼的快速方法是:
Set myobj = Foo.bar.blah ' do the resolution of blah ONCE
Myobj.baz = myobj.qaz(1)
If Myobj.zaq = Myobj.abc Then '...
如果您使用 VBScript 5.0 或更高版本,您可以使用 With 語句寫此代碼:
With Foo.bar.blah
.baz = .qaz(1)
If .zaq = .abc Then '...
...
End With
注意此技巧也適用於 VB 程式設計。
技巧 13:避免重新確定數組的維數
應盡量避免 Redim 數組。就效能而言,如果電腦的實體記憶體大小有限,最好將數組的初始維數設定為其最不利的情況 - 或將維數設定為其最佳的情況,然後再按需要重新確定維數。這並非意味著,如果知道您不需要記憶體時,就隨便分配幾MB的記憶體。
下面的代碼給您顯示使用 Dim 和 Redim 不當的情形。
<%
Dim MyArray()
Redim MyArray(2)
MyArray(0) = ?hello?
MyArray(1) = ?good-bye?
MyArray(2) = ?farewell?
...
' some other code where you end up needing more space happens, then ...
Redim Preserve MyArray(5)
MyArray(3) = ?more stuff?
MyArray(4) = ?even more stuff?
MyArray(5) = ?yet more stuff?
%>
最好一開始就將數組的初始大小 Dim 正確(在本例中,是 5)比 Redim 數組使其更大好得多。您可能浪費一些記憶體(如果您沒有使用所有的元素),但獲得的好處是速度變得更快。
技巧 14:使用響應緩衝
您可以通過啟用“響應緩衝”,將要輸出的一整頁緩衝起來。這樣就將寫到瀏覽器的量減到最少,從而改善總體效能。每個寫操作都會產生很大的系統開銷(在 IIS 中以及在通過網路發送的資料量方面),因此寫操作越少越好。由於其啟動慢且使用 Nagling 演算法(用來減輕網路塞車情況),TCP/IP 在發送一些大的資料區塊時比必鬚髮送許多小的資料區塊時的效率高得多。
有兩個方法啟用響應緩衝。第一種,您可以使用 Internet Services Manager 為整個應用程式啟用響應緩衝。我們建議採用這種方法,在 IIS 4.0 和 IIS 5.0 中預設為新的 ASP 應用程式啟用響應緩衝。第二種,可以在每個 ASP 頁面的接近頂端的地方加入下面的程式碼,從而啟用響應緩衝:
<% Response.Buffer = True %>
此程式碼必須在任何響應資料被寫到瀏覽器之前執行(即,在任何 HTML 出現在 ASP 指令碼之前以及在使用 Response.Cookies 集合設定任何 Cookies 之前)。一般來說,最好為整個應用程式啟用響應緩衝。這樣,您就不必在每個頁面最上面寫入上述的程式碼。
Response.Flush
關於響應緩衝有一個常見的抱怨,就是使用者感覺到 ASP 頁面的響應速度很慢(即使整個回應時間得到改進),因為他們必須等到整個頁面產生,然後他們才能看到東西。對於已耗用時間長的頁面,您可以設定 Response.Buffer = False,禁用響應緩衝。但是,一個更好的策略是利用 Response.Flush 方法。這種方法將 ASP 轉換的所有 HTML 送到瀏覽器。例如,在轉換 1,000 行的表的前 100 行之後,ASP 可以調用 Response.Flush,強制將轉換的結果送到瀏覽器,這樣可使使用者在其餘的行準備好之前看到頭 100 行。這種技術可以將響應緩衝與瀏覽器逐漸顯示資料完美地結合在一起。
(注意在上面的 1,000 行表的舉例中,許多瀏覽器在它們看到關閉 </table> 標記之前不會開始顯示表。檢查您的目標瀏覽器是否支援。為避免這種情況,將表分成多個具有較少行的表,並在每個表之後調用 Response.Flush。較新版本的 Internet Explorer 在表完全下載之前就開始顯示表,如果您指定表列寬,顯示速度就會特別快,這樣做可避免強制 Internet Explorer 通過測量每個儲存格的內容寬度來計算資料行寬。)
另一個關於響應緩衝的常見的抱怨是,當產生非常大的頁面時,將佔用許多伺服器記憶體。撇開產生大頁面的方法不談,這種問題也可通過巧妙使用 Response.Flush 來加以解決。
技巧 15:批處理內嵌指令碼和 Response.Write 語句
VBScript 文法 <% = expression %> 將“expression”的值寫到 ASP 輸出資料流中。如果響應緩衝未啟用,那麼執行其中的每一條語句,都會以許多小的資料包通過網路將資料寫到瀏覽器中。這樣速度很慢。而且穿插執行少量的指令碼和 HTML,將引起指令碼引擎和 HTML 之間的切換,從而降低效能。因此,使用下面的技巧:使用 Response.Write 調用代替捆綁緊密的內嵌運算式。例如,在下面的樣本中,在每一行的每一欄位對響應流有一次寫操作,每一行在 VBScript 和 HTML 之間有許多切換:
<table>
<% For Each fld in rs.Fields %>
<th><% = fld.Name %></th>
<%
Next
While Not rs.EOF
%>
<tr>
<% For Each fld in rs.Fields %>
<td><% = fld.Value %></td>
<% Next
</tr>
<% rs.MoveNext
Wend %>
</table>
下面的代碼更有效,每一行對響應流有一次寫操作。所有的代碼都包含在一個 VBScript 塊內:
<table>
<%
For each fld in rs.Fields
Response.Write (?<th>? & fld.Name & ?</th>? & vbCrLf)
Next
While Not rs.EOF
Response.Write (?<tr>?)
For Each fld in rs.Fields %>
Response.Write(?<td>? & fld.Value & ?</td>? & vbCrLf)
Next
Response.Write ?</tr>?
Wend
%>
</table>
當禁用響應緩衝時,這一技巧的效果特別大。最好啟用響應緩衝,然後看批處理 Response.Write 是否有助於提高效能。
(在這一特定舉例中,建立表主體的嵌套迴圈 (While Not rs.EOF...) 可以用仔細構建的 GetString 調用來替代。)
技巧 16:如果頁面需要很長時間才能完成,那麼執行前使用 Response.IsClientConnected
如果使用者性急,他們可能會在您開始執行他們的請求之前,就會放棄 ASP 頁面。如果他們單擊重新整理或移到伺服器上的另一個頁面,在 ASP 請求隊列的末尾就有一個新的請求等候,在隊列的中間有一個中斷連線的請求。當伺服器的負載很高時(因此請求隊列就會很長,回應時間也會相應地變長),就會經常發生這種情況,這樣只能使情況變得更糟。如果使用者不再串連,執行 ASP 頁面(特別是慢的、大的 ASP 頁面)已沒有任何意義。您可以使用 Response.IsClientConnected 屬性檢查這一情況。如果它返回 False,則應調用 Response.End 並放棄頁的其餘部分。事實上,IIS 5.0 已將這一做法編為程式 - 每當 ASP 即將執行新請求時,它就會檢查請求在隊列中已等候了多長時間。如果已經在那裡等候了多於 3 秒鐘,ASP 將檢查客戶機是否仍處於串連狀態,如果沒有串連,就立即終止請求。您可以在設定資料庫中使用 AspQueueConnectionTestTime 設定將逾時時間由 3 秒調整為其它值。
如果頁面要花很長時間才能執行完,也可以不時地檢查 Response.IsClientConnected。當啟用了響應緩衝時,最好不時地執行 Response.Flush,以使用者知道,正在發生什麼事。
注意 在 IIS 4.0 上,除非先執行了 Response.Write,否則 Response.IsClientConnected 就不能正常工作。如果啟用了緩衝,您也必須執行 Response.Flush。在 IIS 5.0 上,卻沒有必要這樣做,- Response.IsClientConnected 工作正常。在任何情況下,Response.IsClientConnected 都會有一些開銷,因此只有在一個操作至少要花(比方說) 500 毫秒(如果您想維持每秒鐘數十頁的輸送量,這是一個很長的時間)才使用它。經驗表明,不要每次重複執行緊密迴圈時都調用它,如顯示表的許多行時 - 每隔二十或五十行調用一次可能比較合適。
技巧 17:使用 <OBJECT> 標記例示對象
如果要引用不在所有代碼路徑(特別是伺服器或Application 領域的對象)中使用的對象,使用 Global.asa 中 <object runat=server id=objname> 標記聲明它們,而不使用 Server.CreateObject 方法。Server.CreateObject 能立即建立對象。如果以後不再使用該對象,您就浪費了資源。<object id=objname> 標記聲明 objname,但在其方法或屬性第一次使用以前,不會建立 objname。
這又是一個惰性計算的例子。
技巧 18:對於 ADO 和其它組件使用 TypeLib 聲明
當使用 ADO 時,開發人員經常加入 adovbs.txt,以訪問 ADO 的各種常量。在要使用常量的每個頁面中必須包含此檔案。此常量檔案相當大,給每個 ASP 頁面的編譯時間和指令碼大小增加了許多系統開銷。
IIS 5.0 引入了綁定到組件類型庫的功能。這可使您參考型別庫一次,並將其用在每個 ASP 頁面上。每個頁面不會產生編譯常量檔案的開銷,且組件開發人員不必建立 VBScript#_include 檔案以在 ASP 上使用。
要訪問 ADO TypeLib,將下面一條語句放在 Global.asa 中。
<!-- METADATA NAME=?Microsoft ActiveX Data Objects 2.5 Library?
TYPE=?TypeLib? UUID=?{00000205-0000-0010-8000-00AA006D2EA4}? -->
或
<!-- METADATA TYPE=?TypeLib?
FILE=?C:\Program Files\Common Files\system\ado\msado15.dll? -->
技巧 19: 利用瀏覽器的驗證功能
現今的瀏覽器對一些進階功能如 XML、DHTML、Java Applet和遠端資料服務提供支援。儘可能使用這些功能。所有這些技術都可以執行客戶機端驗證和資料緩衝,免去了到 Web 服務器的往返。如果您在運行一個智能瀏覽器,那麼瀏覽器就能為您進行一些驗證(例如,在執行 POST 之前,檢查信用卡校正和是否有效)。儘可能使用這一功能。通過減少客戶-伺服器之間的往返,可降低 Web 服務器上的負載,並能減少網路通訊量(雖然發送到瀏覽器的第一個頁面可能比較大)以及伺服器訪問的任何後端資源。此外,使用者不必像住常一樣讀取新頁,從而使用者的感覺會好一些。這樣做並不意味著您可以不進行伺服器端驗證 - 您還應始終進行伺服器端驗證。這可以防止由於某種原因(如駭客,或瀏覽器不運行客戶機端驗證常式)客戶機產生錯誤的資料。
人們已經進行了大量的工作,開發“獨立於瀏覽器”的 HTML。正是由於這種憂慮,開發人員不願再使用流行的瀏覽器功能,但這些功能本可以改善效能。對於一些真正的高效能網站,必須關心瀏覽器“訪問”問題,一個好的策略是最佳化頁面,使其適應流行的瀏覽器。使用瀏覽器功能組件,可以在 ASP 中方便地檢測到瀏覽器功能。Microsoft FrontPage 等工具有助於設計適合於瀏覽器和指定 HTML 版本的代碼。參見 When is Better Worse?Weighing the Technology Trade-Offs,以瞭解更進一步的討論。
技巧 20:避免在迴圈語句中使用字串串聯
許多人在迴圈語句中建立一個字串,如下所示:
s = ?<table>? & vbCrLf
For Each fld in rs.Fields
s = s & ? <th>? & fld.Name & ?</th> ?
Next
While Not rs.EOF
s = s & vbCrLf & ? <tr>?
For Each fld in rs.Fields
s = s & ? <td>? & fld.Value & ?</td> ?
Next
s = s & ? </tr>?
rs.MoveNext
Wend
s = s & vbCrLf & ?</table>? & vbCrLf
Response.Write s
採用這種方法會出現一些問題。第一個問題是反覆串聯字串需要花兩次方的時間,更通俗地說,運行這種迴圈語句所花的時間與記錄數乘以欄位數所得值的平方成正比。舉一個更簡單的例子,就可以更清楚地說明這一問題。
s = ??
For i = Asc(?A?) to Asc(?Z?)
s = s & Chr(i)
Next
在第一次迭代中,您獲得了一個字元的字串 ?A?。在第二次迭代中,VBScript 必須重新分配字串並將兩個字元 (?AB?) 複製到 s 中。在第三次迭代中,它還必須再次重新分配 s 並將三個字元複製到 s 中。在 N 次(第 26 次)迭代中,它必須重新分配並將 N 個字元複製到 s 中。總共就是 1+2+3+...+N,即 N*(N+1)/2 次複製。
在上面的記錄集舉例中,如果有 100 個記錄和 5 個欄位,內迴圈將執行 100*5 = 500 次,所有的複製和重新分配所花的時間與 500*500 = 250,000 成正比。這對於中等大小的記錄集來說複製操作太多了。
在本例中,代碼可以用 Response.Write() 或內嵌指令碼 (<% = fld.Value %>) 替代字串串聯來改進。如果啟用了響應緩衝的話(應該的),這樣做就會更快,因為 Response.Write 只將資料附加到響應緩衝的末尾。並不涉及重新分配,因此效率很高。
在將 ADO 記錄集轉換為 HTML 表的特定情況下,應考慮使用 GetRows 或 GetString。
如果在 JScript 中串聯字串,特別建議使用 += 運算子,即,使用 s += ?某字串?,而不使用 s = s + ?某字串?。
技巧 21:啟用瀏覽器和代理緩衝
在預設情況下,ASP 禁止在瀏覽器和代理中進行緩衝。這是有意義的,因為就實質而言 ASP 頁面是動態,上面有隨時間不斷變化的潛在資訊。如果頁面不要求在每個視圖上進行重新整理,您應啟用瀏覽器和代理緩衝。這可使瀏覽器和代理在一定的時間內使用頁面的“緩衝”副本,您可以控制時間的長短。緩衝可以大大減輕伺服器上的負載,縮短使用者的等待時間。
哪一種動態網頁面可作為要緩衝的頁面呢?下面舉一些例子:
天氣預報頁面,在此頁面上,每隔 5 分鐘更新一次天氣預報。
列出新聞條目或新聞稿的首頁,它一天更新兩次。
共同基金業績列表,在此列表中,基本統計資訊每隔幾小時更新一次。
注意,在使用瀏覽器或代理緩衝的情況下,Web 服務器上記錄的訪問次數減少了。如果您想準確地測量所有整頁模式或張帖公布,您就不希望使用瀏覽器和代理緩衝。
瀏覽器緩衝由 HTTP“到期”前序控制,該前序由 Web 服務器發送給瀏覽器。ASP 提供兩個簡單的機制發送此前序。要設定頁面使其過多少分鐘後到期,則應設定 Response.Expires 屬性。下面的例子告訴瀏覽器內容在 10 分鐘內到期:
<% Response.Expires = 10 %>
若將 Response.Expires 設定為負數或 0,則禁用緩衝。一定要使用大的負數,如 -1000(略多於一天),以避免伺服器和瀏覽器時鐘之間的不匹配。第二個屬性 Response.ExpiresAbsolute 將使您設定內容到期的具體時間:
<% Response.ExpiresAbsolute = #May 31,2001 13:30:15# %>
您可以不使用 Response 對象設定到期時間,而將 <META> 標記寫進 HTML,通常寫在 HTML 檔案的 <HEAD> 部分。一些瀏覽器將遵照此指令,而代理則不然。
<META HTTP-EQUIV=?Expires? VALUE=?May 31,2001 13:30:15?>
最後,您可以使用 Response.CacheControl 屬性,指示其內容是否可以讓 HTTP 代理緩衝。若將此屬性設定為“Public”,代理就可以緩衝此內容。
<% Response.CacheControl = ?Public? %>
在預設情況下,此屬性被設定為“Private”。注意,對於顯示某使用者特定資料的頁面,不應啟用代理緩衝,因為代理可能給使用者提供屬於其他使用者的頁面。