【轉發】前端進階篇之如何編寫可維護可升級的代碼

 

前言javascript

我還在攜程的作業務的時候,每一個看似簡單的移動頁面背後每每會隱藏5個以上的數據請求,其中最過複雜的當屬機票與酒店的訂單填寫業務代碼css

這裏先看看比較「簡單」的機票代碼:html

而後看看稍微複雜的酒店業務邏輯:前端

機票一個頁面的代碼量達到了5000行代碼,而酒店的代碼居然超過了8000行,這裏還不包括模板(html)文件!!!java

而後初略看了機票的代碼,就該頁面可能發生的接口請求有19個之多!!!而酒店的的交互DOM事件基本多到了使人髮指的地步:ios

固然,機票團隊的交互DOM事件已經多到了我筆記本不能截圖了:git

就這種體量的頁面,若是須要迭代需求、打BUG補丁的話,我敢確定的說,一個BUG的修復很容易引發其它BUG,而上面還僅僅是其中一個業務頁面,後面還有強大而複雜的前端框架呢!如此複雜的前端代碼維護工做可不是開玩笑的!github

PS:說道此處,不得不爲攜程的前端水平點個贊,業內少有的單頁應用,一套代碼H5&Hybrid同時運行不說,還解決了SEO問題,嗯,很贊。web

如何維護這種頁面,如何設計這種頁面是咱們今天討論的重點,而上述是攜程合併後的代碼,他們兩個團隊的設計思路不便在此處展開。面試

今天,我這裏提供一個思路,認真閱讀此文可能在如下方面對你有所幫助:

文中是我我的的一些框架&業務開發經驗,但願對各位有用,也但願各位多多支持討論,指出文中不足以及提出您的一些建議

因爲該項目涉及到了項目拆分與合併,基本屬於一個完整的前端工程化案例了,因此將之放到了github上:https://github.com/yexiaochai/mvc

其中工程化一塊的代碼,後續會由另外一位小夥伴持續更新,若是該文對各位有所幫助的話請各位給項目點個贊、加顆星:)

我相信若是是中級水平的前端,認真閱讀此文必定會對你有一點幫助滴。

一個實際的場景

演示地址

http://yexiaochai.github.io/mvc/webapp/bus/list.html

代碼倉促,可能會有BUG哦:)

代碼地址:https://github.com/yexiaochai/mvc/

頁面基本構成

由於訂單填寫頁通常有密度,我這裏挑選相對複雜而又沒有密度的產品列表頁來作說明,其中框架以及業務代碼已經作過抽離,不會包含敏感信息,一些優化後續會同步到開源blade框架中去。

咱們這裏列表頁的首屏頁面以下:

簡單來講組成以下:

① 框架級別UI組件UIHeader,頭部組件

② 點擊日期會出框架級別UI,日曆組件UICalendar

③ 點擊出發時段、出發汽車站、到達汽車站,皆會出框架級別UI

④ header下面的日期工具欄須要做爲獨立的業務模塊

⑤ 列表區域能夠做爲獨立的業務模塊,可是與主業務靠太近,不太適合

⑥ 出發時段、出發汽車站、到達汽車站皆是獨立的業務模塊

一個頁面被咱們拆分紅了若干個小模塊,咱們只須要關注模塊內部的交互實現,而包括業務模塊的通訊,業務模塊的樣式,業務模塊的重用,暫時有如下約定:

這裏有些朋友可能認爲單個模塊的CSS以及image也應該參與獨立,我這裏不太贊成,業務頁面樣式粒度太細的話會給設計帶來不小的麻煩,這裏再以通俗的話來講:尼瑪,我CSS功底通常,拆分的太細,對我來講難度過高……

很差的作法

很差的這個事情實際上是相對的,由於很差的作法通常是比較簡單的作法,對於一次性項目或者業務比較簡單的頁面來講反而是好的作法,好比這裏的業務邏輯能夠這樣寫:

根據以前的經驗,若是僅僅包含這些業務邏輯,這樣寫代碼問題不是很是大,代碼量預計在800行左右,可是爲了完成完整的業務邏輯,咱們這裏立刻產生了新的需求。

需求迭代

由於我這裏的班次列表,最初是沒有URL參數,因此根本沒法產出班次列表,頁面上全部組件模塊都是擺設,因而這裏新增一個需求:

因而,咱們這裏會新增一個簡單的彈出層:

這個看似簡單的彈出層,背後卻隱藏了一個巨大的陷阱,由於點擊出發或者到達時會出城市列表,而城市列表自己就是一個比較複雜的業務:

因而頁面的組成發生了改變:

① 自己業務邏輯約800行代碼

② 新增出發到達篩選彈出層

③ 出發城市頁面,預計300行代碼

而彈出層的新增對業務自己形成了深遠的影響,原本url是不帶有業務參數的,可是點擊了彈出層的肯定按鈕,須要改變URL參數,而且刷新自己頁面的數據,因而簡單的一個彈出層新增直接將頁面的複雜程度提高了一倍。

因而該頁面代碼輕輕鬆鬆破千了,後續需求迭代js代碼量破2000僅僅是時間問題,到時候維護便複雜了,頁面複雜無規律的DOM操做將會令你焦頭爛額,這個時候組件化開發的優點便得以體現了,因而下面進入組件化開發的設計。

準備工做

整體架構

此次的代碼依賴於blade骨架,包括:

① MVC模塊,完成經過url獲取正確的page控制器,從而經過view.js完成渲染頁面的功能

② 數據請求模塊,完成接口請求

