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

浙公網(wǎng)安備 33010602011771號