有关视图状态(ViewState)的一些问题
ASP.NET的ViewState是一个毁誉参半的设计。虽然很少有人真的讲清楚里面存的是什么东西,而且它到底将怎么影响我们的开发。但是大家目前的几点共识如下
1. 能不用它的时候就不用,以免降低性能。
要关闭它有三种方式
1.1 通过在web.config中将所有页面的视图状态都关闭掉
1.2 在某个特定页面中将其关闭
1.3 在某个特定控件上将其关闭
2. 如果非得用它,但又顾忌到性能和网络流量。那么可以选择压缩它。下面这个文档有详细的介绍。
http://www.cnblogs.com/webabcd/archive/2008/08/21/672587.html
大致的代码如下。主要重写了两个方法,SavePageStateToPersistenceMedium和LoadPageStateFromPersistenceMedium
【注意】这里既然是可以控制保存和加载行为,那么从道理上说,也可以把这些状态保存到其他地方,例如Session中。(手机页面通常这么做)
【注意】同样的道理,这里也可以进行加密和解密操作
protected override void SavePageStateToPersistenceMedium(object state) //这里传过来的参数就是那些视图状态值
{
LosFormatter formatter = new LosFormatter();
StringWriter writer = new StringWriter();
formatter.Serialize(writer, state);
string viewState = writer.ToString();
byte[] data = Convert.FromBase64String(viewState);//一般都是用BASE64进行编码
byte[] compressedData = ViewStateHelper.Compress(data);
string str = Convert.ToBase64String(compressedData);
ClientScript.RegisterHiddenField("__MYVIEWSTATE", str);//目前还是放在页面里面。只不过是另外一个隐藏域而已。
}
protected override object LoadPageStateFromPersistenceMedium()
{
string viewstate = Request.Form["__MYVIEWSTATE"];
byte[] data = Convert.FromBase64String(viewstate);
byte[] uncompressedData =
ViewStateHelper.Decompress(data);
string str = Convert.ToBase64String(uncompressedData);
LosFormatter formatter = new LosFormatter();
return formatter.Deserialize(str);
}
为了方便,这里编写了一个用来压缩和解压缩的帮助类(ViewStateHelper)
public static byte[] Compress(byte[] data)
{
MemoryStream ms = new MemoryStream();
GZipStream stream = new GZipStream(ms, CompressionMode.Compress);
stream.Write(data, 0, data.Length);
stream.Close();
return ms.ToArray();
}
public static byte[] Decompress(byte[] data)
{
MemoryStream ms = new MemoryStream();
ms.Write(data, 0, data.Length);
ms.Position = 0;
GZipStream stream = new GZipStream(ms, CompressionMode.Decompress);
MemoryStream temp = new MemoryStream();
byte[] buffer = new byte[1024];
while (true)
{
int read = stream.Read(buffer, 0, buffer.Length);
if (read <= 0)
{
break;
}
else
{
temp.Write(buffer, 0, buffer.Length);
}
}
stream.Close();
return temp.ToArray();
}
3. 除了可以压缩和加密,还有朋友提出,为了提高页面对于搜索引擎的友好性,应该将ViewState从表单的顶部移动到底部。这是一个很有意思的想法,下面这个文章讲述了大致怎么做
http://blog.csdn.net/lee576/archive/2008/11/01/3200258.aspx
这个代码的工作原理其实比较简单,它是重写了页面的Render方法,并且强制将那个视图状态的input移动到了底部。
//ViewState的Html标记的正则表达式
private static readonly Regex viewStateRegex = new Regex(@"(<input type=""hidden"" name=""__VIEWSTATE"" id=""__VIEWSTATE"" value=""/w(.*)"" />)", RegexOptions.Multiline | RegexOptions.Compiled);
//</form>标记的正则表达式
private static readonly Regex endFormRegex = new Regex(@"</form>", RegexOptions.Multiline | RegexOptions.Compiled);
protected override void Render(HtmlTextWriter writer)
{
System.IO.StringWriter stringWriter = new System.IO.StringWriter();
HtmlTextWriter htmlWriter = new HtmlTextWriter(stringWriter);
base.Render(htmlWriter);
string html = stringWriter.ToString();
Match viewStateMatch = viewStateRegex.Match(html);
string viewStateString = viewStateMatch.Captures[0].Value;//找出ViewState的Html标记
html = html.Remove(viewStateMatch.Index, viewStateMatch.Length);//替换掉ViewState的html标记
Match endFormMath = endFormRegex.Match(html, viewStateMatch.Index);
html = html.Insert(endFormMath.Index, viewStateString);//将ViewState的Html标记插入到</form>标记之前
writer.Write(html);
}
4. ViewState对象
ViewState不光是控件或者页面的一个属性,它也是一个特殊的API。我们可以通过ViewState[“VarName”]=value这种方式对其进行读写。但这个功能显然也是受到了页面级别的那个EnableViewState开关的控制。
5. 控件状态
因为ViewState可以被禁用,但是如果服务器控件需要保留自己的一些值,那么就可能会用到控件状态。有兴趣的朋友可以参考我另外一篇博客
http://www.cnblogs.com/chenxizhang/archive/2009/04/02/1427827.html