【前端組件化】系列第一篇——方案探究

前端組件化系列文章第一篇:前端組件化方案探究

背景

公司目前基于多業務部門,很多業務組件和功能邏輯都具有較高的普適性,但與此同時各業務部分和開發人員缺乏一定的交流平臺,更多的是在遇到對應需求時會簡單內部討論一番,當了解到其他業務部門存在落地的方案時,再進一步進行溝通交流。

這種方式,總體來說還是比較原始的,無論從溝通方式的效率來說,還是對于組件業務邏輯的深入理解都是非常低效的。因此,對于探索一種高效的、簡單的、現代化方案是非常有必要的。

我們想要的結果是(任務目標):

  1. 不同的業務平臺開發的組件能夠最大程度復用;
  2. 新人能夠快速了解通用組件,提升開發效率

這也就是本系列文章的真實背景,也是我著手負責的項目,相關文章會按照系列形式輸出,并確保文章內容都是在真實業務中的實踐總結。

簡要說明

作為本系列的第一篇文章,其定位和作用也是相當清晰的:

  1. 搞清楚前端組件化是什么?
  2. 搞明白組件化要怎么做?
  3. 思考如何把組件化做的更好?

前期主要以第1、2兩點作為執行方向進行探索,在基本明白了什么是組件化以及如何實現自己的組件化之后,進一步探索、對比、研究、思考如何實現高效能的組件化方案。

前端的發展路徑

在互聯網早期階段,傳統的開發模式中,往往是把前端的網頁代碼和后端的程序代碼混合在一起,借助某種模板技術(如 JSP、ASP、PHP)來在服務器端動態生成 HTML 頁面。

在這種開發模式下,網頁的每次改動都需要前后端人員共同參與才能完成,網站前后端的開發人員需要很大的溝通成本、協調成本。企業招人的時候,也不得不招一些既懂前端又懂后端程序員,來減少前后端開發人員的沖突。

可以發現這種模式是非常低效的。

現在流行的前后端分離的開發模式,就是讓后端只負責給前端提供數據,由前端負責整個頁面的模板渲染、數據填充以及交互邏輯。

前后端分離之后,人們發現前端已經不再是傳統意義上的網頁了,它甚至還可以做成一個手機應用,或者做成是微信小程序那樣的小型應用,它更接近于傳統的 B/S(客戶端/服務器)架構,且仍然具備網頁輕量級、無需下載和安裝的優勢。

什么是組件化

在前后端分離的現代開發模式下,以ReactVue為例,涌現了很多優秀的現代化的前端框架,得益于新技術的發展,SPA 應用已經相當成熟,該開發范式也是相當普遍。

伴隨著這種技術行為,組件化開發也是應運而生,作為一個非常常規的模式,圍繞幾點說明:

  1. 方便復用(很多業務代碼、功能代碼都不可避免有所重復,對于組件的復用使用,可謂作用很大)
  2. 方便維護(如果大量的業務代碼與功能代碼耦合一起,對于代碼的日后維護和功能拓展都有著很大的局限性和不足)
  3. 功能細分、專一、職責明確(組件的核心原則就是,一個組件只做一件事,而且其不應該是有上下強關聯耦合性的,可以隨用隨取,做到職責單一,對于日后功能迭代和代碼維護好處都是顯而易見的)

就現代前端而言,我覺得組件化已不單單局限于 UI 組件層面,它涉及面更廣、更全、也更系統化,類似 hooks 的功能和業務邏輯封裝,工具類的使用封裝,構建工具、腳本代碼等,一切符合其獨立思想的都可稱為組件化。

為什么需要組件化

組件化的目的:

  1. 為了讓各功能和業務邏輯可以被復用,以減少重復的代碼。
  2. 可以更好地使團隊分工協作,不同的人負責編寫不同的組件,提高開發效率。
  3. 職責單一也更方便功能的迭代拓展與維護,對于接手的開發人員也能降低不少上手成本

對于需要開發復雜的大型應用的企業來說,組件化開發能極大地提高開發效率,它讓前端開發團隊能高效地完成工作,是一個非常有用的技術。

簡單來說就是兩個關鍵詞:“復用”、“易維護

代碼拆分也好,職責單一也好,基本都離不開這兩詞,這也正式組件化的核心點。我們做這些事,就是為了之后遇到類似需求和功能的時候減少代碼的開發,能夠快速重復使用。而對于組件在日后隨著功能和業務的發展、包括我們對于組件代碼健壯性的提升,都會進行不斷的迭代,維護性的重要性也是不言而喻的了。

基本目標

圍繞核心述求可以進一步細化梳理,我們希望它能夠這樣:

  1. 可以圍繞基礎組件、業務組件,以及工具類的定位進行劃分
  2. 項目方案要足夠簡單清晰,確保新人上手成本足夠低,可以快速投入開發使用
  3. 組件的可維護性需要有保障
  4. 嚴格的規范和工程化保障
  5. 支持查看API、示例的文檔網站
  6. 真實開發環境的模擬調試和開發
  7. 能夠以npm包的形式進行引用使用
  8. 可靠的多包項目管理、多包版本管理
  9. 足夠優秀的性能表現

技術背景

首先,以公司實際項目為背景思考,我們的技術棧目前主要圍繞 vue2 相關生態進行前端 web 后臺類系統的開發。因此,主要方案設計也要以此為基礎導向進行探索。

就目前而言,我們的組件主要包括可復性較高的業務組件以及一些通用功能組件,也包括一些工具類。組件開發主要依賴于iview基礎組件和lodash工具庫,部分特殊業務有使用到monaco-editor、codemirror等。

