2020年4月,有讚美業的前端團隊歷經7個月時間,完成了美業PC架構從單體SPA到微前端架構的設計、遷移工做。PPT在去年6月份就有了,如今再整理一下造成文章分享給你們。
目錄
Part 01 「大話」微前端
把這個事情的來龍去脈講清楚
微前端是什麼
想要回答這個問題直接給一個定義其實沒那麼難,可是沒接觸過的同窗未必理解。因此須要先介紹一下背景,再解釋會更容易明白。git
這張圖,展現了軟件開發前端後分工的三個時期:github
- 單體應用:在軟件開發初期和一些小型的Web網站架構中,前端後端數據庫人員存在同一個團隊,你們的代碼資產也在同一個物理空間,隨着項目的發展,咱們的代碼資產發展到必定程度就被變成了巨石。
- 先後端分離:前端和後端團隊拆分,在軟件架構上也有了分離,彼此依靠約定去協做,你們的生產資料開始有了物理上的隔離。
- 微服務化:後端團隊按照實際業務進行了垂直領域的拆分單一後端系統的複雜度被獲得分治,後端服務之間依靠遠程調用去交互。這個時候前端須要去調用後端服務時候,就須要加入一層API網關或者BFF來進行接入。
如今不少互聯網公司的研發團隊的工做模式更靠近這種,把整個產品拆分紅多個阿米巴模式的業務小組。
在這種研發流程和組織模式下,後端的架構已經經過微服務化造成了拆分可調整的形態,前端若是還處於單體應用模式,不談其它,前端的架構已經給協做帶來瓶頸。
另外 Web 3.0 時代來臨,前端應用愈來愈重,隨着業務的發展迭代和項目代碼的堆積,前端應用在勤勞的生產下演變成了一個龐然大物。人關注複雜度的能力有限,維度大概維持在5~8左右。單體應用聚合的生產資料太多,帶來複雜性的維度太多,也容易引起更多的問題。簡而言之,傳統的SPA已經沒辦法很好的應對快速業務發展給技術底層的考驗。
咱們的產品和前端項目也一樣遇到了這個問題。如何解決這個問題呢?
其實後端的發展已經給出了可借鑑的方案,在理念上參照微服務/微內核的微前端架構應時而生。
想要解決這個問題,在吸引力法則的指引下咱們遇到了微前端架構,也驗證了它的確幫助咱們解決了這個難題。 web
如今給出咱們的微前端這樣一種定義:數據庫
微前端是一種相似於微內核的架構,它將微服務的理念應用於瀏覽器端,即將 Web 應用由單體應用轉變爲多個小型前端應用聚合爲一的應用。多個前端應用還能夠獨立運行、獨立開發、獨立部署。
背景
- 美業PC做爲一個單體應用經歷4年迭代開發,代碼量和依賴龐大,純業務代碼經統計有60多萬行
- 工程方面,構建部署的速度極慢,開發人員本地調試體驗差效率低,一次簡單的構建+發佈須要7+8=15分鐘以上
- 代碼方面,業務代碼耦合嚴重,影響範圍難以收斂,屢次帶來了「蝴蝶效應」式的的線上Bug和故障
- 技術方面,通用依賴升級帶來的改動和迴歸成本巨大,涉及例如Zent組件、中臺組件等依賴包相關的平常需求和技術升級幾乎不可推進
- 測試方面,單應用應對多人和多項目發佈,單應用發佈總和高且很是頻繁,每次的集成測試都有衝突處理和新問題暴露的風險
- 組織方面,單應用也沒法很好應對業務小組的開發組織形式,邊界職責不清晰且模塊開發易干擾
- 架構方面,前端沒法和後端造成對應的領域應用開發模式,不利於業務的下沉,也沒法支持前端能力的服務化和對技術棧的演進依賴
整體來講,臃腫的單體應用模式,給開發人員帶來了沒法忍受的難處,給快速支撐業務帶來了很大的瓶頸,也沒有信心應對接下來的業務的繼續拓展。對美業PC進行架構調整就是很是迫切和有價值的事情了npm
目標
- 業務架構層面,圍繞美業PC的業務形態、項目架構以及發展趨勢,將大型多團隊協同開發的前端應用視爲多個獨立團隊所產出功能的組合。
- 技術架構層面,解耦大型前端應用,拆分紅基座應用、微前端內核、註冊中心、若干獨立開發部署的子系統,造成分佈式體系的中心化治理系統。
- 軟件工程方面,保證漸進式遷移和改造,保證新老應用的正常運行。
達成價值
業務價值
- 實現了前端爲維度的產品的原子化,若是整合新業務,子應用能夠快速被其餘業務集成
- 以業務領域劃分,讓組織架構調整下的項目多人協做更職責清晰和成本低,且適應組織架構調整
- 減慢系統的熵增,鋪平業務發展道路。
工程價值
- 實現了業務子應用獨立開發和部署,構建部署的等待耗時從15分鐘降到了1分半
- 支持漸進式架構,系統子應用之間依賴無關,能夠單個升級依賴,技術棧容許不一致,技術迭代的空間更大
- 前端能力可以服務化輸出
- 架構靈活,新的業務能夠在不增長現存業務開發人員認知負擔的前提下,自由生長無限拓展
缺點
一個架構的設計其實對總體的一個權衡和取捨,除了價值和優點以外,也帶來一些須要去考慮的影響。後端
Part 02 架構與工程
從全局視角把握成果
微前端方案有哪些
- 使用 HTTP 服務器反向代理到多個應用
- 在不一樣的框架之上設計通信、加載機制
- 經過組合多個獨立應用、組件來構建一個單體應用
- 使用 iFrame 及自定義消息傳遞機制
- 使用純 Web Components 構建應用
- 結合 Web Components 構建
每種方案都有本身的優劣,咱們兄弟團隊採用了最原始的網關轉發配置相似 Nginx 配置反向代理,從接入層的角度來將系統組合,可是每一次新增和調整都須要在運維層面去配置。
而 iframe 嵌套是最簡單和最快速的方案,可是 iframe的弊端也是沒法避免的。
Web Components的方案則須要大量的改形成本。
組合式應用路由分發方案改形成本中等且知足大部分需求,也不影響個前端子應用的體驗,是當時比較先進的一種方案。
架構設計選型注意點
- 如何下降系統的複雜度?
- 如何保障系統的可維護性?
- 如何保障系統的可拓展性?
- 如何保障系統的可用性?
- 如何保障系統的性能?
綜合評估以後咱們選用了組合式應用路由分發方案,可是仍然有架構總體藍圖和工程實現須要去設計。
需求分析
- 子應用獨立運行/部署
- 中心控制加載(服務發現/服務註冊)
- 子應用公用部分複用
- 規範子應用的接入
- 基座應用路由和容器管理
- 創建配套基礎設施
設計原則
- 支持漸進式遷移,平滑過渡
- 拆分原則統一,嘗試領域劃分來解耦
應用架構圖
系統拆分
這裏拆分須要說明三個點:
- 獨立部署(服務註冊):上傳應用資源包(打包生成文件)到Apollo配置平臺,是一個點睛之筆
- 服務化和npm包插件化的區別是不須要經過父應用構建來集成,彼此依賴無關,發佈獨立,更加靈活/可靠
- 同時 Apollo 承載了註冊中心的功能,能夠省去子應用的web服務器的這一層,簡化了架構
時序圖
前端流程圖
## Part 03 關鍵技術
落地中有哪些值得一提的技術細節
關鍵技術一覽
咱們按項目拆分來結構化講述,有架構核心、註冊中心、子應用、代碼複用四篇。
其中包含了這些技術點:
- Apollo
- Apollo Cli
- Version Manage
- Sandbox
- RouterMonitor
- MicroPageLoader
- Shared Menu
- Shared Common
[架構核心]消息通訊
[架構核心]路由分發
當瀏覽器的路徑變化後,最早接受到這個變化的是基座的router,所有的路由變化由基座路由 RouterMonitor 掌管,由於它會去劫持全部引發url變化的操做,從而獲取路由切換的時機。若是是apps/xxx/#
以前的變化,只會攔截阻止瀏覽器再次發起網頁請求不會下發,沒有涉及#以前的url變化就下發到子應用,讓子應用路由接管。
[架構核心]應用隔離
主要分爲 JavaScript執行環境隔離 和 CSS樣式隔離。
JavaScript 執行環境隔離:每當子應用的JavaScript被加載並運行時,它的核心其實是對全局對象 window 的修改以及一些全局事件的的改變,例如 JQuery 這個js運行以後,會在 window 上掛載一個 window.$ 對象,對於其餘庫 React、Vue 也不例外。爲此,須要在加載和卸載每一個子應用的同時,儘量消除這種衝突和影響,最廣泛的作法是採用沙箱機制 SandBox。
沙箱機制的核心是讓局部的 JavaScript 運行時,對外部對象的訪問和修改處在可控的範圍內,即不管內部怎麼運行,都不會影響外部的對象。一般在 Node.js 端能夠採用 vm 模塊,而對於瀏覽器,則須要結合 with 關鍵字和 window.Proxy 對象來實現瀏覽器端的沙箱。
CSS 樣式隔離:當基座應用、子應用同屏渲染時,就可能會有一些樣式相互污染,若是要完全隔離 CSS 污染,能夠採用 CSS Module 或者命名空間的方式,給每一個子應用模塊以特定前綴,便可保證不會相互干擾,能夠採用 webpack 的 postcss 插件,在打包時添加特定的前綴。
對於子應用與子應用之間的CSS隔離就很是簡單,在每次應用加載是,就將改應用全部的 link 和 style 內容進行標記。在應用卸載後,同步卸載頁面上對應的 link 和 style 便可。
[架構核心]核心流程圖
咱們把路由分發、應用隔離、應用加載、通用業務邏輯收納到到了微前端內核的二方包中,用做各個業務線複用,在內部達成統一約定。
[註冊中心]Apollo
其實大部分公司在落地微前端方案的時候,並有沒所謂的註冊中心的概念。爲何咱們的微前端也會有註冊中心這個概念和實際存在呢?選型的思考點也主要來自咱們後端的微服務架構。
爲何選擇引入註冊中心增長總體架構的複雜度?
兩個緣由:
- 咱們的子應用之間雖然不須要通訊,可是也存在基座應用須要全部子應用的資源信息的狀況,用來維護路由對應子應用資源地址的映射。大部分公司落地時候,都把子應用的地址信息硬編碼到了基座。這樣子應用增刪改時候,就須要去從新部署基座應用,這違背了咱們解耦的初衷。註冊中心把這份映射文件從基座剝離出來了,讓架構具有了更好的解耦和柔性。
- 要知道咱們的子應用的產物入口是 hash 化的上傳到 CDN 的 JS 文件,同時避免子應用發佈也須要發佈基座應用。有兩個解決方案,一種是增長子應用的 Web 服務器,能夠經過固定的 HTTP 服務地址拿到最新的靜態資源文件。一種就是增長註冊中心,子應用發佈就是推送新的 JS地址給到 註冊中心,子應用的架構就能夠更薄。
須要一個註冊中心的話,咱們也有兩種方案,一種是本身自研一個專門服務於本身的微前端,雖然能夠更加貼合和聚焦,可是做爲註冊中心,高可用的技術底層要求下的熔斷降級等機制必不可少,這些研發難度大成本也高。還有一種是直接應用成熟的提供註冊中心能力的開源項目或者依賴公司的已經存在的技術設施組件。
最後咱們肯定在選用公司內部的基礎技術設施的 Apollo 項目,優點有這麼兩方面。
- 項目自己開源,成熟程度很高,在多環境、即時性、版本管理、灰度發佈、權限管理、開放API、支持端、簡單部署等功能性方面作得很不錯,是一個值得信賴的高可用的配置中心。
- 公司內部針對作了私有化定製和部署,更加適配業務,而且在 Java 和 Node 場景下都有穩定和使用,有維護人員值班。
子應用的打包構建體驗
- 定位:一個子應用構建完是一個帶 hash 的靜態資源,等待被基座加載。
怎麼作:
- 打包一個單入口的靜態資源,同時暴露全局方法給基座
- 每次構建生成帶 hash 的入口 app.js
- 獲取打包產出生成上傳配置
- 根據環境參數上傳到apollo
體驗如何
很是輕量,無須發佈,構建便可
子應用如何推送打包完成的 cdn 地址給 Apollo
- 獲取打包完成的產物的 JSON,獲取入口文件 Hash,和當前項目的基礎信息。
- 基於上述配置生成內容,而後調用 Apollo 平臺開放的 API 上傳到 Apollo。
如何進行多環境發佈及服務鏈協做
- 環境主要分爲測試、預發、生產。
- 打包完成後,根據微前端構建平臺指定環境。
- 推送配置時候,指定 Apollo 對應的環境集羣就行了。
- 基座應用在運行時候,會根據環境與 Apollo 交互對應環境集羣的註冊表信息。
[代碼複用]子應用之間如何複用公共庫
一、添加 shared 爲遠程倉庫
git remote add shared http://gitlab.xxx-inc.com/xxx/xxx-pc-shared.git
二、將 shared 添加到 report 項目中
git subtree add --prefix=src/shared shared master
三、拉取 shared 代碼
git subtree pull --prefix=src/shared shared master
四、提交本地改動到 shared
git subtree push --prefix=src/shared shared hotfix/xxx
注:若是是新建立子應用 1-2-3-4 ;若是是去修改一個子引用 1-3-4
[代碼複用]使用shared須要注意什麼
- 修改了 shared 的組件,須要 push 改動到 shared 倉庫
- 若是一個 shared 中的組件被某個子應用頻繁更新,能夠考慮將這個組件從 shared 中移除,內化到子應用中
[子應用]子應用如何接入
首先,咱們須要明白咱們對子應用的定位:
一個子應用構建完後是一個帶 hash 的靜態資源,等待被基座加載,而後在中心渲染視圖,同時擁有本身的子路由
第一步,根據咱們的模板新建一個倉庫,並置入對應子應用的代碼
第二步,接入shared以及修改一系列配置文件
第三步,進行開發所須要的轉發配置
第四部,運行,並嘗試打包部署
[子應用]子應用能獨立調式嗎?怎麼基座應用聯調?
- 開啓基座,端口和資源映射到本地再調式
- Zan-proxy
- 本地 Nginx 轉發
[子應用]子應用開發體驗
Part 04 項目實施
一個問題從出現到被解決走過的曲折道路
1.立項前的心路
- 看過微前端這個概念,以爲花裏胡哨,玩弄名詞,強行造出新概念。
- 對項目的目前出現的問題有個大概感知(是個問題)
- 從業務出發利用現有知識背景思考解決手段(幾乎無解)
- 回想瞭解過微前端架構的概念和場景,感覺到二者有契合(人生若只如初見)
- 參考行業的解決方案印證,決定用微前端來脫掉膨脹的包袱(原來是該拆了)
- 首先把項目在前端架構優化理了一遍,輸出架構圖(項目總體上探路)
- 接下來梳理各個業務模塊的依賴,看下有哪些(子應用分析)
- 大量和不一樣人的聊天、瞭解、討論,獲取支撐技術選型的信息(外界專家)
- 肯定微前端架構在美業下的落地基本模型(架構基本)
- 進行概要技術設計(具象化)
- 明確迭代範圍
- 技術評審
- 拉幫結夥/分工
- kickoff
- 然而故事纔剛剛開始…
2.參考微前端資料
3.進行PC架構優化計劃
4.風險
預知
- 開發人員投入度不足
- 技術上的不肯定性來更多工期風險
- 細節的技術實現須要打磨耗時超出預期
- 部分功能難以實現
意外
- 對項目架構理解不許確
- 任務拆分和邊界理解不到位
- 測試人員投入不足
- 協做摩擦
5.迭代立項
6.進展
- PC微前端基座應用已上線
- PC數據拆分紅子應用已上線
- 協調中臺前端抽取了美業微前端內核
- 通用工具方法和枚舉的可視化
- 搭配Apollo平臺造成了前端子應用資源的註冊中心
- 子應用接入文檔輸出
- 若干前端技術體系的優化
7.後續計劃