準備到項目的收尾階段,小編終於活過來了。在寫這個項目的後期,小編遇到了刷新頁面重複調用接口的問題,排查了挺久都沒有能找出具體緣由。後來詢問了組內同事,才知道是微前端的問題。css
說實話,小編也是第一次遇到這種類型的問題,因而就去查了下資料,發現有篇文章寫的還闊以,藉此機會share給須要的小夥伴。html
原文連接:www.ayqy.net/blog/micro-…前端
爲了解決龐大的一整塊後端服務帶來的變動與擴展方面的限制,出現了微服務架構(Microservices)
:git
微服務是面向服務架構(SOA)的一種變體,把應用程序設計成一系列鬆耦合的細粒度服務,並經過輕量級的通訊協議組織起來。 具體地,將應用構建成一組小型服務。這些服務都可以獨立部署、獨立擴展,每一個服務都具備穩固的模塊邊界,甚至容許使用不一樣的編程語言來編寫不一樣服務,也能夠由不一樣的團隊來管理。github
然而,愈來愈重的前端工程也面臨一樣的問題,天然地想到了將微服務思想應用(照搬)到前端,因而有了微前端(micro-frontends)
的概念:npm
Micro frontends, An architectural style where independently deliverable frontend applications are composed into a greater whole.編程
即,一種由獨立交付的多個前端應用組成總體的架構風格。具體的,將前端應用分解成一些更小、更簡單的可以獨立開發、測試、部署的小塊,而在用戶看來仍然是內聚的單個產
品:後端
Decomposing frontend monoliths into smaller, simpler chunks that can be developed, tested and deployed independently, while still appearing to customers as a single cohesive product.架構
簡單來說,微前端的理念相似於微服務:app
In short, micro frontends are all about slicing up big and scary things into smaller, more manageable pieces, and then being explicit about the dependencies between them.
將龐大的總體拆成可控的小塊,並明確它們之間的依賴關係。關鍵優點在於:
漸進地升級、更新甚至重寫部分前端功能成爲了可能
;理想的代碼天然是模塊清晰、依賴明確、易於擴展、便於維護的……然而,實踐中出於各式各樣的緣由:
總存在一些不那麼理想的代碼:
而要對這些代碼進行完全重構的話,最大的問題是很難有充裕的資源去大刀闊斧地一步到位
,在逐步重構的同時,既要確保中間版本可以平滑過渡,同時還要持續交付新特性:
In order to avoid the perils of a full rewrite, we’d much prefer to strangle the old application piece by piece, and in the meantime continue to deliver new features to our customers without being weighed down by the monolith.
因此,爲了實施漸進式重構,咱們須要一種增量升級的能力,先讓新舊代碼和諧共存,再逐步轉化舊代碼,直到整個重構完成。
這種增量升級的能力意味着咱們可以對產品功能進行低風險的局部替換
,包括升級依賴項、更替架構、UI 改版等。另外一方面,也帶來了技術選型上的靈活性,有助於新技術、新交互模式的實驗性試錯。
獨立部署的能力在微前端體系中相當重要,可以縮小變動範圍,進而下降相關風險。
所以,每一個微前端都應具有有本身的持續交付流水線(包括構建、測試並部署到生產環境),而且要能獨立部署,沒必要過多考慮其它代碼庫和交付流水線的當前狀態:
就算舊的系統是按固定週期季度發佈或手動發佈的,甚至隔壁團隊誤發佈了一個半成品或有問題的特性也可有可無。也就是說,若是一個微前端已經準備好發佈了,它就應該隨時可發佈,而且只由開發維護它的團隊來定。
P.S.甚至還能夠結合BFF 模式實現更進一步的獨立:
除代碼庫及發佈週期上的解耦以外,微前端還有助於造成徹底獨立的團隊,由不一樣團隊各自負責一塊產品功能從構思到發佈的整個過程,團隊可以徹底擁有爲客戶提供價值所需的一切,從而快速高效地運轉。
爲此,應該圍繞業務功能縱向組建團隊,而不是基於技術職能劃分。最簡單的,能夠根據最終用戶所能看到的內容來劃分,好比將應用中的每一個頁面做爲一個微前端,並交給一個團隊全權負責。與基於技術職能或橫向關注點(如樣式、表單、校驗等)組織的團隊相比,這種方式可以提高團隊工做的凝聚力。
實現上,關鍵問題
在於:
微前端架構中通常會有個容器應用(container application)
將各子應用集成起來,職責以下:
集成方式分爲 3 類:
服務端集成的關鍵在於如何保證各部分模板(各個微前端)可以獨立發佈
,必要的話,甚至能夠在服務端也創建一套與前端相對應的結構:
常見的構建時集成方式是將子應用發佈成獨立的 npm 包,共同做爲主應用的依賴項,構建生成一個供部署的 JS Bundle。
然而,構建時集成最大的問題是會在發佈階段形成耦合
,任何一個子應用有變動,都要整個從新編譯,意味着對於產品局部的小改動也要發佈一個新版本,所以,不推薦這種方式
。
將集成時機從構建時推遲到運行時,就能避免發佈階段的耦合。常見的運行時集成方式有:
雖然直覺上用 iframe 好像不太好(性能、通訊成本等),但在這裏確實是個合理選項,由於 iframe 無疑是最簡單的方式,還自然支持樣式隔離以及全局變量隔離,但這種原生的隔離性,意味着很難把應用的各個部分聯繫到一塊兒
,路由控制、歷史棧管理、深度連接(deep-linking)、響應式佈局等都變得異常複雜,於是限制了 iframe 方案的靈活性。
另外一種最多見的方式是前端路由,每一個子應用暴露出渲染函數,主應用在啓動時加載各個子應用的獨立 Bundle,以後根據路由規則渲染相應的子應用。目前看來,是最靈活的方式。
還有一種相似的方式是Web Components,將每一個子應用封裝成自定義 HTML 元素(而不是前端路由方案中的渲染函數),以得到Shadow DOM帶來的樣式隔離等好處。
子應用之間,以及子應用與主應用間的樣式、做用域隔離是必需要考慮的問題,常看法決方案以下:
資源複用對於 UI 一致性和代碼複用有重要意義,但並不是全部的可複用資源(如組件)都必須在一開始就提出來複用
,建議的作法是前期容許必定程度的冗餘,各個 Bundle 在各自的代碼庫中建立組件,直到造成相對明確的組件 API 時再創建可供複用的公共組件。
另外一方面,資源分爲如下 3 類:
其中,不建議跨子應用複用業務組件
,由於會形成高度耦合,增長變動成本。
對於公共資源的歸屬和管理,通常有兩種模式:
從實踐經驗來看,前者很容易衍變成沒有明確規範,且背離技術願景的大雜燴,然後者會形成資源建立和使用的脫節,比較推薦的模式是開源軟件的管理模式
:
Anyone can contribute to the library, but there is a custodian (a person or a team) who is responsible for ensuring the quality, consistency, and validity of those contributions.
即,全部人都能補充公共資源,但要有人(或一個團隊)負責監管,以保證質量、一致性以及正確性。
經過自定義事件
間接通訊是一種避免直接耦合的經常使用方式,此外,React 的單向數據流模型也能讓依賴關係更加明確,對應到微前端中,從容器應用向子應用傳遞數據與回調函數。
另外,路由參數除了能用於分享、書籤等場景外,也能夠做爲一種通訊手段,而且具備諸多優點:
但原則上,不管採用哪一種方式,都應該儘量減小子應用間的通訊
,以免大量弱依賴形成的強耦合。
每一個子應用都應該有本身的全套測試方案,特殊之處在於,除單元測試、功能測試外,還要有集成測試
:
自下而上造成一個金字塔結構,每一層只需驗證在其下層覆蓋不到的部分便可。
固然,這種架構模式並不是百益而無一害,一些問題也隨之而來:
獨立構建意味着公共資源的冗餘,繼而增長用戶的流量負擔。
沒有很是理想的解決辦法
,一種簡單的方案是將公共依賴從(子應用的)構建產物中剔除,但又會引入構建時耦合:
Now there is an implicit contract between them which says, 「we all must use these exact versions of these dependencies」.
在採用微前端以前,先要考慮幾個問題:
總之,與以前不一樣的是,微前端將產生一堆小的東西,所以須要考慮是否具有采用這種方法所需的技術和組織成熟度
。
相似於微服務之於後端,前端業務在發展到必定規模以後,也須要一種用來分解複雜度的架構模式,因而出現了微服務思想在前端領域的應用
,即微前端。主要目的在於:
最大的意義在於解鎖了多技術棧並存的能力
,尤爲適用於漸進式重構中架構升級過渡期:
Suddenly we are not tightly coupled with one stack only, we can refactor legacy projects supporting the previous stack and a new one that slowly but steadily kicks into production environment without the need of a big bang releases (see strangler pattern).
容許低成本嘗試新技術棧,甚至容許選用最合適的技術棧作不一樣的事情(相似於微服務中容許用不一樣的語言編寫不一樣服務):
we can use different version of the same library or framework in production without affecting the entire application, we can try new frameworks or approaches seeing real performances in action, we can hire the best people from multiple communities and many other advantages.