在web應用中,複雜表單這類web應用富交互元素多,業務邏輯複雜,犬牙交錯,且需求變化頻繁。及容易成爲晦澀和幽暗之地,也常常是各類代碼壞味道的來源。針對這種典型的複雜應用,本文以淘寶機票訂單爲例提出一種架構模式梳理和消化表單帶來的複雜性。javascript
解決複雜表單的的第一步,劃分模塊。php
概念上,爲了複用和解耦方便,應將模塊按照功能的內聚程度進行劃分。強相關,頻繁溝通和交互的功能應該歸爲一個模塊。模塊間儘可能不存在依賴關係。也就是常說的「高內聚,低耦合」。
以下圖所示,淘寶機票訂單頁面主要有被分爲7個主要模塊。css
模塊劃分完畢,下一步確認組成模塊的組件。
關於模塊和組件的區分。通常按照如下三個緯度考量。html
是否有業務邏輯參與。html5
是否包含html。java
是否具有必定獨立性。web
「模塊」,定義爲一個包含」html」、」css(圖片被認爲是css的一部分)「、」javascript」的代碼集。模塊的 應用方式多爲經過web模板技術(如:velocity、freemarker、php)。由於包含了html,使得模塊必須經過服務端合併加載而且最終 推送到用戶瀏覽器。此外,「模塊」仍是具有必定獨立業務和交互的集合,最好能夠被其餘頁面引用。良好的獨立性也能夠幫助協同開發,在實際開發中可多人能夠 並行開發多個獨立模塊,提升效率。瀏覽器
「組件」,定義爲一個僅包含」css」和」javascript」的代碼集。正由於不包含html,因此組件可經過 javascript異步加載。由於這種可異步加載的特性,組件在複用方面的容易性遠超模塊。組件沒有業務邏輯或者僅有少部分公共業務邏輯。業務邏輯越 多,組件的可複用性就越低。網絡
組件/模塊劃分的目的是將彼此間相對獨立的功能分離,前面經過模塊和組件的劃分解決了分離問題。實際中,模塊之間存在協做關係。模塊間應以一種輕量的方式協做。通常的爲了更好的分離和解耦,能夠考慮用廣播的方式在模塊間溝通,考慮使用事件的方式在組件間通信。架構
以下圖所示,淘寶機票訂單頁面的數據流向。
不一樣模塊在後期均有可能擴展小功能。例如不按期的活動優惠等。事件廣播可讓不一樣模塊/組件間新增功能影響面縮小。在淘寶機票訂單中應用中,使用廣播組件通信主要用來完成如下意圖。
一、知會。
知會的特性在於異步通信。廣播發起方只須要放出事件,無需等待其餘關注者完成處理。稱爲異步廣播。例如表單模塊的內容變動須要知會到顯示訂單金額的模塊,顯示訂單金額的模塊接受事件後須要更改金額。
基於這種方式的通信,各模塊之須要作好本身的事情,外部關注的事件廣播出去便可。異步廣播還有一個好處是系統堅固性比較強,廣播發送者不會由於事件監聽者的使用不當而異常。
二、請求數據
例如,模塊6(負責提交)須要在被點擊後從模塊2(伺機人表單),模塊4(聯繫地址)、模塊7(金額計算)。獲取具體數據提交。請求數據的場景特性在於,廣播發起者須要等待事件處理者完成處理後再繼續下一步行爲。稱爲同步廣播。
基於此機制。提交模塊只須要負責綜合校驗,浮層,網絡請求及異常處理。而具體請求的內容由其餘模塊決定。對後續模塊的擴充起到了很好的左右。
模塊和組件劃分完畢後,可能會發現某些組件很是複雜,幾乎佔據了整個web應用一半以上的代碼。這部分組件由純js實現,而且使用javascript模塊加載器加載。
同一個組件大量代碼糾結在一塊兒,最終仍是會致使架構腐化。所以,複雜組件須要進一步拆分。在淘寶機票訂單中,伺機人信息組件是一個複雜組件。以下圖所示:
拆分這類輸入型的複雜組件,通常來講有兩種思路方式。
縱切,組件樹型式。
將組件進一步劃分爲更細力度的輸入組件,將每一個輸入域做爲一個單獨組件。最終造成一個組件樹。
這樣的組織方式結構嚴謹層級清晰,最大的優勢是很容易支持字段擴展。
但考慮以下場景,爲了儘可能友好的提示用戶,須要在輸入域外的某處增長提示幫助。
這種場景下組件樹的組織方式每次在面對變化時就會略顯手忙腳亂。難道把每一個地方出現的tip都座位獨立組件看待嗎?
字段級的適普性下降了適應細節調整的能力,付出的代價在於界面體驗。
橫切,AOP式。
將全部輸入域抽象的看待爲同一個組件。按照組件的富應用特性分層看待。在本例中,伺機人組件被按照從簡單到複雜分爲3個切面。
切面1-基礎展示層只負責最基礎的可完成輸入的表單控件,及基礎dom管理。
切面2-富展示層負責修飾base層的基礎html控件,造成富輸入控件。
切面3-校驗層負責對base層的輸入數據進行業務級校驗。
將來,若是新增tip或者其餘業務邏輯,增長一個新切面便可,徹底或者不多須要修改老代碼文件。
淘寶機票訂單採用了AOP這種方式,從最終代碼量上來看,能夠看出複雜度被比較均衡的分佈到不一樣文件中去。
一樣,這種方式也有侷限,若是須要擴展字段,那將是一個災難,你有可能須要到每個切面裏面去作修改。
有句老話說的好,沒有最優方案,只有最適合的解決方案,任何解決方案,都須要放到具體場景中去評判。事實上,對這個問題的進一步研究,能夠發現如下規律。
對於一個組件、模塊,同時追求簡單設計、適普性(字段級擴充)、界面體驗是不可能的。若是場景須要適應字段靈活擴展,那就採用縱切的模式。若是使用 場景需字段肯定,須要更多細節控制力度,那就橫切,AOP式。若是二者都要兼顧,就須要引入複雜設計,綜合運用橫切和縱切。可是這樣造成的最終設計會很復 雜,開發和可維護性上會有代價付出。
對於淘寶機票這類互聯網應用,使用了橫切的方式來拆分組件,由於在這個場景中,字段的數量是相對固定的,而圍繞固定數量字段的優化需求是層出不窮的。然而在企業內網應用或者網站後臺web應用中,字段的變化會比較頻繁。建議主要採用縱切的思路劃分。
有表單的地方就有校驗。項目初期,校驗的功能老是不起眼。等待項目後期時候常常會發現校驗已經佔據了巨大工做量而且成爲海量bug的源頭。所以校驗是一種典型的容易被輕視單又蘊含巨大工做量的事情,須要特別對待,專門設計。
通常來講,這根據校驗根據其複雜度能夠分爲如下兩類:
格式校驗
格式校驗通常是校驗用戶輸入的格式是否知足要求,好比是否數字、電話號碼、郵箱等等。此類校驗的特色是校驗域單一,通常只對一個input或者某個組件的 value進行檢查。格式類校驗應與與用戶展示很是接近,一種很是好的作法是將此類校驗信息直接描述在html標籤屬性中。html5中input的 pattern屬性就是一種基於這種思想的解決方案。
邏輯校驗
邏輯校驗是知足格式校驗後,繼續進行的與業務相關的校驗,例如是否存在相同用戶名,輸入的生日是否和身份證號不符等等。此類校驗的通常涉及多個輸入域,要綜合處用戶的輸入內容一塊兒校驗。此類校驗邏輯複雜,不適合寫在html中。
目前有不少流行的form校驗框架解決校驗問題,如何引入合適的校驗框架,先從理解校驗這件事的過程開始。
典型的一個校驗過程以下,用戶在某個input處完成輸入,應用在某個時刻被觸發校驗,能夠是失去焦點或者keyup或者其餘。被觸發的校驗過程找到此處 input所須要的校驗規則(有時候這個規則被直接寫在html中)判單正確與否,若是正確,可能有提示,若是錯誤,可能也有提示。
從以上場景的描述中,能夠找到校驗的幾個關鍵環節。這裏局部採用一下管理學上經典的5w1h問題分析方法來分析問題
who: 哪一個輸入控件的內容須要校驗。這是框架是解決不了的。要對哪一個輸入域作校驗應該是應用傳遞進入的。
when: 什麼時候被觸發校驗。好比說是「who」失去焦點時。變化太多,框架解決不了。只能被動觸發。
what: 作什麼校驗。有時候這個」what」被寫在html中。基本上,全部格式校驗都是固定的,這個問題應框架解決。但框架應預留接口作更加複雜的業務校驗。
how: 校驗完畢後的動做。框架不能決定作什麼,可是在校驗結果出來後,框架應能知會到外部調用者。
在設計框架或者選擇已有框架時,首先要區分框架的邊界,簡單來講,就是作什麼和不作什麼。框架應實現相對固定的業務流程。同時對可變部分預留足夠的靈活性。
一個通用的校驗框架必定是不含界面部分的。界面是多變和難以窮舉的,是用tip顯示錯誤,仍是在輸入域附近顯示,是否須要動畫,是否須要修改輸入域 的視覺狀態,這些可變化的部分應爲框架外部內容,由更專業的tip組件或者popup來完成。框架只應該負責在校驗完成時候知會相關組件完成顯示錯誤提示 等若干事情。
基於以上的分析,校驗框架應該具有如下規格
1. 解決what問題。內置了各類格式校驗規則,如電話號碼、e-mail等.而且可以靈活定義新的邏輯校驗。
2. 解決who問題。說明如何根據輸入的字符真正找到who對應的value。而且可以對於這個who使用哪些校驗規則
3. 解決when問題。提供一個觸發校驗的方法。
4. 解決how問題。產生校驗結果後可以知會外部的功能框架。
在淘寶機票訂單應用中,依據上述原則自行設計了一個Validator框架,接口定義以下,Validator是校驗框架對象。
在構造函數中提供表格化的校驗邏輯定義型式。以下圖所示,傳遞以下結構,定義每一個字段對應的校驗方式。在下圖中,定義每行爲一個field,每一個field有若干rule,每一個rule能夠是框架內置的格式校驗,也能夠是自定義的邏輯校驗,其實是函數名。
Validator框架提供validate()方法,validate方法有兩個行爲,若是不指定參數,將依次執行完全部field的校驗,而且將最終結果返回。若是執行一個field name,框架將只校驗field name對應的輸入域。
一旦執行validate()方法,不管校驗結果如何,框架均向其觀察者發送事件’onValidate’。以便觸發後續動做。
一些輔助參數,須要提供一個從field name找到輸入域value的function。
在處理複雜表單時,首先經過合理模塊、組件劃分,將複雜度分散。而後利用詳細和廣播機制解決分散的模塊和組件間通問題。接着,過於複雜的組件要考慮進一步拆分,具體拆分的方式有縱切和橫切兩種,根據具體使用場景決定。最後,不要小看了校驗,須要特別對待,專門設計。
在新浪微博關注我:@魏凡哲-陶清