2010年8月31日 星期二

整理網頁中的ViewState(一) - 壓縮ViewState

之前上班的公司中有一組專門做SEO的Team,因為成員都不是具有程式背景,所以都會認為所有網站程式都是一個樣,
所以當遇到ASP.NET的網站要做SEO時,總是會氣急敗壞的問我原始碼中那一大沱亂碼到底是什麼?


其實也不是什麼怪東西,就是「__VIEWSTATE」這玩意!!
是呀!這到底是什麼東西,為什麼ASP.NET的網站就會有這東西,
而且還長長一大串而且還很吃傳輸頻寬,更耗費前端Render的時間,

我這邊也不需要講解什麼是ViewState,看看以下的文章就可知一二
ASP.NET 檢視狀態概觀

當然有些開發人員就會去想辦法讓ViewState再原始碼中消失,以下的方法就是其中一種
seanyhkao的 讓ViewState存到Session去吧
但是如同文章最後的討論,將ViewState存放到Server的記憶體中也是一項成本的增加
如果主機不單單是一個網站所使用時,那麼這樣的方式還是盡量避免,而且Session是有時間限制的
有篇文章可讓大家參考參考
Taking a Bite Out of ASP.NET ViewState

當然也有人會說,那就乾脆把前端ASP.NET的ViewState給Disable不就好了,這樣就不會有ViewState的產生…
這樣的方法或許可行,那如果網頁中一定會用到ViewState怎麼辦?
要兼顧網頁傳輸大小、網頁SEO、網頁功能、主機記憶體效能…etc,真的是一堆問題需要去考量到。

一個一個問題來解決。

首先不去考量ViewState的是否需要出現的問題,先解決ViewState的大小問題,那麼最先會想到的就是去壓縮ViewState,
網路上有許多教你如何去壓縮ViewState的文章,如點部落中gipi大的「使用SharpZipLib進行網頁ViewState壓縮
方法有許多種,而我就是用比較簡單的Base-64壓縮

首先是覆寫Page事件中的SvaePageStateToPersistenceMedium以及LoadPageStateFromPersistenceMedium
建議是建立PageBasre(並繼承Page),讓網站專案的每個頁面去繼承這個PageBase,這樣就不用每個網頁都要去覆寫
#region -- SavePageStateToPersistenceMedium --
 
/// <summary>
/// Saves the page state to persistence medium.
/// </summary>
/// <param name="viewState">State of the view.</param>
protected override void SavePageStateToPersistenceMedium(object viewState)
{
CompressViewStateByBase64(viewState);
}
 
#endregion
 
#region -- LoadPageStateFromPersistenceMedium --
 
/// <summary>
/// 載入任何儲存的檢視狀態資訊到 <see cref="T:System.Web.UI.Page"/> 物件。
/// </summary>
/// <returns>儲存的檢視狀態。</returns>
protected override object LoadPageStateFromPersistenceMedium()
{
return DecompressViewStateByBase64();
}
 
#endregion
 
/// <summary>
/// Compresses the view state by base64.
/// </summary>
/// <param name="viewState">State of the view.</param>
public void CompressViewStateByBase64(object viewState)
{
MemoryStream ms = new MemoryStream();
_formatter.Serialize(ms, viewState);
byte[] viewStateArray = ms.ToArray();
ClientScript.RegisterHiddenField(
"__COMPRESSEDVIEWSTATE",
Convert.ToBase64String(CompressViewState.Compress(viewStateArray))
);
}
 
/// <summary>
/// Decompresses the view state by base64.
/// </summary>
/// <returns></returns>
public object DecompressViewStateByBase64()
{
string vsString = Request.Form["__COMPRESSEDVIEWSTATE"];
byte[] bytes = Convert.FromBase64String(vsString);
bytes = CompressViewState.Decompress(bytes);
return _formatter.Deserialize(Convert.ToBase64String(bytes));
}

這邊有關壓縮後的ViewState Tag名稱,我自訂命名為「__COMPRESSEDVIEWSTATE」,

這部份名稱可以依照自己的喜好而命名,但是一定要清楚!

另外將壓縮和解壓縮方法給抽出來獨立存放


public class CompressViewState
{
#region -- Compress --
 
/// <summary>
/// Compresses the specified data.
/// </summary>
/// <param name="data">The data.</param>
/// <returns></returns>
public static byte[] Compress(byte[] data)
{
MemoryStream output = new MemoryStream();
GZipStream gzip = new GZipStream(output, CompressionMode.Compress, true);
gzip.Write(data, 0, data.Length);
gzip.Close();
return output.ToArray();
}
 
#endregion
 
#region -- Decompress --
 
/// <summary>
/// Decompresses the specified data.
/// </summary>
/// <param name="data">The data.</param>
/// <returns></returns>
public static byte[] Decompress(byte[] data)
{
MemoryStream input = new MemoryStream();
input.Write(data, 0, data.Length);
input.Position = 0;
GZipStream gzip = new GZipStream(input, CompressionMode.Decompress, true);
MemoryStream output = new MemoryStream();
byte[] buff = new byte[64];
int read = -1;
read = gzip.Read(buff, 0, buff.Length);
while (read > 0)
{
output.Write(buff, 0, read);
read = gzip.Read(buff, 0, buff.Length);
}
gzip.Close();
return output.ToArray();
}
 
#endregion
 
}

先來看看沒有使用壓縮前的網頁大小,大概會有17KB的大小,傳輸時間約4.74秒

2010-06-29_145628

再來看看有經過壓縮ViewState的網頁大小,減少為15.3KB,傳輸時間約2.92秒

2010-06-29_150410

因為測試網頁並沒有放多少東西,也沒有GridView等會大量增加ViewState的ServerControl,所以顯示的差距就不是那麼明顯,

但是仍然可以看出有壓縮與沒有壓縮還是有差的!!

但是壓縮就會比較好嗎?

必須要說在前的就是,因為多經過壓縮以及解壓縮的程序,就勢必會增加主機CPU的Loading,

所以主機效能與傳輸成本的考量與取捨,就端看開發者的智慧。



下一篇再來說說,如何把ViewState從前面移到頁面的下方




2 則留言:

  1. 請問一下您監控程式傳輸時間的工具是?
    該有的資訊都有了呢! 謝謝!

    回覆刪除
    回覆
    1. 那個是 Firefox 的擴充套件「Firebug」,
      如果是用 Chrome 的話,Chrome 本身就有開發者工具可以使用,有類似的功能,
      也可以另外安裝 for Chrome 的 Firebug Lite.

      刪除

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。