我前段時間參與了一個react
爲主的大前端項目,覆蓋Web、Android、Ios三個平臺。因爲整個業務邏輯側重在手機端,且Web端也是到了項目中期纔開始啓動,我分別以react-native
和react
分開建了兩個項目。javascript
但是,後端微服務集羣是同一個,兩個項目調用的API大多同樣,致使兩個項目中不可避免的出現了一些重複邏輯。好比說寫在redux中的業務邏輯代碼,寫在http攔截器中的請求處理代碼等等,這些重複的部分從目錄名和文件名便可肉眼看出。前端
我嘗試了將重複部分封裝成獨立的npm
包供兩個項目引用,但這個作法僅適用於變化頻率較小的工具類代碼,一旦封裝了變化頻繁的業務邏輯代碼,用起來也麻煩不斷。首先,抽出到項目以外但不易開發和調試,其次,在項目開發過程當中業務代碼更新太頻繁,經常要不斷升級版本。權衡利弊,最終得不償失。java
那麼,有沒有辦法更好的重用這部分代碼邏輯呢?有沒有辦法把這兩個項目的通用代碼抽取成獨立項目,可是又能避開封裝成獨立npm
包的弊端呢?react
我最近嘗試引入lerna框架,把這個大前端項目架構做爲參照物來改造,驚喜的發現它不但能解決當時項目的痛點,還能額外帶來一些多項目管理相關的好處。git
事實上,開源社區早已有不少項目使用了這種多項目合而爲一的方案,且採用了lerna框架的代碼庫也大多耳熟能詳,好比國外的有babel、create-react-app、react-router、jest、以及國內跨端小程序框架Taro。github
最後通過改造,兩個項目合併成一個,重複的代碼邏輯也被抽取成另外一個獨立項目,整個項目結構變成了下面圖示這樣。web
lerna
的名字來源於希臘神話中的九頭蛇海德拉(Lernaean Hydra),拿它形容多項目工程是再貼切不過了。npm
lerna
的引入比想象中簡單,其實,與其說引入lerna
,倒不如說是導入到lerna
更合適,由於具體的作法是經過命令行建立了一個新的lerna
項目,而後把全部項目導入進去。並且在導入的同時,每一個項目的git提交記錄也都合併在了一塊兒。json
lerna init
lerna import 你本地的項目路徑
複製代碼
每一個被導入的項目都會被存放在根路徑的packages目錄下,下面是我demo項目的截圖,一共引入了三個子項目:rntest, web-app, shared。分別表明mobile,web和可重用的邏輯代碼。redux
引入lerna
後,第一件事就是要處理安裝依賴的問題,咱們須要用lerna add
命令來代替咱們習慣的npm
或yarn
,好比說給rntest項目安裝lodash
,就要執行下面的命令。
lerna add lodash --scope=rntest
複製代碼
不過,執行後你會發現其餘項目中package-lock.json都發生了變化,讓人很是困惑,這背後的緣由是跟添加依賴後自動執行的安裝命令lerna bootstrap
有關。
lerna
能夠經過lerna bootstrap
一行命令安裝全部子項目的依賴包,並且在安裝依賴時還有依賴提高功能,所謂「依賴提高」,就是把全部項目npm依賴文件都提高到根目錄下,這樣能避免相同依賴包在不一樣項目安裝屢次。好比多個項目都用了redux
,經過依賴提高,多個項目一共只須要下載一次便可。不過,須要額外的參數--hoist
讓依賴提高生效。
lerna bootstrap --hoist
複製代碼
可是自動執行lerna bootstrap
命令是不帶依賴提高參數的,這就致使上面每一個項目的lock文件都會被修改的緣由。
固然,要解決這個問題也容易,能夠經過lerna
的配置來避免npm對lock文件的修改便可,寫法以下:
lerna
默認使用npm
做爲安裝依賴包工具,但也能夠選擇其餘工具。yarn
在1.0版本以後提供了workspaces的功能,該功能從更底層的地方提供了依賴提高,作的事情跟lerna
一模一樣。把它跟lerna
放在一塊兒看,簡直就像是爲lerna
量身定作同樣。所以,推薦在lerna中搭配yarn一塊兒使用。
把npm替換成yarn只需在lerna的配置文件添加兩行代碼便可,配置完之後馬上順暢百倍。
在我參與的這個大前端項目裏,多端之間代碼重複的部分包含redux
中的業務邏輯、http請求的處理、代碼規範工具的檢查、git
鉤子中的自定義腳本等等。在lerna
架構下,前二者可直接抽取到一個獨立的項目,而後被其餘項目引用,好比在個人demo中,能夠像其餘依賴包同樣直接引入shared
項目, lerna會自動識別並把它導向內部項目。
import shared from 'shared'
複製代碼
這跟直接封裝成npm
包的一大區別就是實時更新,修改馬上可見,就像在同一個項目同樣,不影響開發和調試。
我嘗試把處理git
鉤子的工具husky
安裝到了根目錄,觸發的事件和自定義腳本能覆蓋到每一個項目,給這部分代碼重用帶來了極大便利。好比,很多項目會添加自定義腳原本約束git commit
提交時的消息描述,在lerna
架構下,只需寫一次便可。
那些經常須要在根目錄添加配置文件的第三方依賴,好比eslint
、prettier
、babel
等,在lerna
中沒法簡單粗暴的提高合併到一處。所以,對於eslint
這種前端開發已不可或缺的工具,能夠嘗試將全部配置項抽取到獨立項目,而後安裝第三方依賴的方式引入,相似eslint-config-airbnb
,eslint-config-prettier
,eslint-config-google
這樣。
不得不說,即使不用lerna
框架咱們也能夠這麼作,只不過在lerna
框架下修改馬上可見,方便了調試和開發。
多項目的結構無疑給CI/CD
帶來挑戰,好在主流的CI框架能完美解決這個問題。好比在gitlab上,only/changes
參數徹底知足了咱們的需求,讓咱們能夠爲每個子項目設置單獨的pipeline,好比如今咱們設置一個pipeline,只當rntest項目下的文件被修改時纔會觸發:
在lerna框架下,全部項目都合在一個工程裏,但CI/CD
並沒必要這樣。經過把腳本中的關鍵參數配置到CI/CD
的項目內裏,共用同一份.gitlab-ci.yml
文件,從而可以實現每一個子項目對應一個獨立的CI/CD
項目,最終CI/CD
結構以下圖:
因爲全部的項目都歸併到了一個lerna
工程下,一旦有了訪問權限意味着你能夠修改全部子項目中的代碼,在實際的開發工做中多多少少會帶來一些麻煩。好比說,開發web和開發mobile平臺的是兩個不一樣的團隊,假如我做爲web組的一員,一不當心修改了或刪除了mobile項目的文件該怎麼辦?假如不加入任何限制,這種事情早晚會發生,我想這多是lerna
框架與生俱來的的痛點。
不幸的是,在lerna
框架下,gitlab或github這類第三方代碼託管平臺,自己的權限管理功能沒法解決這問題。但好在有其餘工具的幫助能夠緩解這種痛,我嘗試用來約束開源貢獻者提交PR規範的工具dangerjs
來完成權限分隔,利用的信息就是當前gitlab帳號的用戶名,看起來效果還不錯。
能夠看到,此工具會在合併MR時,判斷出我gitlab帳號沒有權限修改rntest子項目內的文件,從而禁止合併此MR,並將這些信息自動添加到MR的評論裏。固然,腳本判斷是本身寫的僅用做演示,邏輯比較簡陋,腳本代碼以下:
關於dangerjs
的部分我會另寫一篇文章詳細介紹。
大前端項目將會是前端發展的趨勢,如何更好的管理大前端項目是每一位前端開發躲不開的課題。lerna
框架經過合而爲一的理念提供了一種解決方案,經過揚長避短,咱們能夠發揮出lerna
的最大效用。假如你尚未用過,也許,下一個項目就能夠試試看。
相關資料