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