個推前端微服務化:突破傳統SPA瓶頸

目前的前端領域,單頁面應用(SPA)大行其道。而隨着時間的推移以及應用功能的豐富,這些應用變得愈來愈龐大也愈來愈難以維護。因而「微前端」這一律念應運而生。css

「微前端」出自2016 年的 ThoughtWorks 技術雷達,指將項目拆分紅一個個可獨立運行、獨立開發、獨立部署的前端微應用,這些微應用能夠並行開發、共享組件。前端

而微前端的實現方式也分不少種:服務器路由重定向、組合多個獨立應用、iFrame、經過Web Components構建等。vue

微前端的相關概念也在個推前端中的部分項目(基於Vue框架)中獲得應用。node


爲何要進行前端微服務化

之因此強調「部分項目」,是由於任何一種技術或者概念都有其適用場景,微前端也不例外。針對中小型的項目,使用微前端反而會將事情複雜化,由於微前端對項目的開發並不友好。webpack

以個推的業務場景爲例:web

在A項目線中有10-20個模塊,每個模塊中有5-15個不等的頁面。而A項目線中全部的產品都是基於這些模塊來自由組合的,也就是說:若是按照普通的SPA開發路線,咱們可能須要不少分支或者repo來維護這些產品,由於每一個產品所需的模塊版本會有細微區別。vue-router


演變的過程

爲了避免出現分支混亂、項目龐大、代碼衝突、打包麻煩等一系列的問題,藉着後端微服務拆分的機會,咱們開始對A項目線前端開發和部署方式進行了調整。vuex

最初,咱們並無使用前端微服務的開發和部署方式,而是先把項目中的各個模塊拆分紅了許多獨立的repo,避免團隊內的工程師在開發的過程當中出現須要pull代碼並解決衝突的狀況(一個模塊一個迭代通常由1-2人完成)。vue-cli

所以,咱們的問題是:模塊拆分後,如何解決開發、打包部署,以及項目中的公共依賴和組件複用的問題。npm

拆分後的模塊項目目錄結構大體以下:
圖片描述

項目中的main.js入口和公共組件被抽離成了一個單獨的項目,這裏稱爲main項目。

因爲各個子模塊項目中僅有當前模塊的頁面代碼和路由、菜單配置,因此dev子模塊沒法被直接開發。因而咱們開發了一個名爲lego的CLI工具。開發模塊時,開發人員只須要在模塊根目錄運行「lego dev」命令便可啓動一個當前模塊的開發服務,開發好的模塊都會被髮布到咱們本身的npm源進行版本的管理。

若是僅僅是對模塊進行拆分,那麼開發人員單獨對模塊進行開發時,須要給模塊配置對應的運行環境,而且模塊與模塊之間的相互調用也很麻煩。而「lego」CLI解決了模塊運行環境的問題,運行環境由CLI自動加載,模塊開發人員只須要關注模塊自身的業務邏輯便可。

此外,模塊還提供了一個config.js文件,能夠從npm源配置其餘依賴模塊,幫助開發人員在開發時更便捷地調用不一樣模塊。使用「lego dev」命令還支持「@self/」路徑引入,「@self/」路徑指向當前模塊的src/文件夾,而「@/」指向main項目的src/文件夾,從而避免了模塊開發時import路徑的問題。

經過模塊的拆分改造,解決了項目龐大、分支混亂的問題,代碼衝突的狀況也顯著減小。可是對於單個產品的打包部署,咱們仍然須要從各個模塊獲取源碼,並經過main項目打包成一個獨立的產品。即便只修改了某一個模塊的一行代碼,整個系統也須要從新打包,打包後的整個產品也須要進行迴歸測試。

針對這一問題,咱們思考是否能夠直接把模塊打包成應用以供調用。


模塊打包以及獨立部署

咱們的理想狀況是:各個模塊能夠獨立開發和部署,而後由產品自身決定加載的模塊。

效果以下:

圖片描述

所以咱們須要在模塊打包以後,入口(index.js)能夠按照須要被注入到main項目中,而且被main項目加載(路由)。

一方面,使用webpack進行打包的項目,代碼是基於CommonJS規範的。因爲umd規範兼容於CommonJS規範,這使得開發人員能夠直接在項目中使用基於umd規範打包後的模塊。

另外一方面,vue-router和vuex庫,都支持動態加載addRouter/registerModule的API。

咱們採用過兩種方案:

第一種:main項目在Vue實例初始化時,將vue-router和vuex的實例暴露到全局(window),將子模塊的路由前綴存儲在項目中的路由表。當頁面跳轉到匹配的子模塊的路由時,main項目加載子模塊umd.js文件並動態註冊router和vuex module,進而渲染頁面。

簡單DEMO以下圖所示:

圖片描述

第二種:子模塊umd.js文件先加載,向全局(window)暴露該子模塊的路由和vuex信息。Vue實例從window獲取路由信息和vuex module、菜單信息等,造成一個獨立的產品。

簡單DEMO以下圖所示:
圖片描述

固然,兩種方案都存在必定的缺點:

第一種方案:首先,子模塊js文件是在頁面跳轉以後再進行加載,所以,在404跳轉和路由權限校驗的實現上會遇到一些問題;其次,在子模塊文件加載完成以前以及子模塊渲染以前都存在較長的頁面白屏時間。

第二種方案:不管子模塊用戶是否會訪問到umd入口文件,該文件都須要事先加載。這就要求入口文件須要足夠小,意味着子模塊沒法使用min-chunk-size-plugin插件來對chunk進行合併,須要開發人員採用手寫webpackChunkName或者使用其餘工具進行合併。


基於VUE-CLI3的實踐

Vue-cli3.x對子模塊的打包提供了比較好的支持,使用"vue-cli-service build - target=lib"便可將子模塊代碼打包成umd規範格式。

可是,須要注意如下幾個問題:

「--target=lib」的初衷是給發佈到npmjs的組件使用,因此打包出的文件是不帶hash值的(即便在vue.config.js中配置了chunkName)。咱們採起的辦法是在執行lego腳手架的打包命令前,修改vue-cli-service源碼。

使用「--target=lib」打包子模塊時,若是沒有配置css-in-js,打包出的css文件中的background-image路徑有問題。基於此,咱們給出兩個解決辦法:配置css-in-js,或者修改node_modules中vue-cli-service源碼再打包。

以上即是個推前端微服務化的開發及部署的實踐狀況。

在實踐中咱們發現,微服務化的接入,很好地解決了項目中遇到的維護難、產品編譯部署麻煩等問題。在模塊化拆分時,咱們開發的CLI工具也很好地解決了模塊單獨開發運行的問題。

固然,咱們的微服務化方案也存在侷限。它比較適合模塊之間聯繫比較緊密的大型項目,且沒有微前端概念中強調的技術無關性以及團隊代碼隔離性。

在不久的未來,除了微服務化方案的繼續升級,咱們還會接入新的框架,迎接新的挑戰。

相關文章
相關標籤/搜索