聲明:FineUIMvc(基礎版)是免費軟件,本系列文章適用於基礎版。
用戶反饋
有網友在官方論壇拋出了這么一個問題,似乎對 FineUIMvc 中的瀏覽器端與服務器端的交互方式很有異議。
這里面的關鍵詞就是:回發!
似乎一提到回發(__doPostBack),就讓人聯想到 WebForms 中的 ViewState 和單表單提交,因為回發時會把頁面上所有控件的 ViewState 一股腦的提交到后台,無疑加重了網絡的上行數據量。從此 回發 這一名詞給人的印象就很晦澀了。
真的是這樣嗎?我們分別來比較 WebForms、ASP.NET MVC、以及FineUIMvc中的回發,來探索其中的聯系和差異。
WebForms中的回發(__doPostBack)
每位經歷過 ASP.NET WebForms 的開發人員都不會忘記這個字符串:__doPostBack,因為它出現在你寫的每一個 .aspx 頁面的瀏覽器源代碼中:
<script type="text/javascript"> //<![CDATA[ var theForm = document.forms['form1']; if (!theForm) { theForm = document.form1; } function __doPostBack(eventTarget, eventArgument) { if (!theForm.onsubmit || (theForm.onsubmit() != false)) { theForm.__EVENTTARGET.value = eventTarget; theForm.__EVENTARGUMENT.value = eventArgument; theForm.submit(); } } //]]> </script>
在 WebForms 中,整個頁面就是一個表單,所以早期的微軟工程師大搖大擺的定義了一個全局變量:
var theForm = document.forms['form1'];
__doPostBack 函數則是對頁面上這個唯一的表單提交( theForm.submit),傳入的 eventTarget 和 eventArgument 分別用來標識本次回發的觸發控件以及回發參數。
這些都沒啥,關鍵是頁面上永遠都有一個 ViewState 隱藏字段:
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTQyNjcyNDU5MWRkpK.....aycxe0NfpWe+PGI0=" />
這里面存放了頁面上所有控件的狀態信息,比如下拉列表的數據,表格的數據,輸入框的數據等等,所有這個字段一般會比較大,導致上傳的數據量動輒就有20K~1000K。在網絡有限的情況下會非常影響性能,從而給人了臃腫的印象。
比如在 FineUI(開源版)中一個包含表格的頁面:
http://fineui.com/demo/iframe/grid_iframe.aspx
頁面回發時,請求的數據量就達到 19802 bytes = 19K
ASP.NET MVC中的回發(BegionForm)
在 ASP.NET MVC 中,我們可以定義多個表單,從而自行控制需要提交的表單,以及表單中字段。也就是只提交我們需要的數據,這樣不僅靈活而且上傳數據量不會很大。
在 MVC 的其他文檔中,你可能很少會看到 回發 這個字眼,很多是這樣描述的:提交某個表單到控制器的某個操作方法,或者說發起一個 HTTP POST 請求。其實這些都對應於 WebForms 中的回發字眼,只不過操作的表單和表單字段不同而已。本質上還是一樣的。
在我之前寫MVC系列教程中有一個典型的回發過程:【第四篇】ASP.NET MVC快速入門之完整示例(MVC5+EF6)
@using (Html.BeginForm()) { @Html.AntiForgeryToken() <p> 所學專業: @Html.DropDownList("Major", ViewBag.MajorList as IEnumerable<SelectListItem>, "全部") 姓名: @Html.TextBox("Name") <input type="submit" value="檢索" /> </p> }
通過 Html.BeginForm 輔助方法來生成一個表單,這個表單會提交到當前頁面對應的控制器方法,默認使用 POST 請求,生成到頁面的HTML結構類似:
<form action="/Students/Create" method="post"> </form>
表格里面,明確定義了兩個表單字段,分別是 Major 和 Name,以及一個提交按鈕(type=submit)。
對應的后台控制器方法類似:
[HttpPost] [ValidateAntiForgeryToken] public ActionResult Index(string Major, string Name) { // ... return View(students.ToList()); }
在這個過程中,雖然我們可以控制要提交的表單,以及提交哪些參數,但這個過程還是整個頁面提交。
而 FineUIMvc 中,我們不僅不需要定義表單(只需要告訴FineUIMvc需要提交哪些參數即可),而且所有的提交都是AJAX過程。
FineUIMvc中的回發(OnClick、OnPageIndexChanged)
FineUIMvc中將對控制器方法的調用放到每個具體的控件中, 對應於 FineUI(開源版)中控件的事件。
無參數回發
按鈕的點擊事件:
@(F.Button() .OnClick(Url.Action("btnHello_Click")) .ID("btnHello") .Text("點擊彈出對話框") )
這樣就將按鈕的客戶端點擊事件(click)和服務器端控制器的方法(btnHello_Click)關聯起來,而且命名也和WebForms中的一模一樣,是不是倍感親切。
仔細觀察下這個 HTTP 請求,我們就能知道這個客戶端點擊事件將去向何方:
從這張圖上,我們有如下收獲:
- Basic是區域名稱(Area),Hello是當前控制器名稱
- btnHello_Click是點擊按鈕時對應的控制器方法
- 請求方法是 POST
- X-Requested-Width: XMLHttpRequest,表明當前請求是AJAX
再來看下HTTP請求正文數據:
只要一個防止跨站請求偽造(CSRF)的參數_RequestVerificationToken,再無其他參數。
對應的控制器方法:
[HttpPost] [ValidateAntiForgeryToken] public ActionResult btnHello_Click() { Alert.Show("你好 FineUIMvc!", MessageBoxIcon.Warning); return UIHelper.Result(); }
因為在這個過程中,后台無需參數,所以前台也沒必要傳入任何數據。這樣一個AJAX回發過程就非常干凈,不像之前的WebForms一樣,需要傳遞一堆參數了。
帶參數回發
來看下表格的數據庫分頁事件,在數據庫分頁時后台C#代碼需要知道兩個參數:
1. 當前的頁碼
2. 表格中用到了哪些數據庫字段,即使熟練的WebForms開發人員可能也不會意識到這一點,因為在WebForms中后台能夠知道控件的所有參數,而MVC中回發時,你對表格的任何參數一無所知,所有你需要的參數都需要通過前台傳入
FineUIMvc對於帶參數的回發回發進行了深度優化,你根本無需自己通過JavaScript來獲取這些參數(當然你也可以這么做,只要你願意),而是指定表格的ID即可:
@(F.Grid() .EnableCheckBoxSelect(true) .Width(850) .ShowHeader(true) .ShowBorder(true) .EnableCollapse(true) .Title("表格") .ID("Grid1") .DataIDField("Id") .DataTextField("Name") .AllowPaging(true) .PageSize(5) .IsDatabasePaging(true) .OnPageIndexChanged(Url.Action("Grid1_PageIndexChanged"), "Grid1") .Columns( F.RowNumberField(), F.RenderField() .HeaderText("姓名") .DataField("Name") .Width(80), .... ) .RecordCount(ViewBag.Grid1RecordCount) .DataSource(ViewBag.Grid1DataSource) )
注意 OnPageIndexChanged 的第二個參數,這樣在發起對控制器方法(Grid1_PageIndexChanged)的POST請求時,會自動附加所需的參數:
對應的控制器方法:
[HttpPost] [ValidateAntiForgeryToken] public ActionResult Grid1_PageIndexChanged(JArray Grid1_fields, int Grid1_pageIndex) { var grid1 = UIHelper.Grid("Grid1"); var recordCount = DataSourceUtil.GetTotalCount(); // 1.設置總項數(數據庫分頁回發時,如果總記錄數不變,可以不設置RecordCount) grid1.RecordCount(recordCount); // 2.獲取當前分頁數據 var dataSource = DataSourceUtil.GetPagedDataTable(pageIndex: Grid1_pageIndex, pageSize: 5, recordCount: recordCount); grid1.DataSource(dataSource, Grid1_fields); return UIHelper.Result(); }
小結
WebForms中的回發由於需要附加上ViewState而略顯臃腫;
ASP.NET MVC原生的回發需要借助Html.BeginForm輔助方法來生成單獨的表單,並把需要提交的參數放置到表單中,回發過程是整個頁面提交;
FineUIMvc對回發過程進行深度優化,無需創建表單,只需要提供需要回發的參數,而且回發過程是AJAX的。對於部分控件比如表單和表格,甚至不需要指定回發參數,只需要設置控件ID即可,非常方便。
如果你還對WebForms中的回發念念不忘,那就無需忘卻。