禁掉VIEWSTATE之后(二)

时间:2022-09-02 14:52:04
禁掉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没有关系,但能澄清我们很多人的误解!包括我第一节里所犯的最大的错误,为了更清晰的演示,我修改了之前的代码:


禁掉VIEWSTATE之后(二)禁掉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 >
禁掉VIEWSTATE之后(二)禁掉VIEWSTATE之后(二)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的解析、加密和压缩的内容。而且有详细的图片解说,相当值得一看。