個人的中小型項目前端架構淺談

HTML DIALOG 路由器 騰訊TM CSDN 2017-04-27

一、為什麼要有一個好的架構

首先明確一點,架構是為需求服務的。前端架構存在的目的,就我個人理解來說,有以下幾點:

1. 提高代碼的可讀性

一個好的架構,代碼的可讀性一定是很強的。

簡單來說,假如有一個新人加入團隊,那麼他接手這個項目,一定是容易上手的,能簡單輕鬆的瞭解整個前端部分的相互關係,從而找到自己需要重點關注的點。而不是需要花很多時間去熟悉這個項目的很多細節,才能開始上手做東西。

就文件來說,可以從文件名上,分清哪些是頁面、哪些是邏輯、哪些是樣式、哪些是可以複用的組件、哪些是圖標組、又有哪些是移動端或是PC端專享的樣式/邏輯等。

就代碼來說,包括統一的命名風格,封裝在同一個文件裡的代碼的相關性足夠強等。

2. 提高代碼的可維護性

一個好的架構,一定是易於維護的,例如在新增需求、更改需求、修正bug,都不會造成意料之外的變化,比如說修改了一個頁面組件的內容,卻導致另外一個頁面組件發生變化(這也太坑了)。因此,要低耦合,高內聚,以及輸入和輸出是可預期的。

3. 提高代碼的可擴展性

一個好的架構,一定擴展性要強,不能寫死。

需求變更太TM正常了,新增需求也太TM正常了。因此好的架構,必須要考慮到這些情況的發生,因為這些是一定會發生的。所以,一定要避免把代碼寫死。

比如頁面組件A裡需要有一個日曆組件,而這個日曆組件引用的是別人的(比如從github上找的)。那麼儘量不要直接在頁面組件A裡面直接引用這個日曆組件,而是將寫一個日曆組件B,在這個日曆組件B裡封裝你引用的日曆組件C,然後通過這個日曆組件B來進行操作。

原因很簡單,假如某天產品經理說,這個日曆組件太醜了,我們換一個吧。如果你直接在頁面組件A裡內嵌這個引用的日曆組件C,你很可能就要改很多代碼(因為不同日曆組件的使用方法和暴露的接口可能不同)。假如你還在其他多個地方引用了這個日曆組件,那就更糟糕了!每個地方都要改。

而若是將引用的日曆組件C封裝到自己寫的日曆組件B之中,那麼你只需要改日曆組件B裡的相應代碼即可,而因為日曆組件B暴露的接口是不變的,那麼自然不用修改頁面中的代碼了。

附圖,以日曆組件為例,是否考慮到擴展性的結果

  • 未考慮到擴展性:


個人的中小型項目前端架構淺談

考慮到擴展性:

個人的中小型項目前端架構淺談

4. 便於協同

包括前端和後端的協同,前端和前端之間的協同。

具體來說,前後端的協同通常是以ajax為交互,那麼應至少有一個用於專門封裝了所有ajax請求的文件,所有ajax請求都封裝在這裡。在開發時,這裡封裝的方法應該可以模擬發送和接收約定好的交互內容,方便開發聯調。

而前端和前端的協同,主要體現在同時在更改代碼時,不會影響對方代碼的正常運行。因此要求封裝、解耦以及低干擾度是必須的。

5. 自動化

自動打包,壓縮,混淆,如果有必要,再加上自動單元測試。

總結:總結來說,一個好的架構的目的是,讓前端寫代碼寫的舒服,讓後端聯調的舒服,讓產品經理改需求改的舒服。

二、我如何設計架構

我不敢說自己的架構是好的架構(顯然不是啦),只能分享自己最近做的一個項目,它的架構的如何做的。

首先,確定需求:

1、一箇中小型網站,同時面向移動端和PC端(單端大概15個頁面,算上彈窗大約20個);

2、預算有限(給的錢少),開發時間有限(一個月);

3、可能存在一定程度上的需求變更(比如增加頁面或修改某些頁面內容);

4、客戶可能不太在乎優化(但是我自己在乎啊);

5、要求兼容IE9以上。

其次開始決定:

1、兼容IE9以上說明可以使用主流框架,而無需必須使用jQuery。因此我採用了vue,版本是2.0;

2、預算有限,時間有限,因此PC端和移動端共html和js,獨立css;

3、頁面有限,因此無需將架構層級劃分的比較細,只需要按其類型劃分即可;

4、根據原型圖來看,頁面複雜程度有限,複用部分不是很多,因此可以確定哪些東西需要封裝複用,哪些比較複雜需要獨立封裝,哪些比較簡單為了簡化開發難度可以直接耦合;

5、自己比較熟練單頁面網站,因此採用以單頁面為主,異步加載其他頁面的形式。

於是使用相關配套的東西,比如webpack,vue-router等,另外為了開發和生產的方便性,採用以下模式進行開發。

個人的中小型項目前端架構淺談

第三,劃分功能:

首先有一個根html,用戶需要通過訪問它來加載我們的js邏輯,因此js邏輯的代碼被寫在main.js之中。

在main.js之下,我們的前端代碼可以被劃分為三部分:

  • 組件樹

  • 功能模塊

  • 各種資源

如下圖:

個人的中小型項目前端架構淺談

功能劃分好之後,相同功能的放在同一個文件夾下,命名風格應該類似。

具體來說,組件樹相關的東西,通常是以.vue結尾,放置在components文件夾下;資源,有圖片或者國際化資源等,以.png或者.js或.json結尾,放置在resources文件夾下;而功能插件、服務等,因為可能被多處引用,因此為了方便引用,放在src文件夾下,並且該文件夾是components文件夾和resources文件夾的上級文件夾。

