2012年8月22日 星期三

Asp.Net Ajax 生命週期

看到 Asithangae 把 ASP.Net 使用 Ajax 技術後的前端事件生命週期寫得這麼詳細,真是令人讚嘆。 閱讀後,大致上對於前端的事件,清楚許多。

先以一般的事件來說,大概可分為三種:

一、pageInit   (ref. Sys.Application.init 事件)
當頁面第一次被載入,或是整個頁面觸發 post back 之後會發生。但如果是非同步的請求,也就是僅有部份頁面被更新,則不會發生。
Sys.Application.add_init(MyInit);

function MyInit()
{
    alert("MyInit added to page init");
}

二、pageLoad  (ref. Sys.Application.load 事件)
他的觸發順序是在 pageInit 之後。他觸發的時機點跟 pageInit 相似,當頁面第一次被載入,或是整個頁面觸發 post back 之後會發生。唯一不同的地方,是非同步的請求,也就是僅有部份頁面被更新時,他也會發生。
Sys.Application.add_load(MyLoad);

function MyLoad()
{
    alert("MyLoad added to page load");
}

三、pageUnload  (ref. Sys.Application.unload 事件)
當要離開頁面的時候,這事件會被觸發。例如程式裡面使用了 Response.Redirect() 或是 Server.Transfer()。
Sys.Application.add_unload(MyUnload);

function MyUnload()
{
    alert("MyUnload added to page unload");
}


此外,針對非同步請求,則又可以分為以下五種:

一、initializeRequest (ref. Sys.WebForms.PageRequestManager initializeRequest 事件)
觸發的時機點,是使用者壓下頁面上的控制項,準備要執行非同步更新的請求。只是目前這個請求,尚未送到後端的伺服器。既然這請求尚未送到後端,表示在這時間點,我們有機會執行取消或放棄請求的動作。
Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest( MyIntializeRequest );

function MyIntializeRequest(sender,args)
{
    alert("My Request is getting initalized");
}


二、beginRequest (ref. Sys.WebForms.PageRequestManager beginRequest 事件)
這事件是發生在 initializeRequest 之後,此時的非同步更新請求已經傳送到後端伺服器,並等待處理。通常在這時候,可以擺放「處理中」的動畫圖示。
Sys.WebForms.PageRequestManager.getInstance().add_beginRequest( MyBeginRequest );

function MyBeginRequest(sender,args)
{
    alert("My Request is ready about to sent to server");
}

三、pageLoading (ref. Sys.WebForms.PageRequestManager pageLoading 事件)
當後端伺服器處理完我們的非同步更新請求後,在尚未把處理結果交予前端之前,會觸發這個事件。
Sys.WebForms.PageRequestManager.getInstance().add_pageLoading( MyPageLoading );

function MyPageLoading(sender, args)
{
    alert("My page is started loading");
}

四、pageLoaded (ref. Sys.WebForms.PageRequestManager pageLoaded 事件)
當前端頁面完成所有非同步更新請求的結果後,會觸發這個事件。我們原先在 beginRequest 所擺放的「處理中」動畫圖示,在這個時間點就可以撤除了。
Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded( MyPageLoaded );

function MyPageLoaded(sender, args)
{
    alert("My page is loaded");
}

五、endRequest (ref. Sys.WebForms.PageRequestManager endRequest 事件)
這個事件是整個非同步更新請求的最後一個,當他結束後,就會把控制權還給使用者。而再這個事件裡,也會回傳此次非同步更新請求的結果,例如:失敗或成功,如果有需要做紀錄(log),也是在這個事件裡面來撰寫。
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(MyEndRequest);

function MyEndRequest(sender, args)
{
    alert("My Request has end");
}



參考「Ajax 用戶端生命週期事件」一文,假設非同步回傳的狀況假設如下:

頁面包含一個 ScriptManager 控制項,且控制項的 SupportsPartialRendering 和 EnablePartialRendering 屬性皆為 true。

頁面包含一個 UpdatePanel 控制項,且控制項的 ChildrenAsTriggers 屬性為 true。

UpdatePanel 控制項內部的按鈕會啟始非同步回傳。

伺服器成功傳回回應。

下列用戶端事件會依此順序發生:

1.按一下 UpdatePanel 內部的按鈕,該按鈕會啟始非同步回傳。

2.PageRequestManager 執行個體會引發 initializeRequest 事件。

3.PageRequestManager 執行個體會引發 beginRequest 事件。

4.傳送要求至伺服器。

5.用戶端收到回應。

6.PageRequestManager 執行個體會引發 pageLoading 事件。

7.PageRequestManager 執行個體會引發 pageLoaded 事件。

8.Application 執行個體會引發 load 事件。

9.PageRequestManager 執行個體會引發 endRequest 事件。

請注意,Application 執行個體的 load 事件,是在 PageRequestManager pageLoaded 事件之後、及其 endRequest 事件之前引發的。



在上面所介紹的各種事件觸發裡,實際測試後,發現 pageUnload 其實並沒這麼單純。
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>    
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <script>
            function MyUnload() {
                alert('this is my unload');              
            }

            Sys.Application.add_unload(MyUnload);
        </script>    
    </div>
    </form>
</body>
</html>

這個範例裡,期待當網頁關閉,或要切換到別的頁面時,會出現「this is my unload」的訊息。這需求在 IE 瀏覽器可以正常執行,但是在 Firefox、Chrome 則完全失效。以前 IE 獨大的年代或許可以不考慮這問題,但今非昔比,接下來就是要特別處理 unload 事件了。
參考先前 網頁離開前跳出確認視窗 的文章,改成去攔 window.onbeforeunload 事件。這時候,我把原先 Sys.Application.add_unload(MyUnload)  改成使用 Sys.UI.DomEvent.addHandler(window, "beforeunload", MyUnload)後,的確有些改善,Firefox 可以在離開網頁前跳出「this is my unload」訊息,但 chrome 還是不行。原因是 Chrome 在 onbeforeunload 事件,如果要在當下跳出訊息,不可以用 alert( ),而是要用 return( )。 也就是要寫成:return 'this is my unload'; (參考:unload not working in Chrome)。為了同時讓這三大主流瀏覽器同時能夠支援,所以我在 MyUnload( ) 事件裡,就同時寫了:  alert('this is my unload');   return 'this is my unload';  如此才讓三者相安無事,彼此共存。


<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>    
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <script>
            function MyUnload() {
                alert('this is my unload');
                return 'this is my unload';
            }
          
            Sys.UI.DomEvent.addHandler(window, "beforeunload", MyUnload);
        </script>    
    </div>
    </form>
</body>
</html>


以上是針對 Unload 事件時跳出訊息( alert )的處理,如果是要產生確認 ( confirm )的效果,可以參考網頁離開前跳出確認視窗 的作法。

參考:
01:The Page Life Cycle of Client [Browser]
02:網頁離開前跳出確認視窗
03:unload not working in Chrome
04:Sys.Application.init 事件
05:Sys.Application.load 事件
06:Sys.Application.unload 事件
07:Sys.WebForms.PageRequestManager initializeRequest 事件
08:Sys.WebForms.PageRequestManager beginRequest 事件
09:Sys.WebForms.PageRequestManager pageLoading 事件
10:Sys.WebForms.PageRequestManager pageLoaded 事件
11:Sys.WebForms.PageRequestManager endRequest 事件
12:Ajax 用戶端生命週期事件

沒有留言:

張貼留言