全站依賴於javascript的繼承功能,詳情見:【一次面試】再談javascript中的繼承,若是不太瞭解面向對象編程,文中代碼可能會有點吃力,也請各位多多瞭解。

整體業務架構如圖:

框架架構圖:

.

下面分別介紹下各個模塊,幫助各位在下文中能更好的瞭解代碼,首先是基本MVC的介紹,這裏請參考我這篇文章:簡單的MVC介紹

全局控制器

其實控制器可謂是變化萬千的一個對象,對於服務器端來講,控制器完成的功能是將本次請求分發到具體的代碼模塊,由代碼模塊處理後返回字符串給前端;

對於請求已經來到瀏覽器的前端來講,根據此次請求URL(或者其它判斷條件),判斷該次請求應該由哪一個前端js控制器執行,這是前端控制器乾的事情;

當真的此次處理邏輯進入一個具體的page後,這個page事實上也能夠做爲一個控制器存在……

咱們這裏的控制器,主要完成根據當前請求實例化View的功能,而且會提供一些view級別但願單例使用的接口:

這裏屬於框架控制器層面的代碼,與今天的主題不是很是相關,有興趣的朋友能夠詳細讀讀。

頁面基類

這裏的核心是頁面級別的處理,這裏會作比較多的介紹,首先咱們爲全部的業務級View提供了一個繼承的View:

一個Page級別的View會有如下幾個關鍵屬性&方法:

① template,html字符串,不包含請求的基礎模塊,會構成頁面的html骨架層

② events,全部的DOM事件定義處,以事件代理的方式定義,因此沒必要擔憂執行順序

③ addEvent,用於頁面級別各個階段的監控事件註冊點,通常來講用戶只須要關注不多幾個事件,好比:

一個頁面的基本寫法:

只要按照這種規則寫,便能展現頁面,而且具有DOM交互事件。

頁面模塊類

所謂頁面模塊類,即是用於拆分一個頁面爲單個組件模塊所用類,這裏有這些約定:

這裏代碼能夠再優化,但不是咱們這裏關注的重點:

數據實體類

這裏的數據實體對應着,MVC中的Model,由於以前已經使用model用做了數據請求相關的命名,這裏便使用Entity作該工做:

這裏的數據實體會以實例的方式注入給模塊類實例,他的工做是起一箇中樞左右,完成模塊之間的通訊,反正很是重要就是了

其它

數據請求統一使用abstract.model,數據前端緩存使用abstract.store,這裏由於目標是作頁面拆分,請求模塊不是關鍵,各位能夠把這段代碼看層一個簡單的ajax便可:

業務入口

最後簡單說下業務入口文件:

很簡單的代碼,指定了下require的path配置,最後咱們看看入口頁面的調用:

接下來,讓咱們真實的開始拆分頁面吧。

組件式編程

骨架設計

首先,咱們進行最簡單的骨架設計,這裏依次是其js代碼與模板代碼:

頁面展現如圖:

日曆工具欄的實現

這裏要作的第一步是將日曆工具欄模塊實現,以數據爲先的思考,咱們先實現了一個與日曆業務有關的數據實體:

裏面完成日期工具欄全部相關數據操做,而且不包含實際的業務邏輯。

而後這裏開始設計日期工具欄的模塊View:

這個組件模塊幹了幾個事情:

① 首先,dateEntity實體須要由list.js這個主view注入

② 這裏爲dateEntity註冊了兩個數據響應事件:

render方法繼承至基類,使用template與數據生成html,其中數據產生必須重寫父類一個方法:

由於這裏的日曆數據,默認取當前時間,可是url參數可能傳遞日期參數,因此定義了一個數據初始化方法:

該方法在主頁面渲染結束後會第一時間調用,這個時候日曆工具欄便渲染出來,其中日曆組件的使用便不予理睬了,主控制器的代碼改變以下:

因而,整個界面變成了這個樣子:

這裏是對應的日曆工具模板文件tpl.calendar.html:

搜索工具欄的實現

咱們如今的頁面,就算不傳任何URL參數,已經能渲染出部分頁面了,可是下面出發站汽車等業務數據必須等待班次列表數據請求結束才能替換數據,可是這些數據若是沒有出發城市和到達城市是不能發起請求的,因此這裏先實現搜索工具欄功能:

在出發城市或者到達城市不存在的話便彈出搜索工具欄,引導用戶選擇城市,這裏新增彈出層須要在主頁面控制器(檢測主控制器)中使用一個UI組件:

對應搜索彈出層html模板:

這裏核心代碼是:

因而當URL什麼參數都沒有的時候,就會彈出這個搜索框

這裏也迎來了一個難點,由於城市列表事實上應該是一個獨立的可訪問的頁面,可是這裏是想用彈出層的方式調用他,因此我在APP層實現了一個方法能夠用彈出層的方式調起一個獨立的頁面。

這裏有一個不一樣的地方是,由於咱們點擊查詢的時候纔會作實體數據更新,這裏是單純的作DOM操做了,這裏不設置數據實體一個緣由就是:

這個搜索彈出層是一個頁面級DOM以外的部分,數據實體變化通常只應該影響Page級別的DOM,除非真的有兩個頁面級View會公用一個數據實體。

搜索功能完成後,咱們這裏即可以進入真正的數據請求功能渲染列表了。

其他模塊

在實現數據請求以前,我按照日期模塊的方式將下面三個模塊的功能也一併完成了,這裏惟一不一樣的是,這些模塊的DOM已經存在,咱們不須要渲染了,完成後的代碼大概是這樣的:

相關文章
相關標籤/搜索