第四,細化功能模塊:

功能、組件樹以及資源,我們已經明確了有哪些東西,那麼接下來,我們要明確這些東西該如何以文件的形式來劃分。

如下圖:

個人的中小型項目前端架構淺談

1. 項目構建相關
  • 因為要使用vue.js,也要使用es6語法,因此babel是必須的;

  • 又因為要自動化混淆打包,因此webpack也是必須的;

  • 最後因為要方便多人協同,因此npm的package.json的配置,方便不同人可以快速自動化通過npm install來安裝依賴,也是必須的。

2. CDN相關

而又因為我們要採用外部字體(需求要求引入非常見字體),因此CDN加速是必須的,該字體文件放在html中來配置引用即可。

3. 配置和插件
  • 我們需要直接引入一些插件和配置文件;

  • 為了使用vue,我們需要一個根組件,那麼就是App.vue

  • 使用vue-router,我們需要配置路由文件,因此router-config.js這個路由配置也是必須的;

  • 然後我們還需要以插件形式引入一些功能和服務,因此有了Plugin-開頭的若干個vue插件,這些都是根據需要封裝好的低耦合高內聚方法;

4. 需要的npm依賴

當然,要使用vue肯定要引入vue.js,類似的還有vue-router.js和各種兼容性polyfill和全局插件。

5. 抽離出的功能模塊
  • 除了直接引用的這些插件,我們還有一些和項目高度耦合的功能服務,我認為不能作為插件,但依然需要抽離出來封裝好,方便使用和修改;

  • 如封裝ajax請求的ajax.js,所有的ajax請求都放置其中,只對外暴露接口,方便管理和使用;

  • 又如實時國際化功能的組件LanguageManager.js,他需要引入國際化資源和管理國際化資源的加載;

  • 又例如實現跨組件通信的event-bus.js;

  • 又比如管理用戶信息的user.js。

總結:而這些劃分,都體現在上圖之中。這就是src目錄下的功能模塊文件,我們需要的絕大多數功能都可以包括在其中,我們只需要按照實際開發中的需要,將對應的功能寫入在這些文件中並引用即可。

第五,組件樹:

之前談了功能模塊的劃分,接下來是組件樹。

因為是中小型頁面,因此組件樹的層級無需太深,但該抽離出來的依然還是要抽離,儘量保證抽離出來的組件解耦以及一個頁面組件的邏輯不要太多。如下圖:

個人的中小型項目前端架構淺談

0. 根組件

所有組件最終往上找,都會找到共同的根組件App.vue,根組件只負責管理他的直接子組件。

每個組件都只負責管理自己的直接子組件,不跨級管理,並且不依賴於自己的子組件(否則可能因為子組件的未加載或錯誤而導致父組件錯誤),做到解耦和內聚。

1. 彈窗dialog和彈窗tips

因為彈窗dialog和彈窗提示tips可能同時存在,因此將其劃分為2個組件,方便管理。

2. 未登錄頁面和登錄頁面

因為頁面存在登錄和未登錄狀態,而為了加載速度考慮,當未登錄時,不加載已登錄頁面,因此需要劃分出來,並進行異步加載處理。

3. 未登錄頁面

未登錄頁面又分為三種情況:

  • 初始頁面:毫無疑問要直接加載

  • 登錄彈窗:點擊登錄時加載(異步)

  • 註冊彈窗:點擊註冊時加載(異步)

之所以分拆開,是因為根據需求,已登錄用戶刷新頁面,可以直接進入登錄後頁面,因此無需登錄和註冊,這種處理可以減少流量消耗,提升加載頁面加載速度(特別是註冊彈窗需要加載的內容還比較多)。

4. 已登錄頁面

已登錄頁面有較多頁面,採用默認加載初始頁,然後異步加載其他頁面(訪問時)。

5. 彈窗dialog

由於邏輯較少,代碼量不多,因此為了方便管理,統一將其合併在一個vue文件中,共同相同的打開邏輯,根據傳遞的key決定打開哪一個。這樣在新增彈窗時,無需再去寫彈窗的打開、關閉邏輯。

假如有較複雜的彈窗,可以以子組件的形式引入到當前vue文件中,如此也方便管理;

6. 國際化管理

和頁面高耦合,負責加載對應的國際化資源,並進行切換管理。

7. 頁面組件

可能有子頁面和複用的組件,按照正常方式引用即可。

8. 樣式文件

可以獨立寫為.css文件,但因為我的公共樣式文件比較少,因此我還是將其放在一個.vue文件中,並在App.vue裡來引用。

9. 頁面組件起名
  • 通常以.vue為結尾,除了國際化LanguageManager.js因為高耦合度,因此以.js結尾並是一個單獨的vue實例,表示他更像是一個功能模塊,而不是一個vue的頁面組件;

  • 基礎頁面,如登錄和未登錄頁面,公共組件(並且是header和footer這種),以base-開頭;

  • 彈窗統一以box-為開頭;

  • 可複用的組件以extend-開頭;

  • 引入的外部組件以import-開頭;

  • 普通頁面組件以page-開頭(這些頁面往往是一個獨立的頁面,並且掛靠在登錄或未登錄頁面下);

  • 註冊彈窗因為邏輯比較複雜,並且同類較多,因此以register-為開頭。

通過以文件名來劃分,不同的頁面組件之間的區分可以說是一目瞭然,同時也方便管理。

本文來自CSDN博客:http://blog.csdn.net/qq20004604/article/details/70480932

相關推薦

推薦中...