博客 / 詳情

返回

微模塊-前端業務模塊化探索,拆解巨石應用的又一利器

大家好,我是Eluxjs的作者,Eluxjs是一套基於“微模塊”和“模型驅動”的跨平台、跨框架『同構方案』,歡迎瞭解...

文前聲明,以下推斷和結論純屬個人探索,鑑於本人知識水平所限,謬誤在所難免,懇請各位大佬不吝賜教...

什麼是前端“微模塊”?

Elux中的『微模塊』是指在Web前端工程中,將代碼和相關資源按照不同的業務功能進行歸類和模塊化。

根據業務功能進行模塊化一直以來都是後端的普遍做法,而Web前端則通常都是按照UI界面的視圖區塊View來進行模塊化,這樣的模塊實際上只是Component組件,不具備獨立自治的能力。究其原因我想是因為在早期Web1.0的時代,前端的職能就是僅僅作為後端API數據的一個Render渲染器,所以前後端的視野和格局出現了分化,也導致很多人説前端根本無架構之説。

然而web生態發展到今天,瀏覽器越來越強大,賦能越來越多,甚至不亞於一個小型操作系統,這時候的Web前端早已不是當初簡單的數據渲染器,狀態管理、會話維持、數據持久化、文件緩存、通信協議...隨着PWA、小程序、快應用的推廣,WebAPP不再是瘦客户端,漸漸成長為大胖小子。

此時的我們應當跳出“渲染器”的井口,而從一個完整的軟件工程來思考我們的前端架構,Web前端不只是一層View、一個GUI,我們需要回歸到與後端一致的以業務領域為驅動的模塊化視角。

micro-module.png

為什麼前端需要“微模塊”?

  • 從開發角度來説:我們需要高內聚、低耦合的鬆散結構體,而不是牽一髮而動全身的巨石應用,這不管是對於開發、維護、還是後期漸進式重構,都至關重要。

    前端Leader:經過一年多的迭代和人員變動,我們代碼已經混亂不堪了,開發越來越吃力,必須要重構,否則玩不下去了!
    產品經理:嗯,我理解,這裏面也有很多是我們需求變更頻繁引起的,我支持你們重構!
    前端Leader:感謝大佬理解,那新需求先停下來,等我們重構好了再迭代吧?
    產品經理:你們重構要多久?
    前端Leader:產品這麼複雜了,估計至少要3個月左右吧。
    產品經理被嚇出一身冷汗:大佬,你要3天還可以考慮,停下來3個月估計公司都要關門了...
    前端Leader:可是產品這麼複雜,幾天時間完成重構是天方夜譚。
    產品經理想了想:這樣把,我每個迭代少安排幾個需求,這樣你們每個月就可以留幾天時間重構了。
    前端Leader:這可不是1+1=2的問題,而是0與1的問題,大佬你不瞭解!
    產品經理:誰説我不瞭解,你們就不能漸進式重構嗎?
    前端Leader:...

    此時如果我們的前端工程是基於“微模塊”,一來可以輕鬆的找到“局部重構”的邊界,二來也可以通過維持“微模塊”的對外接口來無極替換。

  • 從產品角度來説:軟件架構永遠是服務於業務需求的。我們希望我們的產品能像搭積木一樣按需組合,可以快速包裝出各種靈活多樣的套餐,以滿足客户越來越精細化的定製需求。

    某個大型應用包含A,B,C,D,E,F,G等若干功能,原來一直是整體打包出售...
    
    隨着用户需求的多樣化,有的用户僅需要部分功能,於是聰明的前端架構師“小李”利用時下流行的微前端技術,
    將應用拆分成了的 3 個子應用:
    
    - 【基礎應用】包含功能:A
    - 【子應用A】包含功能:B,C,D
    - 【子應用B】包含功能:E,F,G
    
    這樣等於有 3 個套餐可以供客户選擇:
    
    - 套餐A:基礎應用 + 子應用A
    - 套餐B:基礎應用 + 子應用B
    - 套餐C:基礎應用 + 子應用A + 子應用B
    
    然而用户的需求越來越精細化,有的需要ABCD,有的需要ACEG,有的需要ABDF...
    而且同一個功能可能還存在需求版本的不同,這讓“小李”無可適從。

    現在我們利用“微模塊”來幫助小李解決問題:

    • 將各種獨立的業務功能封裝成不同的微模塊:A,B,C,D,E,F,G
    • 將各種微模塊按需求迭代版本,發佈成NPM包
    • 某客户需要 A,C1(C功能的某個版本),E2(E功能的某個版本),G 功能,我們單獨為該客户創建一個聚合工程分支,安裝相應版本的微模塊:npm install A C@1 E@2 G

building-blocks.jpeg

我們知道世界上有一款建站神器wordpress,曾經號稱世界上50%的網站都是由它創建的,我認為它的成功秘訣就是社區模版機制和功能插件化,你要什麼功能都總能找到“前端+後端”一起打包安裝的插件,這也類似於“微模塊”的概念。
  • 從工程的角度來説:“微模塊”是跨工程、跨項目共享通用業務代碼的理想決方案,對於跨端、跨平台複用業務邏輯尤其有用。

micro-share.png

前端“微模塊”的劃分原則與邊界

  • 擁有高內聚、低耦合的工程結構。
  • 擁有獨立自治的子域邏輯。

micro-domain.png

從圖中可以看到,每個微模塊負責定義和維護自己領域內的事務,並且麻雀雖小,五臟俱全,擁有獨立的路由解析、狀態管理、數據模型、控制器、視圖、組件、資源、業務實體、API管理等等...總之,所有與自己領域相關的資源都被內聚到了一起。

