ASP.NET页面运行时候,页面将经历一个生命周期,在生命周期中将执行一系列的处理步骤。包括初始化、实例化控件、还原和维护状态、运行时间处理程序代码以及进行呈现。熟悉页面生命周期非常重要,这样我们才能在生命周期的合适阶段编写代码。如果我们能在写代码的时候想着我们现在是在做生命周期的哪一步那将是非常好的。
几个代表性的问题
在开始的时候我们先思考几个问题,看看我们在描述完页面生命周期的时候,能不能回答上这几个问题
1.为什么在服务器端能通过this.textbox1.Text获取到用户提交过来的数据?
2.在Page_Load中Response.Write("hello")查看生成的html代码原文件,hello在哪里?为什么?
3.有一个服务器端的按钮,设置了点击事件,该点击事件什么时候执行?是先执行Page_Load事件还是先执行点击事件?
4.为什么在服务器端通过this.textbox1.Text设置值后,客户端就能显示出来?
了解ASP.NET请求管道、应用程序生命周期、整体运行机制童鞋可能知道,ASP.NET应用程序周期中PreRequestHandlerExecute事件与PostRequestHandlerExecute事件之间就是我们的页面生命周期了,对于aspx页面就是一系列的打造页面控件树,触发各种页面时间,对于一般处理程序ashx就是直接执行咱们开发者写的ProcessRequest方法了,对于MVC应用程序就是创建控制器工厂,创建控制器对象,调用Action那一套了。
下面主要讲述的就是ASP.NET WebForm中的页面的生命周期了。
我们用反编译工具查看Page类的ProcessRequest方法可以看见先调用了FrameworkInitialize; FrameworkInitialize里面就是打造了页面控件树,然后调用了ProcessRequestMain,就开始了执行整个页面生命周期了(其实就是调用了一系列的事件方法)(可能部分图看不见右边,可在新标签页中打开图片)
1.打造页面控件树
FrameworkInitialize内部调用了_buildControlTree()方法
上图中左边是前台页面的代码,右边是对应 生成的打造控件树的代码。中间截取的是生成表单那一部分的代码。
下面看一张原理图
浏览器的DOM树是根据Html标签生成一个C语言的DOM树,而ASP.NET服务器端是用C#打造的一个控件树,也是按照DOM结构打造的。本质是一样。服务器端所有东西都加到页面对象的控件集合中去了。标签在服务器端有对应的控件对象的时候就用控件对象,没有的时候就使用LiteralControl进行封装。不管是服务器控件还是字符串标签(没有runat="server"的标签)都以控件对象的方式存在前台页面类的控件集合里面。好处就是生成前台页面的html代码的时候,只需要遍历控件集合里面的每一个控件对象的RenderControl方法,每一个控件都会调用自己的Render方法生成对应的Html字符串。那么所有控件的生成的html字符串就还原成一个页面的html代码了。
2.触发PerformPreInit事件
在所有初始化之前初始化了这个事件,这个事件主要是初始化了主题,初始化了母版页
private void PerformPreInit() { this.OnPreInit(EventArgs.Empty); this.InitializeThemes(); this.ApplyMasterPage(); this._preInitWorkComplete = true; }
3.触发InitRecursive事件
4.触发LoadAllState()事件
加载页面状态解析ViewState,将页面表单中的ViewState进行反Base64编码,反序列化,存在页面的ViewState属性中
5.触发ProcessPostData(this._requestValueCollection, true)事件
private void ProcessPostData(NameValueCollection postData, bool fBeforeLoad) { if (this._changedPostDataConsumers == null) { this._changedPostDataConsumers = new ArrayList(); } if (postData != null) { foreach (string str in postData) { if ((str != null) && !IsSystemPostField(str)) { Control control = this.FindControl(str); if (control == null) { if (fBeforeLoad) { if (this._leftoverPostData == null) { this._leftoverPostData = new NameValueCollection(); } this._leftoverPostData.Add(str, null); } } else { IPostBackDataHandler postBackDataHandler = control.PostBackDataHandler; if (postBackDataHandler == null) { if (control.PostBackEventHandler != null) { this.RegisterRequiresRaiseEvent(control.PostBackEventHandler); } } else { if (postBackDataHandler != null) { NameValueCollection postCollection = control.CalculateEffectiveValidateRequest() ? this._requestValueCollection : this._unvalidatedRequestValueCollection; if (postBackDataHandler.LoadPostData(str, postCollection)) { this._changedPostDataConsumers.Add(control); } } if (this._controlsRequiringPostBack != null) { this._controlsRequiringPostBack.Remove(str); } } } } } } ArrayList list = null; if (this._controlsRequiringPostBack != null) { foreach (string str2 in this._controlsRequiringPostBack) { Control control2 = this.FindControl(str2); if (control2 != null) { IPostBackDataHandler adapterInternal = control2.AdapterInternal as IPostBackDataHandler; if (adapterInternal == null) { adapterInternal = control2 as IPostBackDataHandler; } if (adapterInternal == null) { object[] args = new object[] { str2 }; throw new HttpException(SR.GetString("Postback_ctrl_not_found", args)); } NameValueCollection values2 = control2.CalculateEffectiveValidateRequest() ? this._requestValueCollection : this._unvalidatedRequestValueCollection; if (adapterInternal.LoadPostData(str2, values2)) { this._changedPostDataConsumers.Add(control2); } } else if (fBeforeLoad) { if (list == null) { list = new ArrayList(); } list.Add(str2); } } this._controlsRequiringPostBack = list; } }
主要做了两件事