前端組件化系列文章第一篇:前端組件化方案探究
背景
公司目前基于多業(yè)務(wù)部門,很多業(yè)務(wù)組件和功能邏輯都具有較高的普適性,但與此同時各業(yè)務(wù)部分和開發(fā)人員缺乏一定的交流平臺,更多的是在遇到對應(yīng)需求時會簡單內(nèi)部討論一番,當(dāng)了解到其他業(yè)務(wù)部門存在落地的方案時,再進(jìn)一步進(jìn)行溝通交流。
這種方式,總體來說還是比較原始的,無論從溝通方式的效率來說,還是對于組件業(yè)務(wù)邏輯的深入理解都是非常低效的。因此,對于探索一種高效的、簡單的、現(xiàn)代化方案是非常有必要的。
我們想要的結(jié)果是(任務(wù)目標(biāo)):
- 不同的業(yè)務(wù)平臺開發(fā)的組件能夠最大程度復(fù)用;
- 新人能夠快速了解通用組件,提升開發(fā)效率
這也就是本系列文章的真實(shí)背景,也是我著手負(fù)責(zé)的項目,相關(guān)文章會按照系列形式輸出,并確保文章內(nèi)容都是在真實(shí)業(yè)務(wù)中的實(shí)踐總結(jié)。
簡要說明
作為本系列的第一篇文章,其定位和作用也是相當(dāng)清晰的:
- 搞清楚前端組件化是什么?
- 搞明白組件化要怎么做?
- 思考如何把組件化做的更好?
前期主要以第1、2兩點(diǎn)作為執(zhí)行方向進(jìn)行探索,在基本明白了什么是組件化以及如何實(shí)現(xiàn)自己的組件化之后,進(jìn)一步探索、對比、研究、思考如何實(shí)現(xiàn)高效能的組件化方案。
前端的發(fā)展路徑
在互聯(lián)網(wǎng)早期階段,傳統(tǒng)的開發(fā)模式中,往往是把前端的網(wǎng)頁代碼和后端的程序代碼混合在一起,借助某種模板技術(shù)(如 JSP、ASP、PHP)來在服務(wù)器端動態(tài)生成 HTML 頁面。
在這種開發(fā)模式下,網(wǎng)頁的每次改動都需要前后端人員共同參與才能完成,網(wǎng)站前后端的開發(fā)人員需要很大的溝通成本、協(xié)調(diào)成本。企業(yè)招人的時候,也不得不招一些既懂前端又懂后端程序員,來減少前后端開發(fā)人員的沖突。
可以發(fā)現(xiàn)這種模式是非常低效的。
現(xiàn)在流行的前后端分離的開發(fā)模式,就是讓后端只負(fù)責(zé)給前端提供數(shù)據(jù),由前端負(fù)責(zé)整個頁面的模板渲染、數(shù)據(jù)填充以及交互邏輯。
前后端分離之后,人們發(fā)現(xiàn)前端已經(jīng)不再是傳統(tǒng)意義上的網(wǎng)頁了,它甚至還可以做成一個手機(jī)應(yīng)用,或者做成是微信小程序那樣的小型應(yīng)用,它更接近于傳統(tǒng)的 B/S(客戶端/服務(wù)器)架構(gòu),且仍然具備網(wǎng)頁輕量級、無需下載和安裝的優(yōu)勢。
什么是組件化
在前后端分離的現(xiàn)代開發(fā)模式下,以React、Vue為例,涌現(xiàn)了很多優(yōu)秀的現(xiàn)代化的前端框架,得益于新技術(shù)的發(fā)展,SPA 應(yīng)用已經(jīng)相當(dāng)成熟,該開發(fā)范式也是相當(dāng)普遍。
伴隨著這種技術(shù)行為,組件化開發(fā)也是應(yīng)運(yùn)而生,作為一個非常常規(guī)的模式,圍繞幾點(diǎn)說明:
- 方便復(fù)用(很多業(yè)務(wù)代碼、功能代碼都不可避免有所重復(fù),對于組件的復(fù)用使用,可謂作用很大)
- 方便維護(hù)(如果大量的業(yè)務(wù)代碼與功能代碼耦合一起,對于代碼的日后維護(hù)和功能拓展都有著很大的局限性和不足)
- 功能細(xì)分、專一、職責(zé)明確(組件的核心原則就是,一個組件只做一件事,而且其不應(yīng)該是有上下強(qiáng)關(guān)聯(lián)耦合性的,可以隨用隨取,做到職責(zé)單一,對于日后功能迭代和代碼維護(hù)好處都是顯而易見的)
就現(xiàn)代前端而言,我覺得組件化已不單單局限于 UI 組件層面,它涉及面更廣、更全、也更系統(tǒng)化,類似 hooks 的功能和業(yè)務(wù)邏輯封裝,工具類的使用封裝,構(gòu)建工具、腳本代碼等,一切符合其獨(dú)立思想的都可稱為組件化。
為什么需要組件化
組件化的目的:
- 為了讓各功能和業(yè)務(wù)邏輯可以被復(fù)用,以減少重復(fù)的代碼。
- 可以更好地使團(tuán)隊分工協(xié)作,不同的人負(fù)責(zé)編寫不同的組件,提高開發(fā)效率。
- 職責(zé)單一也更方便功能的迭代拓展與維護(hù),對于接手的開發(fā)人員也能降低不少上手成本
對于需要開發(fā)復(fù)雜的大型應(yīng)用的企業(yè)來說,組件化開發(fā)能極大地提高開發(fā)效率,它讓前端開發(fā)團(tuán)隊能高效地完成工作,是一個非常有用的技術(shù)。
簡單來說就是兩個關(guān)鍵詞:“復(fù)用”、“易維護(hù)”
代碼拆分也好,職責(zé)單一也好,基本都離不開這兩詞,這也正式組件化的核心點(diǎn)。我們做這些事,就是為了之后遇到類似需求和功能的時候減少代碼的開發(fā),能夠快速重復(fù)使用。而對于組件在日后隨著功能和業(yè)務(wù)的發(fā)展、包括我們對于組件代碼健壯性的提升,都會進(jìn)行不斷的迭代,維護(hù)性的重要性也是不言而喻的了。
基本目標(biāo)
圍繞核心述求可以進(jìn)一步細(xì)化梳理,我們希望它能夠這樣:
- 可以圍繞基礎(chǔ)組件、業(yè)務(wù)組件,以及工具類的定位進(jìn)行劃分
- 項目方案要足夠簡單清晰,確保新人上手成本足夠低,可以快速投入開發(fā)使用
- 組件的可維護(hù)性需要有保障
- 嚴(yán)格的規(guī)范和工程化保障
- 支持查看
API、示例的文檔網(wǎng)站 - 真實(shí)開發(fā)環(huán)境的模擬調(diào)試和開發(fā)
- 能夠以
npm包的形式進(jìn)行引用使用 - 可靠的多包項目管理、多包版本管理
- 足夠優(yōu)秀的性能表現(xiàn)
技術(shù)背景
首先,以公司實(shí)際項目為背景思考,我們的技術(shù)棧目前主要圍繞 vue2 相關(guān)生態(tài)進(jìn)行前端 web 后臺類系統(tǒng)的開發(fā)。因此,主要方案設(shè)計也要以此為基礎(chǔ)導(dǎo)向進(jìn)行探索。
就目前而言,我們的組件主要包括可復(fù)性較高的業(yè)務(wù)組件以及一些通用功能組件,也包括一些工具類。組件開發(fā)主要依賴于iview基礎(chǔ)組件和lodash工具庫,部分特殊業(yè)務(wù)有使用到monaco-editor、codemirror等。
因此,現(xiàn)階段我們公司的技術(shù)棧相關(guān)生態(tài)主要為 vue2,本系列也會以公司的vue2項目經(jīng)歷為依托進(jìn)行記錄。不過結(jié)合當(dāng)下前端整體快速發(fā)展的現(xiàn)實(shí)情況下考慮,在項目實(shí)施落地之后,對相關(guān)生態(tài)具有足夠清晰地掌握之后,以vue3和react為技術(shù)背景的探索也值得進(jìn)一步探索研究。
包依賴
我們公司當(dāng)前項目平臺主要依賴分析,如下:
- 核心三大件:
vue/vue-router/vuex - UI 類:
view-design、codemirror、monaco-editor、vue-echarts、vuedraggble - 工具類:
lodash、dayjs、uuid
圍繞以上技術(shù)相關(guān)生態(tài),做到對各部分及功能使用的二次封裝管理
主要 API 及實(shí)際項目代碼分析:
- slot:組件拆分、UI 布局、自定義展示區(qū)域的好方法
- props:父組件向子組件傳遞參數(shù)
- ref:父組件獲取子組件方法、屬性
- emit:子組件向父組件傳遞參數(shù),觸發(fā)父組件綁定事件方法
- provide/inject:多級子父組件傳值
- vuex:第三方狀態(tài)管理(需要考慮單一組件的可維護(hù)性)
總的來說組件封裝較頻繁使用的屬性方法為props/slot/emit/ref,也是我們項目中大量采用的方式。
vuex 的使用需要謹(jǐn)慎考慮,具有一定的耦合性,需要進(jìn)一步探索分析。
對于更優(yōu)的方式探索值得思考,核心點(diǎn)為職責(zé)單一獨(dú)立、耦合性低,方便維護(hù)。
功能/業(yè)務(wù)組件
業(yè)務(wù)組件不同于功能組件,其具有一定的使用背景和條件限制。以我們使用的 iview、antd等UI組件為例,其提供了大量的功能組件,包括但不限于表單功能組件、Table列表功能組件、多選/單選功能組件、日期功能組件等等,它只提供某一類的通用功能,并抽象封裝為一個具體的功能組件。
而相對于業(yè)務(wù)組件來說就有些許不同了,業(yè)務(wù)組件首先必定聚焦于一個明確的業(yè)務(wù)場景,其最終實(shí)現(xiàn)可能是融合了多個功能組件的組合,也可能是結(jié)合一些插件獨(dú)立實(shí)現(xiàn)的某一特殊功能。
當(dāng)前項目已存在的一些業(yè)務(wù)組件分析:
-
ProTable功能組件:由單個
FilterBtn組件、Table組件、Pagination組件,進(jìn)而組合成整體的一個組件(包含列表、篩選、分頁) -
ComSqlTipTree業(yè)務(wù)組件:使用頻率相當(dāng)高的
ComSqlTipTree組件,也是集合了眾多子組件的一個組合(涉及到對Input的高級封裝、Select選擇、拼音匹配、列表展示等,該組件專門用于公司核心功能篩選類查找時的使用)
組件管理思考
- 基礎(chǔ)組件類:可以參照
iview、antd等UI組件庫的方式進(jìn)行維護(hù)管理,聚焦于功能相關(guān),屬于較小型的組件,統(tǒng)一維護(hù)管理。 - 業(yè)務(wù)組件類:可以參照
pro-component高級組件的管理模式進(jìn)行處理。 - 工具類:可以參照
lodash的方式進(jìn)行維護(hù)管理
pro-component的管理模式為大量基于基礎(chǔ)組件封裝而成的高級組件,如:pro-layout、pro-table、pro-form等,每個組件可以作為獨(dú)立的npm包作為依賴使用,相互獨(dú)立、互不影響。
再思考到對于工具類的管理維護(hù),整體考慮下來,基本確定可以為多包管理的方向(Monorepo)
技術(shù)調(diào)研
圍繞項目技術(shù)背景和目標(biāo)規(guī)劃,進(jìn)行相關(guān)技術(shù)調(diào)研。
相關(guān)開源項目分析
- 需要考慮的方向是:對于
vue2.x版本的組件化方案,哪種更為合適(目前市面上有大量的vue3組件庫) -
vue2對于TS的考慮 (vue2對ts的支持性一般,是否支持視情況而定) - 對
props參數(shù)API的文檔、demo演示,文檔網(wǎng)站建設(shè)的考慮 - 目錄設(shè)計、工程規(guī)范、構(gòu)建發(fā)布流程
-
element
- 無
monorepo,屬于比較傳統(tǒng)的單一包管理項目 - 比價經(jīng)典的
vue2組件庫項目,文檔網(wǎng)站風(fēng)格很不錯 - 主要使用
js + vue開發(fā),構(gòu)建由webpack處理 - 文檔網(wǎng)站由自身組件開發(fā)搭建,結(jié)合
webpack loader實(shí)現(xiàn)對markdown文件的編譯及demo示例的渲染
- 無
-
vuetify
-
lernajs + yarn + workspace的方案,標(biāo)準(zhǔn)的lernajs + yarn用法 -
vue2.x + ts寫法,組件大量以JSX實(shí)現(xiàn),具有很好的參考學(xué)習(xí)價值(vue2 中對 TS 的使用) - 文檔網(wǎng)站由自身組件自行開發(fā)搭建,項目代碼較多,有一定的上手成本
-
-
element-plus
-
vue3的項目,pnpm + workspace的方案,整體思路和設(shè)計非常現(xiàn)代化 - 組件實(shí)現(xiàn)由 vue編寫開發(fā),相關(guān)腳本代碼由 TS編寫,整個項目為 TS項目
- 多包目錄設(shè)計、組件和文檔目錄設(shè)計合理
- 相關(guān)構(gòu)建工具包括
rollup、esbuild、vite - 文檔網(wǎng)站由
vitepress構(gòu)建,并自行編寫了處理markdown和vue demo的插件進(jìn)行處理(vitepress只支持vue3)
-
-
idux
-
vue3的項目,pnpm + workspace的方案,技術(shù)都比較新 - 版本控制由
lerna管理 -
demo與docs的目錄管理很不錯,需要配合腳本實(shí)現(xiàn)最終的網(wǎng)站路由和渲染 - 文檔網(wǎng)站由
vitepress+ 自身組件構(gòu)建,并配合各種腳本實(shí)現(xiàn)對應(yīng)功能 - 構(gòu)建工具主要為
gulp - 組件開發(fā)主要為
tsx形式
-
monorepo 是什么
- Monorepo:所有依賴庫完全放入一個項目工程
- Multirepo:多個依賴包獨(dú)立進(jìn)行 git 管理
monorepo 是把多個項目的所有代碼放到一個 git 倉庫中進(jìn)行管理,多個項目中會有共享的代碼則可以分包引用。整個項目就是有 root 管理的 dependencies 加上多個 packages,每個 package 也可以在自己的作用域引入自己的 dependencies。
該方案可以很好的幫助我們解決不同工作區(qū)對于相同依賴的管理,以及我們在開發(fā)時,包相互間依賴的管理。
monorepo + lernajs + yarn
Lerna 是一種工具,針對使用 git 和 npm 管理多軟件包代碼倉庫的工作流程進(jìn)行優(yōu)化。
Lerna 有兩種管理項目的模式:
-
Fixed/Locked模式 (默認(rèn)): 所有的包共用一個版本號。 -
Independent mode:不同包獨(dú)立使用自己的版本,我們一般采用這種方式。在初始化的時候指定--independent參數(shù)
一般而言,在pnpm未出現(xiàn)前,相關(guān)workspace概念基本是配合 yarn一起使用(yarn有workspace功能),因為 npm7.x之前是沒有 workspace概念的,所以在很長的一段時間里,多包管理方案 monorepo都是與 yarn一起配合使用。
他解決了以下問題:
- 多業(yè)務(wù)組件、互相依賴、無法復(fù)用
- 發(fā)包流程復(fù)雜、版本管理痛苦
依賴管理
在工作區(qū) packages 下面我們有這些包:iview、pro-sqltiptree、components,其 package.json 中的 name 定義分別為@ah-ailpha/iview、@ah-ailpha/pro-sqltiptre、@ah-ailpha/components,在我們相互安裝包時,lerna 工具可以幫我們以用軟鏈接的形式直接使用。(對于軟鏈接的概念可以參考 npm link,以及pnpm的底層實(shí)現(xiàn)邏輯)
# 向 module-2 中添加 module-1 作為依賴
$ lerna add module-1 --scope=module-2
版本管理
對于monorepo的多包場景,lerna另一大核心功能就是可以很好的幫助我們管理每個包的版本,通過使用 lerna version命令即可(可結(jié)合命令式交互快速管理版本)
lerna version 在背后為我們做了這些事:
- 識別出自上次發(fā)布以后更新過的包;
- 提示選擇新版本;
- 修改包的元數(shù)據(jù)來反映最新發(fā)版(修改包的版本號),在根目錄和每個包里面運(yùn)行生命周期腳本;
- 對提交打 tag;
- 推送到遠(yuǎn)程代碼倉庫。
monorepo + pnpm
pnpm現(xiàn)在也支持workspace的概念了工作空間(Workspace),如果是經(jīng)常關(guān)注技術(shù)社區(qū)論壇的,相信你對pnpm已經(jīng)有過了解了,pnpm因為其優(yōu)秀的性能表現(xiàn),以及其對monorepo的支持也更簡單方便,使得現(xiàn)在很多知名的開源項目都采用了該方案,如 Vue3/Vite/VueUse/Element Plus/Next.js/Astro等等。
pnpm 內(nèi)置了對單一存儲庫(也稱為多包存儲庫、多項目存儲庫或單體存儲庫)的支持, 你可以創(chuàng)建一個 workspace 以將多個項目合并到一個倉庫中。
一個 workspace 的根目錄下必須有 pnpm-workspace.yaml 文件, 也可能會有 .npmrc 文件。
如何高效的使用 pnpm,需要了解幾個常用的基本命令:
-
--filter:過濾,過濾允許您將命令限制于包的特定子集,一般用于packages/*下面的子項目。 -
-C:在<path>中啟動pnpm,而不是當(dāng)前的工作目錄。 -
-r:安裝在所有packages中(一般配合--filter指定項目目錄) -
-w:表示把包安裝在 root 下,該包會放置在<root>/node_modules下
pnpm同樣可以實(shí)現(xiàn)對包依賴的管理,類似lerna一樣,通過軟連接的方式關(guān)聯(lián)到子項目,可以極大的方便我們對于多項目依賴的管理。
pnpm add <package-name> --filter <target-package-name>
# 比如要將lodash裝到package-a下
# --filter 后面可以為目錄名稱也可以為 package.josn 的 name 名稱
pnpm add dayjs --filter @ah-ailpha/package-a
# 向 module-2 中添加 module-1 作為依賴
pnpm add module-1 --filter module-2
lerna / changeset 版本管理分析
不管對于 yarn還是 pnpm來說,都只是對于工作空間 woekspace的多包管理,對于多依賴管理,pnpm目前可以支持,而yarn是需要lerna進(jìn)行協(xié)助的。
但是對于各包的version版本控制,兩者都不直接參與管理,只能使用第三方工具進(jìn)行處理。
lerna
目前已知的比較成熟的方案有lerna,也是使用比較頻繁的,好處是其功能比較全面,但也存在一些明顯的問題
- 不能和
pnpm很好的適配,lerna本身不支持workspace協(xié)議,導(dǎo)致基于pnpm開發(fā)的一些倉庫無法使用 -
lerna version根據(jù)commit以及tag更新出來的包版本不符合預(yù)期 - 整個包的體積很大,依賴特別多(輕量版lerna-lite)
- 相比較
pnpm而言,lerna使用起來還是比較的笨重、繁瑣
changeset
Changesets 是一個用于 Monorepo 項目下版本以及 Changelog 文件管理的工具(目前一些比較火的 Monorepo 倉庫都在使用該工具進(jìn)行項目的發(fā)包例如 pnpm、mobx 等)
changesets 主要關(guān)心 monorepo 項目下子項目版本的更新、changelog 文件生成、包的發(fā)布。
一個 changeset 是個包含了在某個分支或者 commit 上改動信息的 md 文件,它會包含這樣一些信息:
- 需要發(fā)布的包
- 包版本的更新層級(遵循
semver規(guī)范) -
CHANGELOG信息
總的來說,changeset的工作內(nèi)容還是比較明確的,我們需要直接參與的就是包版本的管理與發(fā)布,對于changelog的文件生成會由腳本自動處理,我們無需關(guān)心(需要按照commitlint規(guī)范提交message,這里到時我們通過工程化的方式進(jìn)行控制即可)
版本控制管理小結(jié)
- 使用
yarn + monorepo的項目一般使用lerna配合進(jìn)行依賴和版本管理 - 采用
pnpm + workspace方案的 monorepo,可以選擇使用 changeset進(jìn)行版本管理控制,從而快速搭建一個完整的 monorepo項目。
尾聲
本篇文章算得上是搭建本項目的一些前期探究,在從0到1的過程還是比較迷茫的,使用什么方案,哪種方案適合我,在找到適合的方案時又發(fā)現(xiàn)并不是我喜歡的技術(shù)方向時,如何做出選擇,以及如何選擇最優(yōu)解等等,這些都是在我在實(shí)踐的過程中所經(jīng)歷的。
總的來說,我比較關(guān)注技術(shù)社區(qū)和開源項目,一般出現(xiàn)了什么新的技術(shù)框架時,我也能及時的了解和嘗試。在針對如何使用的這個問題上,通過結(jié)合官方文檔和開源社區(qū)各種demo模板,也能略知一二,再簡單上手使用對比下,大致能夠有所了解,就我個人的項目而言我還是滿喜歡嘗鮮的,覺得好用、不錯就會直接開整,遇到問題就嘗試解決,也算是在這個過程中學(xué)習(xí)了。不過,對于公司的項目來說,總體偏穩(wěn)定版、技術(shù)成熟度高、社區(qū)活躍的進(jìn)行選擇,在遇到問題時也能發(fā)現(xiàn)類似的解決方案。
monorepo算是我很早之前就打算研究和嘗試的方向了,苦于一直沒有合適的機(jī)會(??懶~),在沒有項目的推動下,自己也缺乏一定的動力,恰好現(xiàn)在公司在推進(jìn)這個事情,而且還是由我進(jìn)行負(fù)責(zé),也算得上是個不錯的機(jī)會了。
在開始之前我自己之前對于lerna是有一點(diǎn)了解的,因此,最開始在進(jìn)行技術(shù)調(diào)研的時候也是大致圍繞這個方向進(jìn)行的,期間也看了不少開源項目的設(shè)計和使用方式,基本上算是入門了,不過,在我自己進(jìn)行demo測試以及簡單使用后,發(fā)現(xiàn)lerna的確有很多不太方便、不易用的地方,尤其是對于多項目命令的支持,遠(yuǎn)不及pnpm來的方便。
pnpm對于我來說并不算陌生,我在去年就開始在用了,整體還是比較順手的,也比較喜歡,只是對于pnpm + workspace + monorepo這種方式?jīng)]有實(shí)踐過,不過在此之前倒是了解過不少相關(guān)的文章,印象較深的是介紹 vue生態(tài)的多包管理過渡到pnpm + workspace的文章,自己正好也一直想找機(jī)會嘗試來著。
因此,在使用lerna發(fā)現(xiàn)一些較大的短板之后,我也是順勢過渡為pnpm + workspace的方案來了,以及在后期嘗試了對于版本管理控制的changeset方案之后,最終確定的技術(shù)路線為pnpm + workspace + changeset的monorepo多包管理方案。
實(shí)踐篇相關(guān)文章見下一篇,歡迎關(guān)注(已經(jīng)在寫了??~)。