以下是某巨石應用的SRC目錄,其特點是以“文件職能”作為一級分類、“功能模塊”作為次級分類:

├─ src
│  ├─ api                 # API接口管理
│  ├─ assets              # 靜態資源文件
│  ├─ components          # 全局組件
│  ├─ config              # 全局配置項
│  ├─ enums               # 項目枚舉
│  ├─ hooks               # 常用 Hooks
│  ├─ language            # 語言國際化
│  ├─ layout              # 框架佈局
│  ├─ routers             # 路由管理
│  ├─ store               # store
│  ├─ styles              # 全局樣式
│  ├─ typings             # 全局 ts 聲明
│  ├─ utils               # 工具庫
│  ├─ views               # 項目所有頁面
│  ├─ App.vue             # 入口頁面
│  └─ main.ts             # 入口文件

以下是Elux中基於微模塊的SRC目錄,其改進是將“功能模塊”作為一級分類,“文件職能”作為次級分類:

src
├── modules
│      ├──  ModuleA
│      │     ├── entities
│      │     ├── assets
│      │     ├── api
│      │     ├── utils
│      │     ├── language
│      │     ├── components
│      │     ├── views
│      │     ├── model.ts
│      │     └── index.ts
│      │ 
│      ├── ModuleB
│      ├── ModuleC

微模塊的台前與幕後

前端開發最終呈現的是UI界面,但這只是表象,支撐UI界面渲染和交互的是背後一系列state、model、controller等幕後英雄,它們根據自己所屬不同領域被封裝在各個微模塊中,UI既然與它們唇齒相依,必然也將跟隨它們內聚在一起。

View和Component

本質上説View就是一個Component,但我們從架構的思維來區分它們:

  • View:業務視圖,它用來表現業務規則與邏輯,通常能夠較為獨立和完整的解決某一領域問題。
  • Component:UI組件,它用來表現渲染規則與交互邏輯,通常不與具體業務直接相關,可複用在各種不同業務場景中。

所以在“微模塊”的架構中,豐富多彩的UI界面由一個個單一職責的View聚合而成,每個View同樣依據自身所解決的領域問題而被分散在各個微模塊中,這裏面有幾個注意點:

  • 領域性:View被歸屬到不同微模塊的原則是其解決的問題領域,而不是視覺上的幾何空間。View可以在視覺上被拆裝、聚合、嵌套,這並不影響它們所屬微模塊。
  • 完整性:一個View通常能解決一個較為獨立和完整的問題,View與View之間是較為鬆散的關係,如果2個View之間聯繫緊密,那就不應當拆分它們。

micro-view.png

不以視覺延伸和幾何空間作為View的微模塊歸屬原則:如下圖所示,假設有一個View用來展示用户資料,我們將其放在UserModule這個微模塊中,稱其為UserModule.DetailView,但你發現其中又包含一個該用户發表文章的列表,你當然可以把這個列表單獨提取出來作為一個新的View。從視覺上來看,它似乎和用户資料是連在一起的,似乎可以和UserModule.DetailView放在同一個微模塊中;但我們從它解決的問題來看,它屬於文章領域,而與用户領域關係並不大,所以我們最好將其放在ArticleModule中,稱其為ArticleModule.ListView

micro-view2.png

前端“微模塊”的實現方案

  1. 定義和創建微模塊,可藉助於Eluxjs框架,當然你發現了其它框架也可以。
  2. 管理微模塊,可藉助於NPM倉庫。
  3. 使用微模塊,可藉助於打包工具:

    • 靜態編譯:微模塊作為一個NPM包被安裝到工程中,通過打包工具(如webpack)正常編譯打包即可。這種方式的優點是代碼產物得到打包工具的各種去重和優化;缺點是當某個模塊更新時,需要整體重新打包。
    • 動態注入:利用Module Federation,將微模塊作為子應用獨立部署,與時下流行的微前端類似。這種方式的優點是某子應用中的微模塊更新時,依賴該微模塊的其它應用無需重新編譯,刷新瀏覽器即可動態獲取最新模塊;缺點是沒有打包工具的整體編譯與優化,代碼和資源容易重複加載或衝突。

micro-install.png

微模塊 vs 微前端

從本意上來説,微模塊只是一種工程結構和模塊化方案,而微前端只是它的一種應用場景之一。微模塊架構不僅可以用來構建複雜的單體應用,也可以結合Module Federation實現多子應用獨立部署的“微前端”。

如果單獨就微模塊 + ModuleFederation方式實現的微前端,與傳統意義上的qiankun、icestark等微前端方案相比,微模塊方式勝在粒度更細、更靈活、更輕巧,而傳統方式則勝在隔離性更好。

想到一個非常形象的比喻:

IFrame vs 微前端 vs 微模塊 可類比於 進程 vs 線程 vs 協程

從左至右:越來越輕量化,隔離性逐漸變弱,靈活性逐漸增加。所以魚與熊掌不可兼得,具體哪種方案最適合還得看不同的產品需求。

微模塊之間的通信

  • 微模塊之間按照某些規則和約定共享同一個Runtime,強制隔離性較弱,所以它們之間的通信是輕量級的,可以相互引用與調用。
  • 建議觀察者模式,或者使用事件總線模式來保持微模塊之間的鬆散關係,這是另一個故事,可參考Eluxjs中的ActionBus
  • 微模塊高內聚、低耦合的劃分原則,也意味着微模塊之間不會出現特別複雜的互動與交流(互動密切的微模塊應當合併)。

落地與實戰

光練不説傻把式,光説不練假把式,這裏先把思路概念要説的説完,下面就要開始出實例了。先喝口水,請聽下回分解...🤩🤩急性子也可以直接去Eluxjs官網,看看Demo,不吝賜教...

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.