因此,現階段我們公司的技術棧相關生態主要為 vue2,本系列也會以公司的vue2項目經歷為依托進行記錄。不過結合當下前端整體快速發展的現實情況下考慮,在項目實施落地之后,對相關生態具有足夠清晰地掌握之后,以vue3react為技術背景的探索也值得進一步探索研究。

包依賴

我們公司當前項目平臺主要依賴分析,如下:

  • 核心三大件:vue/vue-router/vuex
  • UI 類:view-designcodemirror、monaco-editorvue-echartsvuedraggble
  • 工具類: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選擇、拼音匹配、列表展示等,該組件專門用于公司核心功能篩選類查找時的使用)

組件管理思考

  1. 基礎組件類:可以參照iviewantd等UI組件庫的方式進行維護管理,聚焦于功能相關,屬于較小型的組件,統一維護管理。
  2. 業務組件類:可以參照pro-component高級組件的管理模式進行處理。
  3. 工具類:可以參照lodash的方式進行維護管理

pro-component的管理模式為大量基于基礎組件封裝而成的高級組件,如:pro-layoutpro-table、pro-form等,每個組件可以作為獨立的npm包作為依賴使用,相互獨立、互不影響。

再思考到對于工具類的管理維護,整體考慮下來,基本確定可以為多包管理的方向(Monorepo

技術調研

圍繞項目技術背景和目標規劃,進行相關技術調研。

相關開源項目分析

  1. 需要考慮的方向是:對于 vue2.x 版本的組件化方案,哪種更為合適(目前市面上有大量的vue3組件庫)
  2. vue2 對于 TS 的考慮 (vue2對ts的支持性一般,是否支持視情況而定)
  3. props 參數 API 的文檔、demo演示,文檔網站建設的考慮
  4. 目錄設計、工程規范、構建發布流程
  • 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、esbuildvite
    • 文檔網站由vitepress構建,并自行編寫了處理markdownvue demo的插件進行處理(vitepress只支持vue3
  • idux
    • vue3 的項目,pnpm + workspace 的方案,技術都比較新
    • 版本控制由 lerna管理
    • demodocs的目錄管理很不錯,需要配合腳本實現最終的網站路由和渲染
    • 文檔網站由 vitepress + 自身組件構建,并配合各種腳本實現對應功能
    • 構建工具主要為 gulp
    • 組件開發主要為 tsx形式

monorepo 是什么

  • Monorepo:所有依賴庫完全放入一個項目工程
  • Multirepo:多個依賴包獨立進行 git 管理

monorepo 是把多個項目的所有代碼放到一個 git 倉庫中進行管理,多個項目中會有共享的代碼則可以分包引用。整個項目就是有 root 管理的 dependencies 加上多個 packages,每個 package 也可以在自己的作用域引入自己的 dependencies

該方案可以很好的幫助我們解決不同工作區對于相同依賴的管理,以及我們在開發時,包相互間依賴的管理。

monorepo + lernajs + yarn

Lerna 是一種工具,針對使用 gitnpm 管理多軟件包代碼倉庫的工作流程進行優化。

Lerna 有兩種管理項目的模式:

  • Fixed/Locked 模式 (默認): 所有的包共用一個版本號。
  • Independent mode:不同包獨立使用自己的版本,我們一般采用這種方式。在初始化的時候指定 --independent 參數

一般而言,在pnpm未出現前,相關workspace概念基本是配合 yarn一起使用(yarnworkspace功能),因為 npm7.x之前是沒有 workspace概念的,所以在很長的一段時間里,多包管理方案 monorepo都是與 yarn一起配合使用。

他解決了以下問題:

  • 多業務組件、互相依賴、無法復用
  • 發包流程復雜、版本管理痛苦

依賴管理

在工作區 packages 下面我們有這些包:iview、pro-sqltiptreecomponents,其 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,也是使用比較頻繁的,好處是其功能比較全面,但也存在一些明顯的問題

  1. 不能和pnpm很好的適配,lerna 本身不支持 workspace 協議,導致基于 pnpm 開發的一些倉庫無法使用
  2. lerna version 根據 commit 以及 tag 更新出來的包版本不符合預期
  3. 整個包的體積很大,依賴特別多(輕量版lerna-lite
  4. 相比較pnpm而言,lerna使用起來還是比較的笨重、繁瑣

changeset

Changesets 是一個用于 Monorepo 項目下版本以及 Changelog 文件管理的工具(目前一些比較火的 Monorepo 倉庫都在使用該工具進行項目的發包例如 pnpmmobx 等)

changesets 主要關心 monorepo 項目下子項目版本的更新changelog 文件生成、包的發布。

一個 changeset 是個包含了在某個分支或者 commit 上改動信息的 md 文件,它會包含這樣一些信息:

  • 需要發布的包
  • 包版本的更新層級(遵循 semver 規范)
  • CHANGELOG 信息

總的來說,changeset的工作內容還是比較明確的,我們需要直接參與的就是包版本的管理與發布,對于changelog的文件生成會由腳本自動處理,我們無需關心(需要按照commitlint規范提交message,這里到時我們通過工程化的方式進行控制即可)

版本控制管理小結

  1. 使用 yarn + monorepo的項目一般使用lerna配合進行依賴和版本管理
  2. 采用 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 + changesetmonorepo多包管理方案。

實踐篇相關文章見下一篇,歡迎關注(已經在寫了??~)。

資料

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容