ZT Web Control 开发系列(一) 页面的生命周期
????可以認(rèn)為一個(gè)Page是由很多的Control按照樹形結(jié)構(gòu)組織的,而樹的根就是Page(一個(gè)實(shí)現(xiàn)了IHttphandler的Control), 整個(gè)Control樹的生命周期開始于一個(gè)Http請(qǐng)求,而終止于請(qǐng)求處理的結(jié)束。事實(shí)上在Http請(qǐng)求傳入到當(dāng)前的Page的時(shí)候,之前已經(jīng)經(jīng)過了漫長的路程,如果對(duì)于整個(gè)Http請(qǐng)求的細(xì)節(jié)感興趣,可以查看MSDN中關(guān)于應(yīng)用程序的生命周期。另外關(guān)于頁面的生存周期也有很多的文章可以參考,比如MSDN中ASP.NET 頁生命周期概述。
???? 我這里主要講講我個(gè)人的理解,如果有什么不妥的地方,歡迎大家指正。
????
??????? Page存在的目的就是對(duì)于用戶的內(nèi)容進(jìn)行呈現(xiàn),它是一個(gè)IHttpHandler,它實(shí)現(xiàn)了對(duì)HttpRequest的處理,這個(gè)處理的結(jié)果就是根據(jù)請(qǐng)求Render出它自己,而Render的內(nèi)容就是Html,CSS,Javascript,這些東西最終成為表現(xiàn)Page樣子的一磚一瓦。如果做一個(gè)最簡單的實(shí)現(xiàn),大概就是
public?class?Page?:?IHttpHandler
{
????public?void?ProcessRequest(HttpContext?httpContext)
????{
????????Render();
?????}
}
??????? 事實(shí)并非如此,如果這樣的話,我們豈不是對(duì)于每個(gè)Page都要從零開始做一套完整的Render畫法啊,那樣的Render方法將是一個(gè)很長,很長的調(diào)用,為了支持不同的瀏覽器,為了應(yīng)付復(fù)雜的業(yè)務(wù)邏輯,里面的代碼將是魔鬼,也許寫完一次沒有人愿意維護(hù)了,而且代碼的復(fù)用性也很低。那么怎么辦呢?.net為了解決這個(gè)問題,提供的是Control模型,對(duì)于一個(gè)頁面,頁面本身是一個(gè)可以呈現(xiàn)自己的Control,同事頁面里面也可以任意嵌套其它的Control,每個(gè)Control都具有呈現(xiàn)自己的能力,那樣Control將具有很好的復(fù)用性和可維護(hù)性,任何一個(gè)開發(fā)人員可以任意組合來自于第三方的Control來構(gòu)建自己的Page。所以就需要有一個(gè)Control的類,而且Page是Control的一個(gè)派生類。Page的Render方法需要遞歸調(diào)用RenderChildren,這樣讓整個(gè)Control樹一層一層的呈現(xiàn),最后得到整個(gè)Page的樣子。
??????? 我們知道Http請(qǐng)求里面有Get和Post,對(duì)于Get往往是請(qǐng)求一個(gè)全新的頁面,對(duì)于Post更多的是客戶端的一個(gè)響應(yīng)。對(duì)于Get的請(qǐng)求,我們可以從零開始Render一個(gè)新的Page給客戶端。但是對(duì)于Post,我們一般需要讓頁面恢復(fù)到客戶端看到的狀態(tài),然后再出來Post的數(shù)據(jù)。正是由于這個(gè)原因,Page在ProcessRequest的過程中不得不區(qū)分Page.IsPost or not。而如何進(jìn)行場景恢復(fù)呢,這大概就是ViewState產(chǎn)生的真正原因了,ViewState是Control上的一個(gè)存儲(chǔ)結(jié)構(gòu)。它負(fù)責(zé)保存Control的一些狀態(tài)。這些狀態(tài)通常會(huì)被序列化到客戶端的頁面上,當(dāng)客戶端的頁面發(fā)出Post請(qǐng)求的時(shí)候,.net生產(chǎn)的腳本會(huì)自動(dòng)收集這些ViewState數(shù)據(jù),然后一起Post給服務(wù)器端。這樣Page就可以加載這些狀態(tài)進(jìn)行場景恢復(fù),然后在恢復(fù)好的場景下出來PostData。所以在ProcessRequest里面需要加入對(duì)于LoadViewState,SaveViewState,ProcessPostData的處理。
??????? 但是是否所有對(duì)于Control的設(shè)置都會(huì)序列化成ViewState呢,那樣不是很影響性能嗎,如果說我們每次在請(qǐng)求處理的開始階段(無論是Post還是非Post)都用代碼初始化Control,不是就不需要利用ViewState加載狀態(tài)了啊?所以在ProcessRequest里面又有了新的過程的加入Init,在Init里面可以設(shè)置Control的一些屬性和狀態(tài),而在Init以后才通過TrackViewState來通知Control,TrackViewState階段后對(duì)Control的修改才會(huì)SaveViewState的時(shí)候保存下來,否則都不保存。
??????? 類似的例子還有很多,就像我們自己做的軟件產(chǎn)品,很多時(shí)候,第一個(gè)版本都是簡單的,以后隨著需求的增加,代碼越來越多,結(jié)構(gòu)越來越復(fù)雜,也許未來版本的Page還會(huì)有更多的變化,總之經(jīng)過很多的需求,Page對(duì)于Request的處理就變得復(fù)雜了,最后就分為下面的幾個(gè)階段,關(guān)于這些階段的介紹,我們可以在Google上搜索很多的文章,我認(rèn)為我們不僅僅要了解這些階段,還要深入理解,否則想做出好的Control是很困難的。代碼中的數(shù)字列出了主要的頁面生命周期的階段,對(duì)于PostBack和CallBack,階段會(huì)發(fā)生一些變化,也加了說明
private?void?ProcessRequestMain(bool?includeStagesBeforeAsyncPoint,?bool?includeStagesAfterAsyncPoint)
????{
????????//?1.?PreInit
????????this.PerformPreInit();
????????//?2.?Init
????????this.InitRecursive(null);
????????this.OnInitComplete(EventArgs.Empty);
????????//?對(duì)于Postback,插入下面處理
????????if?(this.IsPostBack)
????????{
????????????//?加載ViewState和ControlState,進(jìn)行場景恢復(fù)
????????????this.LoadAllState();
????????????//?第一次處理PostData
????????????this.ProcessPostData(this._requestValueCollection,?true);
????????}
????????//?3.?PreLoad
????????this.OnPreLoad(EventArgs.Empty);
????????//?4.?Load
????????this.LoadRecursive();
????????//?對(duì)于Postback,插入下面處理
????????if?(this.IsPostBack)
????????{
????????????//?第二次處理PostData
????????????this.ProcessPostData(this._leftoverPostData,?false);
????????????//?如果PostData表面某個(gè)Control數(shù)據(jù)發(fā)生變化,那么RaisePostDataChanged事件
????????????this.RaiseChangedEvents();
????????????//?RaisePostBackEvent
????????????this.RaisePostBackEvent(this._requestValueCollection);
????????}
????????this.OnLoadComplete(EventArgs.Empty);
????????//?對(duì)于CallBack,RaiseCallBackEvent
????????if?(this.IsPostBack?&&?this.IsCallback)
????????{
????????????this.PrepareCallback(callbackControlID);
????????}
????????else?if?(!this.IsCrossPagePostBack)
????????{
????????????//?5.?PreRender
????????????this.PreRenderRecursiveInternal();
????????}
???
????????//?對(duì)于CallBack,?Render出CallBack的結(jié)果
????????if?(this.IsCallback)
????????{
????????????this.RenderCallback();
????????}
????????else?if?(!this.IsCrossPagePostBack)
????????{
????????????this.PerformPreRenderComplete();
????????????
????????????//?6.?SaveViewStae和ControlState
????????????this.SaveAllState();
????????????this.OnSaveStateComplete(EventArgs.Empty);
????????????//?7.?Render?整個(gè)Control
????????????this.RenderControl(this.CreateHtmlTextWriter(this.Response.Output));
????????}
????}
1. PreInit 階段
??? 這個(gè)階段是Page獨(dú)有的,在Control上是沒有的,這個(gè)階段主要是.net Framework自己在里面做了一些自己的初始化工作,比如Skin的加載,MasterPage的加載。會(huì)發(fā)Page.OnPreInit事件,源代碼如下:
private?void?PerformPreInit()
{
????this.OnPreInit(EventArgs.Empty);
????this.InitializeThemes();
????this.ApplyMasterPage();
????this._preInitWorkComplete?=?true;
}
2. Init階段
??? InitRecursive()是Page從Control繼承的方法,它主要進(jìn)行Control的一些初始化和標(biāo)記工作
??? a.? 對(duì)當(dāng)前Control進(jìn)行準(zhǔn)確初始化,包括:
- 初始化_adapter;
- ApplySkin(this.Page);
- 設(shè)置標(biāo)記this._controlState = ControlState.Initialized;
- TrackViewState()//開始跟蹤變化,這個(gè)階段以后的變化會(huì)存入ViewState
- 初始化nameContainer屬性
- 初始化page屬性
- 自動(dòng)生成Id
- 遞歸調(diào)用子Control的InitRecursive()方法
????? 有個(gè)值得注意的是,如果在Control樹執(zhí)行完Init之后創(chuàng)建一個(gè)新的Control加入到樹上,那么當(dāng)它加入的時(shí)候,在父Control的AddedControl方法里面,如果發(fā)現(xiàn)已經(jīng)Initialized,那么手動(dòng)調(diào)用新加入Control的InitRecursive()方法。
??? 所以,我們Control的Init階段給Control的一些需要查找才可以得到的屬性進(jìn)行直接賦值,如Page,nameContainer,這樣可以提高這些屬性的訪問速度。
3. PreLoad階段
?? 僅僅發(fā)Page獨(dú)有的事件:OnPreLoad事件
4. Load階段
?? LoadRecursive()是Page從Control繼承的方法,它比較簡單,僅僅是遞歸調(diào)用子Control的LoadRecursive()方法,然后做一個(gè)階段標(biāo)記: (this._controlState = ControlState.Loaded);
5. PreRender階段
??? PreRenderRecursiveInternal()是Page從Control繼承的方法,這個(gè)方法會(huì)
- EnsureChildControls()-->調(diào)用CreateChildControls(),確保子Control創(chuàng)建完畢,為接下來的Render做準(zhǔn)備
- 遞歸調(diào)用子Control的PreRenderRecursiveInternal()??
6. SaveAllState 階段
??? 主要存儲(chǔ)ControlState和ViewState,ControlState和ViewState唯一不同的地方在于ControlState是不可以禁用的,而ViewState可以禁用,事實(shí)上.net Framework在ControlState里面還加入了一個(gè)數(shù)據(jù),這個(gè)數(shù)據(jù)是一個(gè)ArrayList,里面存入了所有的需要處理PostData的Control的Id,這樣在Post階段,.net Framework會(huì)根據(jù)ArrayList里面保存的Control來依次調(diào)用ProcessPostData方法,前提是這些Control最好實(shí)現(xiàn)IPostDataHandler接口。
7. Render階段
?? RenderControl()是Page從Control繼承的方法,這個(gè)方法會(huì)遞歸調(diào)用子Control的RenderControl (),這樣一層一層進(jìn)行呈現(xiàn)。
總結(jié):
??? Init,Load,PreRender,SaveState,Render這幾個(gè)階段會(huì)在整個(gè)Control樹上遞歸貫穿。在Init里面對(duì)Control的修改,一般是不會(huì)保存到ViewState里面,這個(gè)階段以后的變化會(huì)存入ViewState。
??? LoadAllState發(fā)生在Init和Load之間,因?yàn)長oadState會(huì)進(jìn)行場景恢復(fù),所以如果我們?cè)赑age_Load里面進(jìn)行了一些初始化工作,那么如果在Post階段就不需要二次初始化了,所以經(jīng)常會(huì)寫這樣的代碼 if (Page.IsPostBack) {...; // Init Controls},真正的原因就在這里了。
?? 另外也有一些在Load事件里面動(dòng)態(tài)創(chuàng)建Control的做法,這個(gè)時(shí)候也要小心了。因?yàn)長oadAllState只會(huì)加載ViewState數(shù)據(jù)包,并不會(huì)創(chuàng)建Control(人家也不知道你的Control什么類型啊),所以無論是否IsPostBack,Control都需要?jiǎng)?chuàng)建并且加入到Controls集合。如果在Post階段,當(dāng)Control一加入集合,就會(huì)被調(diào)用InitRecursive()方法進(jìn)行初始化,同時(shí)還會(huì)把父Control上保存的該Control的ViewState傳給它,讓它加載。關(guān)于加載ViewState的知識(shí)也比較復(fù)雜,有安裝Control Index加載和安裝Control Id加載兩種,細(xì)節(jié)可以在以后專題講述。
補(bǔ)充:關(guān)于一個(gè)Control的生和死
Control的生可以分為兩種,一種是在DesignMode下設(shè)計(jì)好,一旦一個(gè)請(qǐng)求到來,Page被創(chuàng)建,這個(gè)時(shí)候Control就已經(jīng)添加到以Page為根的Control樹了,所以它可以經(jīng)歷完整的頁面生命周期(Init, Load。。。);另一種是在頁面生命周期的某個(gè)階段創(chuàng)建,例如Init的時(shí)候,或者Load的時(shí)候,這個(gè)時(shí)候.net為了繼續(xù)保持Control能經(jīng)歷頁面的整個(gè)生命周期,會(huì)在它被加入到Control樹的瞬間進(jìn)行一些補(bǔ)充式的調(diào)用,具體實(shí)現(xiàn)可以看下面的Control.AddedControl方法。
?
protected?internal?virtual?void?AddedControl(Control?control,?int?index){
????//?1.?初始化Page,Parent,NameContainer,ID
????control._parent?=?this;
????control._page?=?this.Page;
????control.flags.Clear(0x20000);
????Control?namingContainer?=?this.flags[0x80]???this?:?this._namingContainer;
????if?(namingContainer?!=?null)
????{
????????control.UpdateNamingContainer(namingContainer);
????????if?((control._id?==?null)?&&?!control.flags[0x40])
????????{
????????????control.GenerateAutomaticID();
????????}
????????else?if?((control._id?!=?null)?||?((control._occasionalFields?!=?null)?&&?(control._occasionalFields.Controls?!=?null)))
????????{
????????????namingContainer.DirtyNameTable();
????????}
????}
????//?2.?判斷當(dāng)前Control所在的頁面生命周期階段,然后對(duì)于新加入的Control進(jìn)行補(bǔ)充調(diào)用
????if?(this._controlState?>=?ControlState.ChildrenInitialized)
????{
????????control.InitRecursive(namingContainer);
????????if?(((control._controlState?>=?ControlState.Initialized)?&&?(control.RareFields?!=?null))?&&?control.RareFields.RequiredControlState)
????????{
????????????this.Page.RegisterRequiresControlState(control);
????????}
????????if?(this._controlState?>=?ControlState.ViewStateLoaded)
????????{
????????????object?savedState?=?null;
????????????if?((this._occasionalFields?!=?null)?&&?(this._occasionalFields.ControlsViewState?!=?null))
????????????{
????????????????savedState?=?this._occasionalFields.ControlsViewState[index];
????????????????if?(this.LoadViewStateByID)
????????????????{
????????????????????control.EnsureID();
????????????????????savedState?=?this._occasionalFields.ControlsViewState[control.ID];
????????????????????this._occasionalFields.ControlsViewState.Remove(control.ID);
????????????????}
????????????????else
????????????????{
????????????????????savedState?=?this._occasionalFields.ControlsViewState[index];
????????????????????this._occasionalFields.ControlsViewState.Remove(index);
????????????????}
????????????}
????????????control.LoadViewStateRecursive(savedState);
????????????if?(this._controlState?>=?ControlState.Loaded)
????????????{
????????????????control.LoadRecursive();
????????????????if?(this._controlState?>=?ControlState.PreRendered)
????????????????{
????????????????????control.PreRenderRecursiveInternal();
????????????????}
????????????}
????????}
????}
}
同樣,Control的死也可以分為兩種,一種就是完整的經(jīng)歷一個(gè)頁面請(qǐng)求,.net會(huì)在所有的請(qǐng)求都處理完了之后,也就是在我上面講的所有的階段之后調(diào)用一個(gè)ProcessRequestClearUp()方法,另外一種就是在頁面的生命周期的某個(gè)階段調(diào)用Controls.Remove(control)方法來干掉Control,在這個(gè)調(diào)用發(fā)生后,父Control有一個(gè)叫做RemovedControl的方法會(huì)調(diào)用(和上面的AddedControl是兄弟哦),來進(jìn)行清理工作,其實(shí)現(xiàn)基本是上面AddedControl的反操作。
值得注意的是,無論是RemovedControl()還是ProcessRequestClearUp(),它們都在Control還沒有從Control樹上摘掉的時(shí)候,調(diào)用了一個(gè)非常重要的方法control.UnloadRecursive(),這個(gè)方法從最底層的子Control向上直到當(dāng)前正在移除的Control,依次執(zhí)行OnUnload()方法,所以做WebControl的時(shí)候,我們可以override on_Unload()方法,在這個(gè)方法里面,可以摘除Event,摘除與Control樹關(guān)聯(lián)的變量,做一些清理工作。這點(diǎn)是非常有用的。
后記:從來沒有往首頁上發(fā)布過我的帖子。當(dāng)我昨天發(fā)布了前言后發(fā)現(xiàn)很多人對(duì)這個(gè)話題都很感興趣,一方面感覺高興,一方面也感覺壓力,畢竟我沒有寫過什么像樣的技術(shù)文章,生怕辜負(fù)大家厚望。最近白天工作忙,只能晚上在家好好整理思路寫出來。因?yàn)閷?duì)于寫WebControl的基本方法已經(jīng)有很多地方介紹了(比如《道不遠(yuǎn)人》),也沒有重復(fù)一遍的必要,所以我主要寫寫我對(duì)WebForm主要實(shí)現(xiàn)的理解。我認(rèn)為這是我們?cè)O(shè)計(jì)一個(gè)專業(yè)的WebControl的基本功。有什么寫的不清楚的地方,歡迎大家指正,我一定盡力補(bǔ)充。
轉(zhuǎn)載于:https://www.cnblogs.com/limxc/archive/2008/07/22/1249070.html
總結(jié)
以上是生活随笔為你收集整理的ZT Web Control 开发系列(一) 页面的生命周期的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到好多孩子是什么意思
- 下一篇: 我来告诉你为什么中国民营企业管理失败的原