所謂顯而易見的代碼,就是看上去和別處相同的代碼。javascript
在這個例子中,就是View‘中初始頁面顯示的內容與將來刷新的內容重複;Controller中初始顯示的運算和刷新的相同。css
Controller好辦,如此:html
- print?private void PrepareAssignItemsData(int sprintID)
- {
- var sprint = ...
- var team = ...
- var overTimes = ...;
- var itemsTreeInSprint = ...
- ViewBag.AssignItemsViewModel = ...
- ViewBag.ViewModel = ...
- }
- public ActionResult AssignItems(int sprintID)
- {
- PrepareAssignItemsData(sprintID);
- return View("~/Areas/SFC/Views/Items/ItemTree.cshtml");
- }
- public ActionResult AjaxRefreshAssignItemsLeftPad(int sprintID)
- {
- PrepareAssignItemsData(sprintID);
- return View("~/Areas/Agile/Views/PlanningMeetings/AjaxRefreshAssignItemsLeftPad.cshtml");
- }
而cshtml頁面中,只須要添加一個在頁面初始化時自動刷新的函數,就能把<div id = "leftpad">變成空的,由刷新頁面來填充。代碼變成:java
- [html] view plaincopyprint?<script type = "text/javascript">
- $(document).ready(function () {
- refreshLeft();
- });
- function refreshLeft() {
- $("#refreshLeft").click();
- };
- </script>
- <div style = "display: no1ne">
- @SFCUI.Link("refresh", "/Agile/PlanningMeetings/AjaxRefreshAssignItemsLeftPad?sprintID=" + assignItemsViewModel.Sprint.ID, ajaxUpdateTargetID: "leftpad", ajaxOnSuccess: "refreshAll", id: "refreshLeft")
- </div>
- <div id = "leftpad">
- </div>
$(document).ready是多出來的代碼,負責第一次初始化時填充<div id = "leftpad>的內容。程序員
何爲多餘的技術代碼?以前在AjaxValue系列中曾經提到過接口封裝的兩個原則:ajax
最小信息原則:方法接口應只傳遞最必須的業務信息。編程
包括兩個層面:dom
1. 技術數據不要傳遞ide
2. 業務數據不能重複函數
如今,先從業務角度分析,這個函數接口設計中,到底哪些信息是必須的;讓咱們來用這一原則,把上面最後的一段cshtml代碼,變成一行代碼。
1. 若是要刷新,應該調用什麼函數(通常是一個JS函數,提供給左邊的紅框,紅框運行成功,就調用這個JS函數)
2. 用於刷新頁面的Ajax Url(上述JS函數被調用後,應該到哪一個Url獲取刷新內容)
3. 刷新成功後,要繼續執行什麼操做(另一個JS函數,好比刷新的內容「錯過了document.Ready」要從新套用一下——嘗試了live功能,不知道爲何無效;或者要串聯刷新多個區域,本例中沒有)
沒了(後面還會看到一些,不過是爲了別的功能)。有幾個東西是多餘的:
1. $(document).ready.....,由於每一個AjaxPageLoad都執行,因此是固定的,不用做爲參數傳入接口。
2. function refreshLeft() ...{ $"#refreshLeft")....,由於這個按鈕是多餘的,並不須要人手去點擊,只是函數實現中的一個步驟而已。換言之這個按鈕叫什麼都無所謂,只要ready / function / SFCUI.Link(id: ...)中出現的值相同就行,無需人工指定。
3. <div id = "leftpad"></div>也是多餘的!由於既然在這裏寫下那一行代碼,就在代碼處LoadPage,至於Load到什麼東西里邊,ID叫什麼,都無所謂。
因此,接口調用應該是:
- print?@SFCUI.AjaxLoadPage("/Agile/PlanningMeetings/AjaxRefreshAssignItemsLeftPad?sprintID=" + assignItemsViewModel.Sprint.ID, refreshFunction: "refreshLeft", ajaxOnSuccess: "refreshAll")
這句話是一個helper,將負責生產前面代碼中提到的<script>及其中的兩個函數ready和refreshLeft、中間的<div>@SFCUI.Link中的Ajax調用</div>、最後的<div id = "leftpad">
下面是Helper的源代碼,裏邊多了一些參數,最後解釋:
- print?public const string AJAX_LOAD_PAGE_CLASS = "ajaxloadpage";
- private static Random _rand = new Random();
- public static MvcHtmlString AjaxLoadPage(string pageLink, string refreshFunction = null, string ajaxOnSuccess = null, string style = null, int timeout = 0)
- {
- int id = _rand.Next();
- string html = SFCUI.Link("refresh", "/SFC/Ajax/AjaxLoadPage?pageLink=" + HttpUtility.UrlEncode(pageLink), ajaxUpdateTargetID: id.ToString() + "Body", ajaxOnSuccess: ajaxOnSuccess, cssClass: "hide " + AJAX_LOAD_PAGE_CLASS + " ", id: id.ToString()).ToString();
- TagBuilder script = new TagBuilder("script");
- script.MergeAttribute("type", "text/javascript");
- script.InnerHtml = "$(document).ready(function () { setTimeout(function () { $(\"#" + id.ToString() + "\").click(); }, " + timeout + "); });";
- if (refreshFunction != null)
- script.InnerHtml += "function " + refreshFunction + "() { $(\"#" + id.ToString() + "\").click(); };";
- html += script.ToString();
- TagBuilder pageContainer = new TagBuilder("div");
- pageContainer.MergeAttribute("id", id.ToString() + "Body");
- pageContainer.MergeAttribute("style", style);
- html += pageContainer.ToString();
- return new MvcHtmlString(html);
- }
1. 先看忽然跳出來的int id = _rand.next()
以前提到,咱們有不少「隨便」的變量,好比那個"refreshLeft",叫什麼都行,外界不關心。可是若是設爲常數,則若是在同一個頁面上放兩個LoadPage的時候,會打架,因此用個Random生成ID。爲何用static 的呢?由於Random的生成機制,是利用調用時的系統時間做爲「種子」,從種子產生下一個隨機數。若是調用時間間隔爲「0」,就會產生出相同的種子進而相同的隨機數。static就沒有這個問題了,你們用一個,順着排。
總之,隨機的id代替了"refreshLeft"這個原本須要傳進來的「變量」。
2. TagBuilder script則直接在代碼上方產生一段script,免去了編寫script的工做。
爲何要判斷refreshFunction爲NULL的狀況呢?由於有一種場景不會重複刷新。
在個人項目中,菜單極其複雜,產生菜單的時間,甚至比頁面自己都長。但咱們也不像犧牲強大的菜單換取性能,怎麼辦呢?子菜單延時產生!
因爲人們在頁面出現後的1~5秒內都不會操做菜單(要離開這個頁面時纔會操做),因此咱們設置全部繁重的子菜單都使用AjaxLoadPage加載,並且注意最後一個參數timeout,它們多數在1000毫秒後才加載,那時候頁面早就爽快地展現在用戶面前了。
這種場景,不會有人刷新菜單了,因此就不用RefreshFunction這個參數。
3. TagBuilder pageContainer 代替了原來最後的<div id = "leftpad">,它的id也是隨機生成的,可是因爲一、二、3中保持了id的互相照應,雖然外界看不到,可是內部卻順暢運行。
4. 最後多了個style,是咱們加載菜單時的須要,這裏就很少說了。
合併顯而易見的代碼是初級程序員的基本功,也是產品代碼的基本要求;封裝多餘的技術代碼難度較大,可是隻要用心,上述提到的原則也沒有作不到的。
爲何要費勁封裝呢?散裝的代碼對程序員要求低,只要好好測試,功能相同,也不會出現缺陷,不也同樣嗎?
在16年的IT從業中我發現,全部最後開發面臨崩潰的軟件,不多有受到單個技術難題或缺陷困擾的,多數都百病纏身;而百病纏身的主要問題,是可維護性差;而可維護性差的主要緣由,是代碼臃腫重複,尤爲是似重複而不重複。
封裝後,則:
1. 老是使用同一段代碼,易於維護。
2. 老是重複使用,代碼的質量有保障。
3. 一個地方發現缺陷,修改代碼後能夠同時避免多個地方的缺陷。
4. 在無需深刻到技術層面的時候,能夠方便地在業務層面閱讀代碼。
5. 把時間花費在深究代碼的封裝上,比重複碼字要有趣得多。
……
在以前的IT職業生涯的「危險職業」系列中有不少人提到:「我一直在作重複勞動,沒有積累,是否是很危險?怎麼辦?」其實萬事萬物看似重複,其實不重複。本人從事Web編程只有一年多(其中只有6個月的編程量超過總工做量的50%),Jquery上上個月剛大體弄明白,可是我相信不少Web編程好久的程序員都不會封裝這些代碼的,而是「重複地」拷貝粘貼代碼。
因此實際上,沒有重複的工做,只有重複的工做心態。
以後我會寫一篇關於「重複勞動中如何提升」的IT職業生涯系列文章。