前端插件化架構的思考
- 2020 年 3 月 17 日
- 筆記
前言
有挺長時間沒有更新博客了。一來是公司比較忙,二是自己也在思考一些新的問題。最近一個月,在我腦海回想最多的一個詞語是「插件化架構」。作此文,也是想簡單分享一下我對這個問題的見解。
來自 Webpack 的啟發

去年有參與過 Udacity 前端課程翻譯。其中我翻譯了構建工具部分。Webpack 作為當前主流構建工具,深刻影響着前端開發工作流的變革,正如課程中所說,Webpack 聚合當前前端構建工具所有最熱門的構建技術,甚至可以說,它可以作任何構建工具能作的事。那麼 Webpack 為什麼這麼全能,是因為 Webpack 自身這麼能幹,精通十八般武藝?No !事實上 Webpack 比想像的要「無能」,它的全能是因為它能容萬物只所能。因為 Webpack 本身是一個高度插件化設計的系統,它所表現的能力都藉助於真正身懷絕技的插件的賦能。當然,我這裡所說的插件包含了 Webpack 的內置插件以及通過配置傳入的plugins
。可以說,Webpack 只抽象了一個軟件運行時環境,在不關心和改動這個系統已有代碼的前提下,卻能獨立開發新的插件來充實整個系統的能力。這不就是軟件設計中所要追求的開閉原則的最佳實踐嗎?從 Webpack 的插件化架構設計上所表現出系統良好的開放性,可持續的簡潔性,我自然想像到,為何不在業務系統中採用這樣的插件化架構。
什麼是插件?
插件的英文是plugin
,拆分開是plug(插頭) + in
,現實生活生活中,電源插線板就是這樣」plugin」應用的例子。插線板和通過插頭插入其中的電器構成一個物理世界的插件化系統。插線板通過插孔為「插件」提供電源,而給系統賦予了插件的能力。插入電視的插頭,就擁有看電視的功能,插入冰箱的插頭就具有冷藏食物的功能,插入檯燈的插頭,就具備照明的功能,等等。

一個良好設計的插件化系統和插線板的設計也是一樣一樣的。系統的核心應該是一個可熱插拔的「插線板」,負責給系統接入的插件提供電源(插件API),系統的能力是所有插件能力的聚合。和物理世界的插線板不同是,軟件插線板的插頭沒有數量的限制,也就是說系統可以接入任意數量的插件,意味着它的功能可以無限增加。
如何設計一個插件化的系統?
如果類比插線板的方式設計一個完全插件化的系統,系統將包含三個部分:

- pluggableCore: 插件運行時核心
- pluginApi: 為插件運行提供訪問運行時接口
- plugin: 實現具體功能的插件
其中 pluggableCore
與 pluginApi
是插件化系統的核心部分,或者內部系統,一旦設計好了,不會輕易改變的;而plugin
是外部系統,通過pluginApi
訪問系統內核和全局狀態,實現對應用全生命周期的訪問和控制,只需要遵循接口的定義,plugin
可以將自身能力賦予給任何插件化系統,而不是與某一個特定的系統耦合在一起。 事實上,plugin
也分為兩個部分。我們依然可以與現實世界的電器,例如上文的電視機對比,它包含用於接入電源的插頭和用於播放電視節目物理實現,我們可以看到,雖然電視機是通過電源線才成為整個系統中的插件一員,但它的物理實現遠比電源線複雜的多,甚至可以獨立出來一個複雜的系統。同樣,插件化系統中,插件的功能實現也可能是一個可獨立出來複雜系統,而只是通過一個簡單的插件註冊或應用接口接入另一個插件的系統。
以 webpack 的插件為例。以下是 webpack 插件的模版:
import VeryComplicatedSystem from "VeryComplicatedSystem"; class AwesomeWebpackPlugin { apply(compiler) { VeryComplicatedSystem.apply(compiler); } }
我們看到插件的電源線就是包含apply
方法的類,只需要5
行代碼。而插件的功能實現可以是一個獨立的複雜系統,它還可以是多個插件的聚合。當然從設計上,插件的功能單一更有利於復用。這裡所要表達的是我們應該把插件看作一個可以脫離插件系統本身存在的功能完整軟件系統,讓這個系統成為插件而為另一個插件化系統賦能並不需要重寫內部實現,而只是加上一根符合插件標準的電源線而已。這樣理解,有助於我們設計出更容易復用的插件,同時也可以復用已有軟件的系統。
插件化架構的優勢
正如前文所說,插件化架構一個顯而易見的優勢就是它是開閉原則在跨系統級別的最佳實踐。何為跨系統,如果說設計模式是設計單個系統的最佳實踐,那麼我們已經論證,插件化架構可以鏈接多個子系統,而做到開閉原則。即插件核心和接口不變,系統可以持續接入新插件,來豐富系統的功能。並且,由於新接入的插件是一個獨立的子系統,它可獨立開發,運行和進行版本管理,不會因為接入的系統複雜而增加接入成本。試想,在一個非插件化的系統中,業務系統就算經過良好設計,隨着功能模塊的增多,代碼量激增,暫且不考慮系統構建和測試,我們想要給系統加入新的功能,甚至是修復已有功能的BUG,都會越來越困難和低效,但插件化架構的系統,增加新功能,不是在一個龐大系統代碼庫中,而是在一個較小的系統或代碼倉庫中,因此不管已有系統多複雜,開發新的功能的接入複雜度始終一樣。同時,開發編譯或修複測試一個插件,也比針對整個系統要簡單得多。
插件與組件的區別
插件本質也是一種軟件復用方式,和我們常說的組件區別是復用的維度不同。組件的復用顆粒度更細,它是技術級復用單元,需要經過進一步加工和組合才能成為解決某一類業務問題的完整部分,而插件是一個更加完整可以解決某一類業務問題的子系統,是業務級別的復用單元。
插件化架構的未來
這裡不好說它一定是未來。暫且認為是我設想的未來發展的一種可能。即通過建立一個插件標準,實現業務系統的全面插件化,所有以開發和正在的開發的系統都不是耦合在一個特定的系統,而很難獨立出來,在不同系統復用。全面的插件化,意味着,我們不用重複造業務輪子,團隊和企業可以持續積累自己的插件生態,讓軟件開可以像汽車等工業製造一樣,打造一條標準化裝配的流水線。
結語
本文的觀點僅是個人思考,尚未經過權威的論證。其中的設想有過初期實踐,但尚未形成一個完整的可以推廣的技術框架,這部分筆者本人還在探索中,就不貼出來,感興趣的讀者可以關注我的 github。