禁掉VIEWSTATE之后
禁掉VIEWSTATE之后(一)发布以后,获得了很多评论,让我很是鼓舞。原以为这是一个说烂了的话题,现在看来,基础的东西永远是值得关注的呀。
尤其要感谢xiaosuo、爱问天、徐少侠 等同学的回复,使得我发现了我以往的几个错误认识。重新学习过后,我们开始我们的第二讲的学习研究吧!这一次,我们关注的焦点是:
1. 什么是VIEWSTATE, 什么不是VIEWSTATE ?
2. VIEWSTATE和页面生命周期的关系 ?
VIEWSTATE首先表现为控件(Control)的属性,类型为StateBag。而StateBag实现了IDictionary(所以是一种键值对)和IStateManager。而IStateManager定义了三个方法:LoadViewState、SaveViewState和TrackViewState,一个属性:IsTrackingViewState(再次推荐.NET Reflactor反编译工具,还没有的同学赶快下一个,好东西呀)。望文生义,可以大概猜测得到,VIEWSTATE应该会被加载(Load),被保存(Save);而Track是什么意思呢?
ASP.NET页面生命周期是一个比较复杂的东西,而和理解VIEWSTATE有关的呢,应该是以下几个:Init、LoadViewState、LoadPostBackData、Load、RaisePostBackEvent和Render几个阶段。下面简要的说明一下:
1. Init:初始化控件。将页面初始化为一个“控件树”(和DOM树其实很相似),一般来说,根节点就是一个HtmlForm控件,就是由<form runat="server" id="form1"></form>转化而来的。
2. LoadViewState:这个阶段,有页面Post过来的VIEWSTATE将被解析加载(其实解析和加载还可分为两步,此处不细究)到控件。使得控件被回复到页面被提交之前的状态。
3. LoadPostBackData:这个阶段和VIEWSTATE没有关系,但能澄清我们很多人的误解!包括我第一节里所犯的最大的错误,为了更清晰的演示,我修改了之前的代码:
page页面
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="Label1" runat="server" Text="I am Label"></asp:Label><br />
<asp:TextBox ID="TextBox1" runat="server" Text="I am TextBox" ></asp:TextBox><br />
<asp:DropDownList ID="DropDownList1" runat="server">
<asp:ListItem Text="I am ListItem (1)"></asp:ListItem>
<asp:ListItem Text="I am ListItem (3)"></asp:ListItem>
<asp:ListItem Text="I am ListItem (2)"></asp:ListItem>
</asp:DropDownList><br />
<asp:LinkButton ID="LinkButton1" runat="server" Text="I am LinkButton">LinkButton</asp:LinkButton><br />
<asp:Button ID="Button1" runat="server" Text="I am Button"
onclick="Button1_Click" /><br />
<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/Default2.aspx">HyperLink</asp:HyperLink>
</div>
</form>
</body>cs文件
protected void Page_Load(object sender, EventArgs e)
{
Response.Write(Request.Form);
Label l = new Label();
l.ID = "lblAdded";
l.Text = "I am added dynamicly";
this.form1.Controls.Add(l);
}
protected void Button1_Click(object sender, EventArgs e)
{
this.Label1.Text = "changed";
this.TextBox1.BorderStyle = BorderStyle.Solid;
this.TextBox1.BackColor = Color.Blue;
this.TextBox1.BorderWidth = 2;
Label l = this.form1.FindControl("lblAdded") as Label;
l.Text = "changed";
}
按照第一节里的方法运行代码,我们可以发现,禁用VIEWSTATE之后,TextBox里的“值”是仍然可以保存的,但TextBox的边框熟悉不能被保存!所以,可以最直观的回答“什么是VIEWSTATE, 什么不是VIEWSTATE”:TextBox里的“值”不是VIEWSTATE,而TextBox的边框属性就是VIEWSTATE!
更深一层,我们会发现,TextBox实现了IPostBackDataHandler,而IPostBackDataHandler定义了两个方法:LoadPostData()和RaisePostDataChangedEvent()。TextBox(包括所有实现了IPostBackDataHandler的控件)的“值”是由以上这两个方法实现的,而不是VIEWSTATE,也不是我之前认为的浏览器。
我们可以通过设置断点,或者通过其他http工具,获得当页面回复时,Request.Form里面的数据(这是在禁用VIEWSTATE的情况下收集的):
可以看到TextBox1、DropDownList1和Button1其实是被传送回服务器端的(其实想想这好像是废话,呵呵)。不过注意他们是通过“正常的”<form action=……></form>表单,submit提交的!而不是像VIEWSTATE这样通过hiddenInput提交的。所以他们当然有理由分别处理了!
4. Load:这个就是我们最熟悉的阶段了,呵呵。到此阶段,所有的控件属性都已经被加载完毕(通过LoadViewState和LoadPostData)。
5. RaisePostBackEvent:这个阶段其实就是处理我们自己订阅的事件的,典型的如Button_Click。在这个阶段,我们就可能更改控件的状态。这种更改,就应该被记录下来(如果需要的话),而记录这种更改的,正是下面的SaveViewState。
6. SaveViewState: 在这里,将需要记录保存的VIEWSTATE编码(不是加密)成一个字符串。(是否服务器端会保存一个副本呢?来回传输的是页面全部的控件状态呢,还是变化的?)。
7. Render:更新过的VIEWSTATE被包含在 hidden input 里,发送到客户端。
以上就是VIEWSTATE生成的过程。
但是,如果回头再仔细想一想的话,我们还可能会产生下面的一些疑问:
是不是所有的控件信息都会被保存为VIEWSTATE?有这个必要么?肯定没有!
比如在页面上声明的控件属性,
<asp:Label ID="Label1" runat="server" Text="I am Label"></asp:Label><br />
每一次页面加载,都会使用页面声明的属性来解析、初始化页面,何必需要VIEWSTATE呢?所以VIEWSTATE根本就不会存储这种信息,它存储的是通过后台程序(cs文件)动态修改的属性,这当然也就包括了通过编程添加的控件。
Label l = new Label();
l.ID = "lblAdded";
l.Text = "I am added dynamicly";
this.form1.Controls.Add(l);
这也是gridview的VIEWSTATE如此巨大的一个重要原因,GridView里面的子控件全部都是动态生成的呀!
而更进一步,前面我们说过,VIEWSTATE必须实现三个方法:LoadViewState、SaveViewState和TrackViewState。LoadViewState和SaveViewState都已在相应的页面事件中实现了(Page其实是一个template controls,它会迭代的调用它的子控件中的LoadViewState和SaveViewState方法),所以剩下的就是TrackViewState方法了。该方法在init之后被调用,它将确保所有在此之前(Init完成之前)所获得的控件状态不会被Save!换句话说,VIEWSTATE只记录页面Init之后的控件状态。(你可能会想,那我何不在Init的时候生成GridView,是不是就会大量的减少VIEWSTATA?呵呵,留做思考题吧,我也想想,应该是不可行的。)
一个很好的参考资料:Understanding ASP.NET View State这篇文章涵盖了本文的所有内容,还包含VIEWSTATE的解析、加密和压缩的内容。而且有详细的图片解说,相当值得一看。
共同学习,写下你的评论
评论加载中...
作者其他优质文章