1. 從0到1,搭建一個體系完善的前端React組件庫

        共 4836字,需瀏覽 10分鐘

         ·

        2020-08-20 21:04

        作者簡介

        ?

        劍橋,攜程資深前端開發(fā)工程師,關(guān)注自動化工具開發(fā)、前端工程自動構(gòu)建相關(guān)技術(shù)。


        隨著前端工程的發(fā)展,組件化的思想早已深入人心;現(xiàn)代的前端框架React/Vue等,都是圍繞組件設(shè)計;組件化的開發(fā)模式,大大提高了開發(fā)效率;設(shè)計和開發(fā)高質(zhì)量高復(fù)用性的公共組件,可以更好地保持產(chǎn)品迭代的高效和穩(wěn)定。

        我們以React的技術(shù)棧為背景,在日常的需求與迭代中, 歷時兩年多時間,沉淀出了攜程用車各大產(chǎn)線(接送機/包車/打車服務(wù)等)的公共組件(機場、航班、城市、地址、時間控件等)。通過持續(xù)交付了一系列的組件庫,讓各個產(chǎn)線的開發(fā)小組不用再各自維護重復(fù)而難以迭代的代碼,完成了前端組件與公共方法的收口,解決了用車前端業(yè)務(wù)組件一致性的問題。同時隨著組件庫工作流上的逐步完善,讓前端開發(fā)同學(xué)脫離了刀耕火種的開發(fā)方式,進入了全新的自動化構(gòu)建與高效開發(fā)的時代。


        開發(fā)和維護一個可持續(xù)迭代的組件庫,從來都不是一件容易的事情。本文將從組件庫的基礎(chǔ)搭建開始,從開發(fā)、打包、發(fā)布、拆包、優(yōu)化、自動化測試等各方面,由淺及深地進行介紹,給大家分享一個相對完善的組件庫落地的過程。同時也會介紹組件庫的迭代過程中真正會遇到哪些問題,以及我們是如何解決這些問題的。希望這些實戰(zhàn)中的經(jīng)驗,可以帶給大家一些啟發(fā)和想法。
        ?

        一、實現(xiàn)最基礎(chǔ)的npm發(fā)布流程


        ?
        在組件庫的設(shè)計之初,我們最先需要考慮的是,如何讓npm包的發(fā)布流程安全、可靠可行。為了保證代碼的安全性,公司內(nèi)部會獨立維護內(nèi)網(wǎng)的npm管理平臺。


        在最早的發(fā)布設(shè)計中,我們?nèi)匀煌ㄟ^官方定義的cli命令,在本地通過設(shè)置registry指向內(nèi)網(wǎng)倉庫后,執(zhí)行npm publish 進行發(fā)布。

        可是對于公司內(nèi)部而言,平臺開放而BU眾多,任何人都可以對任何已發(fā)布的包進行常規(guī)操作,這會帶來一系列的不安全因素。最終在前端委員會的推動下,我司實現(xiàn)了內(nèi)網(wǎng)npm與gitlab ci的關(guān)聯(lián)。將發(fā)布操作遷移到了gitlab上,在發(fā)布權(quán)限上有一定的約束;通過開啟npm deploy插件,以實現(xiàn)可視化交互式的發(fā)布管理,同時得益于gitlab hook的強大, 我們更是在流程實現(xiàn)了push event來觸發(fā)auto publish,這一系列的進步,讓我們的組件庫在后續(xù)的發(fā)布流程上變得更加正式、穩(wěn)定而可靠。


        Npm關(guān)聯(lián)gitlab后,通過指定指定分支下特定目錄的package.json,實現(xiàn)版本升級后自動發(fā)布
        ?

        二、組件庫的打包處理


        ?
        我們的技術(shù)棧涉及ReactWeb 與 React Native, 對于RN的代碼,我們一般會走源碼直接發(fā)布,RN項目中的編譯過程會自動處理node_modules里的源文件。但是對于Web組件庫而言,更傳統(tǒng)的做法,則是需要在發(fā)布之前進行一些編譯和轉(zhuǎn)碼,這樣才能確保發(fā)布之后的npm包,可以在大多數(shù)環(huán)境下正常運行起來。

        對于Web端組件庫的打包,我們進行了多次的探索和優(yōu)化。

        使用webpack對每個組件進行單獨打包,打包類型由umd改為commonjs2。

        module.exports = {    output: {        filename: '[name].js',        path: path.resolve(__dirname, '..', 'dist'),        library: 'Tha',        libraryTarget: 'commonjs2' // umd    }}

        通常我們對組件庫的建議是umd打包,因為這樣可以實現(xiàn)多種模塊方案的加載通用性。但在實踐過程中發(fā)現(xiàn),每個組件都需要單獨打包時,UMD的打包方式,會顯著增大每個文件的基礎(chǔ)體積;而且我們99% 的場景下,其實已經(jīng)并不用再去兼容AMD、CMD等模塊加載方式。

        在確保我們的代碼一定是通過node模塊方式加載的時候,我們只需要打出commonjs2的模塊即可。這一步的調(diào)整,顯著地提升了打包速度,也明顯減小了各個文件的打包體積。

        進一步編譯優(yōu)化

        對于組件庫而言,使用webpack進行打包,即使是使用了commonjs2的模式,繁重的配置工具仍然是顯得重了一些,而且需要額外配置各種external規(guī)則,以防止打包時打入了額外的第三方庫的代碼。使用rollup來處理組件庫的打包固然比webpack要合適,但是又會額外引入新的構(gòu)建工具,增加學(xué)習(xí)成本。

        最終我們選擇的更優(yōu)化的方案,是使用babel 直接做編譯轉(zhuǎn)換,不使用任何額外的構(gòu)建工具,也不做壓縮優(yōu)化處理---- 這些工作,在現(xiàn)代化的前端項目中,都會自動處理,不需要組件庫再做多余的構(gòu)建動作。Babel直接轉(zhuǎn)碼的方式,幫助我們省去了很多復(fù)雜的配置工作,并且讓組件庫打出來的生產(chǎn)代碼更加容易調(diào)試。

        優(yōu)化前,使用webpack等構(gòu)建工具打包組件:


        {  "scripts": {    "build:components": "webpack --config ./build/webpack.config.js --color",    "build": "npm run build:components && npm run build:css && npm run copy_package"  }}


        優(yōu)化后, 編寫腳本直接對組件源文件轉(zhuǎn)碼


        {  "scripts": {    "build:components": "cross-env NODE_ENV=production node ./build/trans"  }}

        提取組件中的樣式文件,單獨打包

        Css-in-js的開發(fā)方式固然是方便許多,但是在打正式包時,內(nèi)嵌的css實際會占用更多的代碼體積,并且node_modules里的js代碼中如果有顯式require css的語句時,在同構(gòu)項目中,可能會遇到服務(wù)端解析css文件的各種問題。

        為了解決這個問題,我們提取了所有組件的css進行單獨打包。其中所有的基礎(chǔ)組件樣式,會整體打包成一個main.css;而復(fù)雜業(yè)務(wù)組件的樣式,則會以組件為單位進行單獨打包,以便實現(xiàn)后續(xù)流程中業(yè)務(wù)組件的按需加載。


        ?

        三、組件庫實現(xiàn)業(yè)務(wù)組件的按需加載



        與各大知名的開源組件庫類似,為了減少項目的打包體積,我們對組件庫中的復(fù)雜業(yè)務(wù)組件,如航班組件、機場組件、城市選擇組件等,設(shè)計了按需加載的模式。

        對RN而言,我們直接利用了require的特性,通過修改導(dǎo)出對象的get方法,顯式地聲明了lazyLoad的組件程式。

        module.exports = {    //按需動態(tài)加載的模塊    get AddressList() { return require('./Address/List').default; }};

        對于Web而言,我們采用了類似ant-design的方式,在前面對業(yè)務(wù)組件的css進行單獨打包處理后,通過在項目中引入babel插件的方式,實現(xiàn)組件的按需加載。

        import { Address } from '@ctrip/thanos-ctrip-mobile/components.biz'/** 等價于import Address from '@ctrip/thanos-ctrip-mobile/components.biz/Address'import '@ctrip/thanos-ctrip-mobile/components.biz/Address/style.css'*/

        四、解決組件庫日漸臃腫問題


        ?
        隨著組件庫的不斷迭代,組件代碼會不斷增多,需求也會越來越復(fù)雜。其他研發(fā)同學(xué)也可能會開發(fā)獨立的npm組件包,但是會基于已開發(fā)完成的組件庫的部分功能來實現(xiàn)。

        這種情況下,開發(fā)其他npm包的同學(xué),可能只想使用當(dāng)前已有庫中的部分功能,而不太愿意引入一個完整而龐大的組件庫。為了使組件庫的功能更加獨立且通用,讓UI組件與功能模塊之間更好地解耦,我們需要對組件庫進行拆子包處理。

        如組件項目中基礎(chǔ)UI部分,從組件庫中剝離,拆分成獨立的ui-basic組件庫;組件項目中工具方法(表單校驗、環(huán)境判斷、正則處理、時間日期格式化等),拆分成獨立的 util庫。這種拆分組件包的開發(fā)形式,組件庫不再是所有功能都揉在一個倉庫中,開發(fā)和維護將變得更加靈活且易于擴展。

        拆包前,core的部分將隨著功能的增加而越來越臃腫:



        拆包后的結(jié)構(gòu):



        如圖所示,拆分獨立功能包后,可以讓我們擴展和組合出更多靈活多樣的組件庫,讓組件庫不再單一而臃腫。
        ?

        五、解決子組件包的開發(fā)環(huán)境問題


        ?
        拆分子組件包后,給組件庫的多樣性擴展帶來了極大的便利,但隨之而來的問題便是,每一個子組件包都需要單獨維護,在開發(fā)子組件包時,每一個包都需要一個可運行的本地開發(fā)環(huán)境。

        隨著子組件包的數(shù)量逐漸增多,給每一個包都單獨設(shè)立一個開發(fā)環(huán)境,必然會帶來更大的維護成本。我們目前選擇的解決方案是,對于粒度更細(xì)的子組件包,所有的子包會公用一套dev的開發(fā)倉庫,通過 git modules在開發(fā)倉庫中嵌套子模塊倉庫,實現(xiàn)了只維護一套開發(fā)環(huán)境,產(chǎn)出多個子模塊包的組件庫工廠。


        ?
        在這種環(huán)境下,還可以做到當(dāng)子模塊之間存在相互依賴時,可以直接引用相對路徑下其他模塊的源碼,不必為了調(diào)試某個模塊的代碼,而跑到node_modules里去翻找,徒增調(diào)試難度。
        ?

        六、組件庫文檔化與協(xié)同開發(fā)


        ?
        為了讓組件庫的開發(fā)流程更加規(guī)范,減少接入方的溝通成本,對組件庫進行適當(dāng)?shù)奈臋n梳理是十分必要的,我們使用gitbook 編寫組件庫的文檔,并部署到公司內(nèi)部的books平臺上。同樣借助于gitlab強大的web hook的能力,實現(xiàn)了文檔倉庫的自動更新與發(fā)布。
        ?


        與此同時,我們也啟用了協(xié)同開發(fā)的模式,讓組件庫成為一個內(nèi)部的開源庫,用車產(chǎn)線的研發(fā)同學(xué),可以通過提交issuse和merge request的方式,自行對組件庫中的個別需求進行開發(fā),提升開發(fā)效率。
        ?

        七、組件庫單元測試、自動化與持續(xù)集成



        單元測試

        當(dāng)組件庫在開發(fā)和交付流程上趨于完善后,在公司G2戰(zhàn)略背景下,為了保證代碼的高質(zhì)量,我們開始在組件庫中接入自動化單元測試。接入單元測試也是一項十分曲折的過程。在測試技術(shù)框架的選型上,綜合考慮了當(dāng)前技術(shù)棧、框架市面通用性等多種因素,最終選擇如下:

        測試框架:Jest
        選取原因:對React技術(shù)棧友好,同時也是React-Native官方推薦的測試框架
        測試庫:
        web端 -> @testing-library/react
        RN ->@testing-library/react-native
        選取原因:React的官方測試庫,對hooks類型的組件支持度高,選擇這兩個庫,也是為了能夠保持后續(xù)與react官方版本更新的同步

        自動化與持續(xù)集成

        在接入單元測試后,我們依然借助gitlab的CI/CD,對整個組件庫的流程進行自動化構(gòu)建與持續(xù)集成交付,在內(nèi)置CtripDevOps或者自定義gitlab-ci.yml的配置下,我們將單元測試的環(huán)節(jié)加入到了pipeline中,同時通過公司統(tǒng)一的sonar檢測,提供最終的組件庫質(zhì)量統(tǒng)計報告。


        ?

        八、結(jié)語


        ?
        要搭建一個相對完善的組件庫,都是需要經(jīng)過一系列項目的沉淀的。目前而言,組件庫的開發(fā)流程上依然會存在一些問題,比如版本管理、升級回退等。時間是最好的老師,相信在后面的迭代中,我們依然能夠保持初心與熱情,積極探索與發(fā)現(xiàn),構(gòu)建出更加完善的前端工程體系。

        【推薦閱讀】


        瀏覽 58
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 九九九九无码 | 国产精品无码一区二区三区免费 | 欧美视频亚泰视频在线观看 | 中文字幕成人乱码熟女 | 在线精品又粗又大ac |