應用程式框架實戰二十八:前端框架決擇
對於BS管理系統,我很長一段時間都工作在Asp.Net Web Form上,Web Form的主要優勢是可以使用伺服器端控制元件,以類似CS的開發模式進行工作,通過拖拽控制元件和定義事件處理函式,極大的簡化了BS的開發。伺服器端控制元件會在渲染階段把自身輸出為Html標籤,對我們完全透明,當需要設定相關屬性時,只需要在屬性面板上操作即可。
Web Form在誕生之時,Ajax還未流行,所以頁面的提交是完全重新整理方式。為了記住控制元件的狀態,Web Form引入了ViewState和回發機制。對於一些複雜的介面,可能建立大量的服務端控制元件,檢視ViewState序列化生成的hidden,有時候達到數百K。回發機制也讓人鬱悶,手工編寫的Js與回發機制不能很好的配合,經常發現程式碼的執行時機不對。Web Form服務端控制元件生成的Html也相當雜亂,基本是面向機器的。
當然這些問題很多時候是自己的使用方式不對,不能全賴到Web Form上,但大家希望.Net能夠提供更好的解決方案。
Ajax逐漸流行,對BS的要求大幅提升,比如區域性重新整理,乾淨整潔的Html等。一些 RIA框架發展起來,比如Ext、EasyUi、Dwz等。
微軟也推出了輕量級的Mvc框架,我是在Mvc 3 釋出時才開始使用的,主要是被Razor檢視引擎吸引過來。為了提升使用者體驗,還選擇了一款RIA框架,當時選擇了Dwz,主要是它開源免費,而其它框架要麼閉源,要麼收費,或者太複雜了,學習成本高。
在使用了幾年Dwz之後,最近決定更換一個前端框架,下面談談使用Dwz的一些情況。
Dwz作為一款國產免費的開源框架是值得支援的,官網http://www.j-ui.com。通過Dwz,我學習到大量Js和Css的相關知識,在此表示感謝。
Dwz基於JQuery,設計以Html擴充套件為主,對SPA (single page applications, 單頁應用程式)有很好的支援,API命名規範,程式碼質量較高。
使用中碰到的問題也不少,首先是缺乏專業的文件,官方只提供了一個很簡單的使用手冊,每當碰到問題,我總是進入dwz原始碼斷點除錯,不過這樣更能深入學習內部機制,似乎也不算嚴重的缺點。
從我開始使用Dwz,好像就沒見更新過。
Dwz有些元件不夠完善,比如彈出模態視窗,只支援彈出一層,如果在彈出的模態視窗上繼續彈出模態窗就不支援了,而這屬於常用功能,對於這些小功能,我自己修改,咬咬牙也還是可以搞出來。
對於一些重量級的元件,比如表格、樹等,Dwz提供的比較弱,我一般都引入第三方外掛,比如ZTree。
還有一個頭痛的問題是佈局,Dwz沒有提供border或fit這樣易用的佈局方式,除了面板和Tabs等常規佈局元件外,只有一個神祕的layoutH屬性,它的值是一個數字,表示工具欄的高度,對於一個複雜點的佈局,我經常東調西試以找出這個正確的數字。當然這也只能怪我水平有限,掌握不夠好,所以引入一個更強大的前端框架就迫在眉睫。
對於大名鼎鼎的Ext,我已經關注它很長時間,不過一直沒敢使用它,有幾個原因。使用純Js開發介面,有點違反一般程式設計師的習慣,大部分程式設計師還是喜歡用Html佈局。另一個原因是面向物件的Js要求比較高,API豐富而龐大,學習成本高。當然最重要的一個原因是,對於管理後臺這樣的系統,大量手寫Js開發效率低,且Js是弱型別語言,程式碼提示很弱,且沒有編譯時檢查,容易出錯,健壯性差。所以哪怕要使用Ext,我也會用C#來包裝一次,這個工作已經有人做了,這就是Ext.Net。十分遺憾的是,Ext.Net不是開源的,而且還收費,它甚至把js等資源內嵌到dll中,另外還不支援DataAnnotations驗證,這讓我打消了使用它的念頭。
Ext也是要收費的,但有一個版本2.0.2可以免費使用,我的想法是,如果做小專案,就用高版本,偷偷的用估計也沒人知道,如果需要公開使用,就切換到2.0.2這個版本。通過C#建立一個抽象機制,不僅可以簡化開發,而且可以方便切換版本。我在嘗試了一段時間後,發現封裝的工作量很大,所以暫停了,待以後確實需要的時候再繼續。
目光轉到EasyUi,EasyUi傳說也是國人開發的,不過其官網卻是純英文。對於EasyUi,我幾年前也曾瞭解過,當時認為沒有原始碼,萬一出現bug不是束手就擒嗎。雖然如此,我卻發現它越來越流行了,部落格園搞框架的十有八九都是用的EasyUi,周邊也有很多公司在使用,甚至在招聘要求上,我也看到過要求有EasyUi的經驗。這說明EasyUi的穩定性還是有保證的,大家用都沒問題,難道我的運氣就那麼背。
除了跟風以外,我選擇EasyUi還有幾個重要原因,首先是功能比較強大,重要的複雜元件和佈局元件都提供了,雖然沒有Ext那麼完善,但也基本夠用。其次是學習成本低,EasyUi也是基於jQuery,而且支援Html擴充套件。最後是官方文件比較齊全。
可以看到,EasyUi的功能、文件、易用性等介於Dwz與Ext之間。
還有一些朋友給我強力推薦Bootstrap,不過我感覺它有點輕量,管理系統需要更重口味的框架,開發類似會員後臺的時候再考慮採用Bootstrap。
我學習EasyUi還不到一個月,很多東西仍處於摸索中,EasyUi雖然比較強大,但還是發現不少問題。
首先是它的方法呼叫方式讓人很鬱悶。比如我現在要關閉一個視窗,需要這樣呼叫$('#xxx').dialog('close'),為什麼不能這樣呼叫$('#xxx'). close ()。還好我主要使用Html擴充套件方式,手工編寫Js僅用來處理回撥,這個問題可以忍忍。
其次發現不少小bug,比如多行文字框無法回車換行,時間控制元件在某些時候點選時報物件為null的錯誤等等,我使用的是IE 11,估計作者還沒有對IE 11進行全面測試,希望能及時更新。
EasyUi雖然是一個比較完善的前端框架,但並不意味著不需要花任何力氣,你就可以開發出健壯的應用。下面討論兩個重要的設計決策。
SPA還是IFrame
SPA,全稱Single Page Applications, 即單頁應用程式。它的設計理念是僅在主框架介面使用一個完整的Html頁面,其它所有內容頁面都是Html片斷,沒有html、head、body這些標籤,主框架介面通過ajax的方式載入內容頁面。
SPA是正宗的Ajax應用模式,並且逐步成為Ajax應用的趨勢。它的優點顯而易見,所有東西都在同一個頁面,查詢任何元素,直接用jQuery選擇器就行了。
任何事物都有兩面性,SPA也有很多缺點,最嚴重是命名衝突和相容性。
對於同一個Html頁面,如果兩個元素的id出現重複,當你用css選擇器進行格式化,或用jQuery選擇器對其操作時,就會發生意想不到的情況。你會發現,操作某個內容頁面時,居然影響到另外一個不相干的內容頁。
jQuery解決這種命名衝突,是通過傳入一個額外的上下文物件,比如$(“#xx”,context)。Context代表某個內容頁,這樣就可以僅查詢該內容頁的id,從而消除了命名衝突。
對於SPA,Dwz提供了天然的支援,它封裝了CRUD相關的所有操作,並提供了一個當前上下文context來儲存當前操作的內容頁或彈出視窗。
雖然Dwz提供了SPA支援,但我在使用中,依然發現偶爾出現各內容頁互相影響的情況,為了防止命名衝突,我將id命名得很複雜,以減少衝突。
再看EasyUi,對於CRUD操作,只在官網找到一個很簡陋的Demo。仔細研究了Tabs等元件後,感覺EasyUi預設支援的是SPA模式,因為這些元件都沒有對IFrame進行支援。EasyUi既然支援的是SPA模式,但卻沒有做進一步的封裝,可以斷定,以SPA模式使用EasyUi,命名衝突是比較嚴重的。
還有一個頭痛的問題是相容性,由於所有內容頁面在同一個Html中,如果某些頁面需要引用一個第三方外掛,而這個外掛不是基於jQuery的,或者jQuery版本不同,引入這些外掛可能失敗。
基於以往的經驗,我決定不在管理系統這種複雜的應用中使用SPA,而是使用IFrame的方式載入內容頁。
由於EasyUi沒有對IFrame提供支援,當我向主介面的Tabs引入IFrame後,引發了一大堆連鎖問題,我花了大把時間終於把這些問題解決了,後面將用專門的文章來介紹碰到的障礙。
封裝有無必要
大部分使用Mvc的朋友,都是從Web Form轉過來的。所謂一遭被蛇咬,十年怕井繩,由於在Web Form上吃過過度封裝的苦頭,來到輕量級的Mvc世界,他們害怕伺服器端的任何東西,只敢使用原生的html和js了。
Mvc提供了一套Html表單控制元件的封裝,比如文字框,@Html.TextBox( "id" ),它等價於<input type="text" name="id"/>。從這個簡單的例子,好像使用伺服器端語法優勢並不明顯。但值得注意的是,伺服器端語法能夠將DataAnnotations驗證自動轉化為jQuery驗證,這一點還是比較強大的。
對於簡單的html標籤,你是否使用mvc的服務端語法不是特別重要,但對於像Dwz或EasyUi這種Html擴充套件為主的前端框架就非常必要了。
EasyUi將每個元件的屬性都擴充套件到了Html標籤上,對於Html標籤,你還能指望程式碼提示嗎?沒有程式碼提示,就意味著你得隨時開啟EasyUi的官網,以複製相關的屬性。當然你很多時候會自己手工輸入,你必須把這些API記得非常精確,如果多一個或少一個字元,你就會得到錯誤的結果。如果你沒有遵循敏捷開發的小步前進,而是把整個頁面輸入完成才開始執行,在密密麻麻的Html標籤中找出輸入錯誤的屬性也不是一件容易的事。
如果把EasyUi的屬性用C#封裝起來,由C#來輸出Html標籤,你就可以完全不再操心API的問題,所有的API只需在程式碼提示中上下移動即可。可以看到,Web Form的很多思想其實是非常好的,比如用服務端程式碼輸出客戶端程式碼,你應該去其糟粕,取其精華。
對前端框架的封裝,真正強大的地方來自Lambda表示式。
從Lambda表示式中,你可以獲取到一些元資料資訊,比如name、value、驗證資訊,一個簡單的操作@Html.EasyUi().TextBox( t => t.Name ),可能設定了10幾個屬性,並且所有的屬性均來自伺服器端,這樣維護也更加方便了,你不用來回修改客戶端程式碼。
Lambda表示式還可以獲取到屬性的型別,這有什麼用呢?當你寫上一句@Html.EasyUi().TextBox( t => t.XXX ),如果XXX是整型,文字框就自動轉成EasyUi的數字文字框,只能輸入數字,如果它是一個日期,文字框就顯示成一個日期控制元件。再比如@Html.EasyUi().Combox( t => t.XXX ),當它是布林型別,就顯示一個是、否的下拉列表,如果它是一個列舉就自動繫結一個列舉值的下拉列表,這是不是比你手工輸入要強些呢。
結語
本文簡要介紹了我對幾個前端框架的認識,也說明了封裝的必要性。
由於我也是EasyUi初學者,我提供的Demo並不是一個可以直接使用的框架,不過作為學習範例,你可以把它作為你的應用程式框架的起點。
我後續文章會逐步介紹Demo中各構造塊的封裝和使用要點,所有程式碼以我最新發放的Demo為準。
為了不沖淡本系列主題,我會專門為EasyUi框架的封裝建立一個系列。
對於EasyUi的封裝,我並沒打算封裝它的全部內容,畢竟我不是靠這個吃飯的,我僅在開發時碰到需求才會進行擴充套件,我的Demo會隨著我的開發逐步增強,我會定期發放最新原始碼。
Demo已更新,需要的老規矩,點推薦,留Email,本次截止2015年1月28日15點。
另外大家短時間沒收到原始碼不要著急,只要在規定時間內都可以拿到。
.Net應用程式框架交流QQ群: 386092459,歡迎有興趣的朋友加入討論。