網頁

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.

      刪除