標籤:winform style blog code java ext
這篇是我參加QCon北京2014的演講內容:
提綱:
公司專屬應用程式在軟體行業中佔有很大的比重,而這類軟體多數現在也都採用B/S的模式開發,在這個日新月異的時代,它們的前端開發技術找到了什麼改進點呢?
B/S企業軟體前端開發模式大體上與案頭軟體類似,都是偏重量級的,在前端可能會有較多的商務邏輯,這些商務邏輯如何被合理模組化,與介面分離,以便測試,成為這個領域的一個重要挑戰。另一方面,由於公司專屬應用程式的介面相對規整,偏重的是資料存取,沒有太多花哨的東西,所以常見的介面控制項也是可枚舉的,如何讓開發介面的工作能更快完成,甚至由不擅長編寫代碼的業務設計人員來做,與介面原型的工作合二為一,能提高不少開發效率。
在AngularJS等MV*架構出現之後,給這個領域帶來一些契機,架構師們能夠有機會去重新規劃前端的架構,甚至是開發流程,從而讓整個軟體的生產更為高效。
本文將探討它給這個領域帶來的變化。
本文:公司專屬應用程式前端的特點
公司專屬應用程式系統是一種很常見的軟體系統,這類系統的特點是面向某個行業,功能較複雜,對介面的要求一般是整齊,不追求花哨。這類系統通常有C/S和B/S兩個流派,其中的B/S方式因為部署和整合的便利,使用得較為普遍。
同樣是在瀏覽器中做東西,寫公司專屬應用程式和網站的差別也很明顯。公司專屬應用程式的商務邏輯較重,前端有一定的厚重性,但是對效果並不追求很多,主要是各類控制項的使用,表單的存取值等等。
公司專屬應用程式產品的一些特點如下:
一般使用者使用互連網產品,都是片段時間使用,比如購物或者閱讀,做完之後就重新整理或者關閉瀏覽器了,而公司專屬應用程式往往是工作的全部,從早上上班開始開啟,到下班才關掉,一天絕大部分工作都在上面完成,比如一個話務中心的操作員。
公司專屬應用程式對視覺的追求是比較低的,一般不會要求花哨效果,以業務操作的流暢性為第一目標。
公司專屬應用程式的介面布局相對有模式可循,可以用很少的情境來窮舉,介面橫平豎直,比較規整,使用到的控制項元素也是可窮舉的,基本沒有什麼特效。
由於公司專屬應用程式的使用者都相對比較專業,在上崗之前需要經過統一培訓,而且每個使用者使用的頻度較高,很多時候他們會用盡量快捷的方式來做操作,比如鍵盤,這一點在互連網產品中比較少見。所以,有時候大家為了追求好看,把系統原生的select用div來替換,在這種情況下反而增加了使用者的麻煩。
我之前所在的行業中,商務邏輯很複雜,前端可能會需要寫很多複雜的邏輯,JS代碼大部分是在處理邏輯,而不是介面互動。
互連網產品往往很重視首屏最佳化,但是其策略可能與公司專屬應用程式不同。比如說,3個200k的模組,在網站型產品中可能最佳化成一個100k加三個150k的模組,但在公司專屬應用程式中,很可能最佳化成一個400k加三個50k的模組。為什麼會這樣呢?因為內容型的網站講究的最佳化策略是分攤,如果首次載入太慢,會很影響使用者的信心,但公司專屬應用程式使用者的容忍度是較高的,他並不在乎剛開啟的時候慢一些,因為開啟了之後就要用一天,對於之後每步操作的模組載入速度倒是要求很高。另外,對於記憶體泄露的處理,也要求得比較高一些。整個這些策略,其實是來源於C/S系統的影響。
很多時候提到公司專屬應用程式,大家的想法就是低端,IE6,但其實這個的原因是客戶只購買軟體,營運一般自己做,每年不會有很多持續的投入來改進,所以導致很多老系統不能持續升級。軟體廠商其實反倒可以用更激進的策略去升級瀏覽器,使用者對這個的接受度還是比較高的,使用系統的群體也是比互連網使用者小很多的,拋棄老舊瀏覽器的事情也確實可以幹,比如我就見過幾年前某電信營業系統預裝的都是Firefox。
公司專屬應用程式常見的前端架構
在開發B/S公司專屬應用程式前端的人群中,有很大一部分群體選擇了服務端的組件化方式,比如JSF之類,它的弊端是與異構服務端的第三方系統整合比較麻煩。也有不少人使用Bindows和ExtJS這樣的架構,最近的KendoUI也是個不錯的選擇。
每種類型選一個有代表性的來說說:
早期有些團隊採用的方式,一般會跟XMLHTTP等結合使用,便於使用,介面代碼整潔,但已被主流瀏覽器拋棄。
以後端為主的架構師最推崇的方式,受Struts的MVC模型影響很深,弱化了前端,使得前端蛻化為後端的一種附屬。
寫其他語言來產生HTML和JS,一般會依賴於一種前端UI庫。這種方式也比較受後端架構師喜歡,因為他們覺得寫JS很頭疼,寧可寫Java。
- ExtJS 用JS封裝介面組件,乾脆就不要HTML了
這是另外一種極端,從Bindows開始,使用純邏輯代碼來描述介面,走著跟Java Swing一樣的道路,也有不少人喜歡。但這種方式在沒有好用的介面設計器的情況下非常痛苦。
這條路其實是對Java Applet的一種延續,好處是可以不受HTML體系的制約,獨立發展,所以其實這些體系在公司專屬應用程式領域的成熟度等級遠超HTML體系。
曾經的企業B/S應用幾件寶
有一段時間,我們幾乎只有IE6,所以那個時候的前端開發人員很快樂,沒有相容的壓力。那時候,我們如何構建前端應用呢?
參見http://weibo.com/1858846672/B1fL3vuYN?mod=weibotime
這是最好用的聲明控制項的方式。
儘管還沒有AJAX的概念,但我們已經可以用它做前後端分離的傳輸機制了。
在IE裡面畫向量圖,不使用外掛程式,有其他選擇嗎?
把XML資料轉換成HTML,跟現在的前端模板像嗎?
建立右鍵菜單最好的方式。
用這些技術構建的一個典型公司專屬應用程式
單頁應用和前端分層
當時這些系統的構建方式也可以算單頁應用,我們用iframe來整合菜單,每個菜單有自己獨立的功能,整個主介面是始終不會重新整理的。
時光飛逝,這些年,前端有了什麼本質的改變,產生了翻天覆地的變化嗎?
有時候我們回顧一下,卻發現多數都是在增加完善一些細節,真正有顛覆性的有比如以RequireJS和SeaJS為代表的模組定義和載入庫,npm這樣的包管理器,grunt,gulp,百度fis這樣的整合開發模式。為什麼它們算是本質改進呢?
因為這些標誌著前端開發從粗放的模式,逐漸層化到精確控制的形態。比如我們再也不能不管代碼的依賴關係,也不能一開啟介面就不分青紅皂白把所有可能要用到的代碼都立刻載入過來,那個時代已經過去了,從任何角度講,現代的前端開發都在精細化,從代碼的可控,到介面體驗的精細最佳化,到整個團隊甚至公司甚至互連網上的組件共用,以及前端團隊協作流程的改進,這已經是一個很成規模的產業了。
我們把眼光放到2013年,在這一年裡最火的前端技術莫過於NodeJS和AngularJS,前者給我們帶來的是一種開發方式的改變,後者是一種典型的前端分層方案。Angular是前端MV*架構的一個流派,用過的人都會覺得很爽。它爽在什麼地方呢?因為它幫我們做的事情太多了,一個雙向繫結,無所不包,凡是存取值相關的操作,基本都不用自己寫代碼。在公司專屬應用程式前端功能裡,表單的存取值和校正佔據了很大的比例,這些事都不用幹了,那簡直太好了。
如果就因為這個用Angular,那還有些早。有一些第三方代碼被稱為庫,另外一些稱為架構,Angular是架構而不是庫。架構的含義是,有更強的約束性,並非作為協助工具功能來提供的。
先看一下公司專屬應用程式的通常形態吧,會有一個可配置的菜單,然後多半會採用MDI的形式,能開啟多個業務功能,用選項卡的形式展示起來,可以隨時切換操作。每個人每天常用的功能是可以窮舉的,他進入系統之後,一般要用到下班才關掉。所以這種系統非常適合做成單頁應用,開始的時候載入一個總體架構,每點擊一個菜單,就載入這個菜單對應的功能模組,放在一個新的選項卡或者別的什麼地方展示出來。
在早期做這種系統的時候,一般都會用iframe來整合菜單,這種方式很方便,但是每個菜單頁都要載入共同的架構檔案,初始化一個環境,資料之間也不能精確共用。
所以現在我們做公司資訊系統,不再適合用iframe來整合菜單,所有菜單的業務代碼,會在同一個頁面的範圍中共存。這在某些方面是便利,比如資料的共用,一個選擇全國城市的下拉框,在多個功能中都存在,意味著這些城市的資料我們可以只載入一次。但從另外一個角度來說,也是一種挑戰,因為資料之間產生幹擾的可能性大大增加了。
我們回顧一下在傳統的用戶端開發中是怎麼做的,早在經典的《設計模式》一書中,就提到了MVC模式,這是一種典型的分層模式。長期以來,在Web開發人員心中的MVC,指的都是Struts架構的那張圖,但我們單頁應用中的MVC,其實更接近最原始的《設計模式》書中概念。所以我們要在前端分層,而不僅僅把整個前端都推到視圖層。
做單頁應用,前端不分層是很難辦的,當規模擴大的時候,很難處理其中一些隱患。分層更重要的好處是能夠從全盤考慮一些東西,比如說資料的共用。跨模組的資料共用是一個比較複雜的話題,搞得不好就會導致不一致的情況,如果考慮到在分層的情況下,把各種資料來源都統一維護,就好辦多了。
所以,以AngularJS為代表的前端MV*架構最重要的工作就是做了這些對於分層的指導和約束性工作,在此基礎上,我們可以進一步最佳化單頁應用這類產品。
前端的自訂標籤體系
構建一個大型公司專屬應用程式,最重要的是建立整套組件體系。一般針對某行業的軟體,長期下來都會有很多固定的模式,可以提煉成組件和規則,從前端來看,體現為控制項陳列庫和前端邏輯。控制項陳列庫這個是老生常談,在很多架構裡都有這個概念,但各自對應的機制是不同的。
從寫一個介面的角度來講,最為便利的方式是基於標籤的聲明式代碼,比如我們常見的HTML,還有微軟的XAML,Flex中的MXML等,都很直接,設想一下在沒有可視化IDE的情況用類似Java Swing和微軟WinForm這樣的方式編寫介面,毫無疑問寫XML的方式更易被接受。所以,我們可以得出初步的結論,介面的部分應該寫標籤。
很遺憾,HTML內建的標籤是不足的,它有基本表單輸入控制項,但是缺乏DataGrid,Tree之類更富有表現性的控制項。所以絕大多數介面庫,都採用某種使用JavaScript的方式來編寫這類控制項,比如:
<div id="tabs"> <ul> <li><a href="#tabs-1">Nunc tincidunt</a></li> <li><a href="#tabs-2">Proin dolor</a></li> <li><a href="#tabs-3">Aenean lacinia</a></li> </ul> <div id="tabs-1"> </div> <div id="tabs-2"> </div> <div id="tabs-3"> </div></div>
$(function() { $( "#tabs" ).tabs();});
如果這樣,這些複雜控制項就都要通過JavaScript來建立和渲染了,這與我們剛才提到的原則是違背的。那我們尋找的是什麼呢,是一種能擴充已有HTML體系的東西。在早期,IE瀏覽器中有HTC,可以通過引入命名空間來聲明組件,現在的標準瀏覽器中又引入了Web Components,在Polymer這個架構中可以看到更多的細節。說到底,這類方式要做些什麼事情呢?
- 隔離組件的實現,讓使用變得簡單
- 支援自行擴充新的組件
- 作一些範圍上的隔離,比如Web Components裡面,style標籤上可以加範圍,表示這個樣式只生效於組件內部
從另外一個角度講,為什麼我們非要這麼做不可?最大好處來自哪裡?對於大型項目而言,管理成本和變更成本都是需要認真考慮的。如果一個組件,需要在DOM中聲明一個節點, 然後再用一個js去擷取DOM,把DOM渲染出來,再填充資料的話,這個過程的管理成本是很大的,因為HTML和JS這兩個部分丟了一個都會有問題,無論在什麼時候,維護一個檔案總是比維護多個檔案要強的,我們看HTC那種方式,為什麼它的使用成本很低,因為它可以把控制項自身的DOM、邏輯、樣式全部寫在自己內部,整個一個檔案被人引用就可以了。在現在這個階段不存在這麼好用的技術了,只能退而求其次。
所以,在這個點上,Angular帶來的好處是可擴充的標籤體系,這也就是標籤的語義化。Angular的主打功能之一是指令,使用這種方式,可以很容易擴充標籤或者屬性。比如,業務開發人員可以直接寫:
<panel> <tree data="{{data}}"></tree></panel>
這樣多麼直觀,而且可以跟原有的HTML代碼一起編寫,不造成任何負擔。語義化的標籤是快速編寫介面的不二法門。
商務邏輯
有了語義化標籤之後,如果我們唯寫介面不寫邏輯,那也夠了,但現實往往沒有這麼美好,我們還要來考慮一下商務邏輯怎麼辦。
公司專屬應用程式一般都是面向某行業的,在這個行業內部,會有一些約定俗成的業務模型和流程,這些東西如何複用,一直是一個難題。以往的做法,會把這些東西都放在服務端,用類似Java這樣的語言來實現業務元素、商務規則和商務程序的管理。
這種做法所帶來的一個缺點就是對介面層的忽視,因為他只把介面層當作展示,對其中可能出現的大量JavaScript邏輯感到無所適從。很多從事這一領域的架構師不認同介面層的厚度,他們認為這一層只應當是很薄的,純展示相關的,但在這個時代,已經不存在真正輕量級的介面了。
前面提到,我們在前端作分層,把展現層跟商務邏輯層完全隔離,帶來的好處就是邏輯層不存在對DOM的操作,只有純粹的邏輯和遠程調用,這麼一來,這一層的東西都可以很容易做測試。對於一個大型產品來說,持續整合是很有必要的,自動化測試是持續整合中不可缺少的一環。如果不做分層,這個測試可能就比較難做,現在我們能把容易的先做掉,而且純邏輯的代碼,還可以用更快的方式來測試。
之前我們做前端的單元測試,都需要把代碼載入到瀏覽器來執行,或者自行封裝一些“無頭瀏覽器”,也就是不開啟實際的展示,類比這個測試過程。這個過程相對來說還是有些慢,因為它還有載入的這個網路傳輸的過程,如果我們能在服務端做這個事情呢?
我們看到,最近很火的NodeJS,它從很多方面給了前端工程師一個機會,去更多地把控整個開發流程,在我們這個情境下,如果能把針對前端邏輯的單元測試都放在node裡做,那效率就會更高。
二次開發平台
我們來看看,有了這麼一套分層機制,又有了介面標籤庫之後,該做些什麼呢?
做企業軟體的公司,有不少會做二次開發平台,這個平台的目標是整合一些已有的行業組件,讓業務開發人員甚至是不懂技術的業務人員通過簡單的拖拉、配置的形式,組合產生新的業務功能。
從介面的角度看,拖拽產生很容易,很多介面原型工具都可以做,但要如何整合資料和業務?因為你要產生的這個功能,是實實在在要拿去用,不是有個樣子看就可以,所以要能跟真實資料結合起來。 但這事情談何容易!
就比如說,介面上有一個選擇所屬行業的下拉框,裡面資料是配置出來的,對這個資料的查詢操作在後端,作為一個查詢服務或者是業務對象管理起來,有些傳統的方式可能是在後端作這個關聯,Angular架構可以把這個事情推到前端來。相比Backbone這樣的架構來說,Angular由於有雙向繫結,這個過程會變得特別省事。一個介面片段想要和資料關聯起來,要做的事情就是各種屬性的設定,所以動態載入和動態綁定都會比較容易。
比如:
partial.html
<ul> <li ng-repeat="item in items">{{item.name}}</li></ul>
main.html
...<div ng-include="‘partial.html‘" ng-controller="CtrlA"></div>...
a.js
function CtrlA($scope) { $scope.items = [{name:"Tom"}, {name:"Jerry"}];}
b.js
function CtrlB($scope) { $scope.items = [{name:"Donald"}, {name:"Micky"}];}
在上面的例子裡,這個列表顯示什麼,完全取決於ng-controller="CtrlA"這句,如果我們把這句搞成配置的,就很容易把資料來源換成另外一個CtrlB,甚至說,即使在同一版本上做項目化,引入另外一個包含CtrlA其他版本的js檔案,也基本無需更改其他代碼,這就達到了二次開發的一個目的:儘可能以配置而不是編碼去新增、維護新功能。
移動開發
現在的企業軟體已經不能只考慮PC的瀏覽器了,很多客戶都會有隨處工作的需求。響應式設計是一種常見的解決方案,但是在公司專屬應用程式領域,想要把複雜的業務功能設計成響應式介面的代價太大了,況且介面設計本身就是開發企業軟體的這些公司的短板,所以我們的比較簡單的辦法是對PC和移動終端單獨設計介面,這樣就有了一個問題了,這兩種介面的商務邏輯並沒有差別,如果我們要維護兩套代碼,代價是非常大的,能有什麼辦法共用一些東西呢?
如果不採用分層的形式,那這個很麻煩,我們注意到兩種系統的差異只在UI層,如果我們用分層的模式,可以共用UI層以外的東西。具體到Angular裡面來說,比如service,factory,甚至controller都是可以共用的,只有directive和HTML模板隨裝置產生差異就可以了。
之前我們很少看到有基於Angular的移動端開發架構,但現在有了,比如Ionic,使用這樣的架構,可以直接引用已有的商務邏輯代碼,只在展示上作一些調整。這麼做有很多好處,同時也對代碼的架構水準有一定要求,需要把商務邏輯跟介面展示完全切割開。
這樣帶來的好處也是很明顯的,獨立的商務邏輯,因為它不依賴於介面了,所以很容易控制,做單元測試,整合測試,打樁等等,總之它是純邏輯的東西,在後端可以用什麼方式保證代碼品質,在前端的商務邏輯也一樣可以用,商務邏輯可以因此而清晰穩定。
對於公司專屬應用程式而言,這麼做可以極大程度地複用以往的商務邏輯,只在負責最終展示的代碼部分作差異化。
工程化
上面這些技術性的問題都解決了,剩下的都是規模帶來的邊際效應,這需要我們從工程化角度去考慮很多問題:
- 某個JS模組被修改,如何得知會影響誰?
- 某個介面片段被調整,會影響什麼介面?
- 如何最小化發布?
- 如何一鍵測試、打包、壓縮?
- 。。。。。。
這些話題,篇幅所限,不在本文中敘述,可以查看我另外的關於Web應用組件化的文章。
原文地址: 基於AngularJS的企業軟體前端架構