成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

大前端時(shí)代下的微前端架構(gòu):增量升級(jí)、代碼解耦、獨(dú)立部署

開發(fā) 架構(gòu)
想做好前端很難,做出可擴(kuò)展的前端,從而讓多個(gè)團(tuán)隊(duì)可以同時(shí)投身于一項(xiàng)復(fù)雜的大型產(chǎn)品項(xiàng)目就更難了。本文將介紹前端領(lǐng)域最近的一項(xiàng)變革:?jiǎn)误w前端架構(gòu)正在過渡到許多較小、較易管理的前端架構(gòu)。

想做好前端很難,做出可擴(kuò)展的前端,從而讓多個(gè)團(tuán)隊(duì)可以同時(shí)投身于一項(xiàng)復(fù)雜的大型產(chǎn)品項(xiàng)目就更難了。本文將介紹前端領(lǐng)域最近的一項(xiàng)變革:?jiǎn)误w前端架構(gòu)正在過渡到許多較小、較易管理的前端架構(gòu)。我們還會(huì)展示這種新的體系結(jié)構(gòu)怎樣提升前端團(tuán)隊(duì)的效率和表現(xiàn)。除了討論這種新趨勢(shì)的好處與代價(jià)外,我們還將介紹一些可行的實(shí)現(xiàn)方案,并深入分析一個(gè)完整的微前端應(yīng)用案例。

[[268516]]

微服務(wù)近年來大受歡迎,許多組織轉(zhuǎn)向了微服務(wù)以克服大型單體后端架構(gòu)的局限。但雖然微服務(wù)在服務(wù)端很流行,很多企業(yè)在前端代碼庫上仍然在沿用問題多多的單體架構(gòu)。

也許你想構(gòu)建一個(gè)漸進(jìn)式或響應(yīng)式的 Web 應(yīng)用,但卻找不到一種將這些功能集成進(jìn)現(xiàn)有代碼中的簡(jiǎn)單途徑;也許你想嘗試 JavaScript 語言的新功能(或者是其他可以編譯為 JS 的某種語言),但你卻無法將關(guān)鍵的構(gòu)建工具融入已有的構(gòu)建流程;或者你只是想擴(kuò)展開發(fā)流程,讓多個(gè)團(tuán)隊(duì)可以同時(shí)開發(fā)一種產(chǎn)品,但現(xiàn)有單體架構(gòu)中的耦合度與復(fù)雜性讓團(tuán)隊(duì)間的合作變得磕磕絆絆。這些都是很現(xiàn)實(shí)的問題,都會(huì)影響你們向客戶交付高質(zhì)量體驗(yàn)的能力。

微前端的定義

最近業(yè)界越來越關(guān)注復(fù)雜的現(xiàn)代化 Web 開發(fā)需要怎樣的整體架構(gòu)和組織結(jié)構(gòu)這個(gè)問題。于是我們開始看到單體前端正在分解為更小、更簡(jiǎn)單的模塊,這些模塊可以各自獨(dú)立開發(fā)、測(cè)試和部署,而它們組合在一起仍然對(duì)客戶表現(xiàn)為一件單一完整的產(chǎn)品。我們將這種技術(shù)稱為 微前端,其定義為:

“微前端是一種架構(gòu)風(fēng)格,其中眾多獨(dú)立交付的前端應(yīng)用組合成一個(gè)大型整體。” 

大前端時(shí)代下的微前端架構(gòu):增量升級(jí)、代碼解耦、獨(dú)立部署

我們認(rèn)為微前端的主要好處有:

  • 更小,更緊密且更易維護(hù)的代碼庫。
  • 組織更具擴(kuò)展能力,其團(tuán)隊(duì)更加獨(dú)立自治。
  • 能夠以更加增量式的風(fēng)格來升級(jí)、更新前端,甚至重寫部分前端代碼。

這些核心優(yōu)勢(shì)與微服務(wù)的優(yōu)勢(shì)基本一致,這也不是什么巧合。

當(dāng)然,軟件架構(gòu)領(lǐng)域沒有免費(fèi)的午餐:一切都要付出代價(jià)。一些微前端實(shí)現(xiàn)可能導(dǎo)致重復(fù)依賴,使用戶不得不下載更多內(nèi)容。此外,大幅提升的團(tuán)隊(duì)自治水平可能會(huì)讓各個(gè)團(tuán)隊(duì)的工作愈加分裂。只不過我們認(rèn)為這些風(fēng)險(xiǎn)都能控制在合理水平上,微前端終究還是利大于弊的。

好處

我們不會(huì)從具體的技術(shù)方法或?qū)嵤┘?xì)節(jié)角度來定義微前端,而是重點(diǎn)關(guān)注它的屬性和好處。

增量升級(jí)

對(duì)于許多組織來說,追求增量升級(jí)就是他們邁向微前端的***步。對(duì)他們來說,老式的大型單體前端要么是用老舊的技術(shù)棧打造的,要么就充斥著匆忙寫成的代碼,已經(jīng)到了該重寫整個(gè)前端的時(shí)候了。一次性重寫整個(gè)系統(tǒng)風(fēng)險(xiǎn)很大,我們更傾向一點(diǎn)一點(diǎn)換掉老的應(yīng)用,同時(shí)在不受單體架構(gòu)拖累的前提下為客戶不斷提供新功能。

為了做到這一點(diǎn),解決方案往往就是微前端架構(gòu)了。一旦某個(gè)團(tuán)隊(duì)掌握了在幾乎不影響舊世界的同時(shí)為生產(chǎn)環(huán)境引入新功能的訣竅,其他團(tuán)隊(duì)就會(huì)紛紛效仿。現(xiàn)有代碼仍然需要繼續(xù)維護(hù)下去,但在某些情況下還要繼續(xù)添加新功能,現(xiàn)在總算有了解決方案。

到***,我們就能更隨心所欲地改動(dòng)產(chǎn)品的各個(gè)部分,并逐漸升級(jí)我們的架構(gòu)、依賴關(guān)系和用戶體驗(yàn)。當(dāng)主框架發(fā)生重大變化時(shí)每個(gè)微前端模塊都可以按需升級(jí),不需要整體下線或一次性升級(jí)所有內(nèi)容。如果我們想要嘗試新的技術(shù)或互動(dòng)模式,也能在隔離度更好的環(huán)境下做試驗(yàn)。

簡(jiǎn)潔、解耦的代碼庫

微前端體系下,每個(gè)小模塊的代碼庫要比一個(gè)單體前端的代碼庫小很多。對(duì)開發(fā)者來說這些較小的代碼庫處理起來更簡(jiǎn)單方便。而且微前端還能避免無關(guān)組件之間不必要的耦合,讓代碼更簡(jiǎn)潔。我們可以在應(yīng)用的限界上下文(詳見下方鏈接)處劃出更明顯的界限,更好地避免無意間造成的這類耦合問題。

當(dāng)然,只靠架構(gòu)更迭本身(比如說“我們改成微前端吧”)并不能自動(dòng)為以往的優(yōu)質(zhì)代碼生成替代品。我們要做的是設(shè)法讓糟糕的決策難以露頭,而讓正確的決策暢通無阻,從而進(jìn)入邁向成功的良性循環(huán)。例如,現(xiàn)在很難跨越限界上下文共享域模型,所以開發(fā)者就不太可能這樣做了。類似地,微前端會(huì)讓開發(fā)者更審慎地把握數(shù)據(jù)和事件在應(yīng)用的各個(gè)部分之間流動(dòng)的方式,其實(shí)就算沒有微前端我們本來也應(yīng)該這樣做的!

獨(dú)立部署

就像微服務(wù)一樣,微前端的一大優(yōu)勢(shì)就是可獨(dú)立部署的能力。這種能力會(huì)縮減每次部署涉及的范圍,從而降低了風(fēng)險(xiǎn)。不管你的前端代碼是在哪里托管,怎樣托管,各個(gè)微前端都應(yīng)該有自己的持續(xù)交付管道;這些管道可以將微前端構(gòu)建、測(cè)試并部署到生產(chǎn)環(huán)境中。我們?cè)诓渴鸶鱾€(gè)微前端時(shí)幾乎不用考慮其他代碼庫或管道的狀態(tài);就算舊的單體架構(gòu)采用了固定、手動(dòng)的按季發(fā)布周期,或者隔壁的團(tuán)隊(duì)在他們的主分支里塞進(jìn)了一個(gè)半成品或失敗的功能,也不影響我們的工作。如果某個(gè)微前端已準(zhǔn)備好投入生產(chǎn),那么它就能順利變?yōu)楫a(chǎn)品,且這一過程完全由開發(fā)和維護(hù)它的團(tuán)隊(duì)主導(dǎo)。 

大前端時(shí)代下的微前端架構(gòu):增量升級(jí)、代碼解耦、獨(dú)立部署

自治團(tuán)隊(duì)

解藕代碼庫、分離發(fā)布周期還能帶來一個(gè)高層次的好處,那就是大幅提升團(tuán)隊(duì)的獨(dú)立性;一支獨(dú)立的團(tuán)隊(duì)可以自主完成從產(chǎn)品構(gòu)思到最終發(fā)布的完整流程,有足夠的能力獨(dú)立向客戶交付價(jià)值,從而可以更快、更高效地工作。為了實(shí)現(xiàn)這一目標(biāo)需要圍繞垂直業(yè)務(wù)功能,而非技術(shù)功能來打造團(tuán)隊(duì)。一種簡(jiǎn)單的方法是根據(jù)最終用戶將看到的內(nèi)容來劃分產(chǎn)品模塊,讓每個(gè)微前端都封裝應(yīng)用的某個(gè)頁面,并分配給一個(gè)團(tuán)隊(duì)完整負(fù)責(zé)。相比圍繞技術(shù)或“橫向”問題(如樣式、表單或驗(yàn)證)打造的團(tuán)隊(duì)相比,這種團(tuán)隊(duì)能有更高的凝聚力。

大前端時(shí)代下的微前端架構(gòu):增量升級(jí)、代碼解耦、獨(dú)立部署

小結(jié)

簡(jiǎn)而言之,微前端是將龐大復(fù)雜的整體分割為更小、更易于管理的模塊,然后明確它們之間的依賴關(guān)系。我們的技術(shù)決策、代碼庫、團(tuán)隊(duì)和發(fā)布流程都應(yīng)該彼此獨(dú)立,無需過多協(xié)調(diào)工作就能自主運(yùn)行并發(fā)展。

案例

假設(shè)要做一個(gè)食品外賣的網(wǎng)站。乍一看這種網(wǎng)站好像很好做,但想要做好需要在諸多細(xì)節(jié)上下足功夫:

  • 應(yīng)該有一個(gè)引導(dǎo)頁面,讓顧客瀏覽并搜索餐館。顧客應(yīng)該能按照一系列參數(shù)(包括價(jià)格、菜品或訂購歷史等)來搜索并過濾餐館。
  • 每家餐館都要有自己的頁面,頁面中要展示菜單,允許客戶自主選餐,還要有折扣、套餐和特殊要求選項(xiàng)。
  • 顧客應(yīng)該有自己的主頁,可以用來查看訂單歷史、跟蹤外賣進(jìn)度并自定義付款選項(xiàng)

每個(gè)頁面都非常復(fù)雜,都應(yīng)該分配一個(gè)專門團(tuán)隊(duì)來負(fù)責(zé),并且每個(gè)團(tuán)隊(duì)都應(yīng)該有足夠的獨(dú)立性。各個(gè)團(tuán)隊(duì)都應(yīng)該能獨(dú)立開發(fā)、測(cè)試、部署和維護(hù)自己的代碼,而不會(huì)與其他團(tuán)隊(duì)發(fā)生沖突或需要其他團(tuán)隊(duì)配合。但在客戶這里,整個(gè)網(wǎng)站仍然應(yīng)該是一個(gè)無縫的整體。

下面我們就會(huì)圍繞這個(gè)案例來展示代碼與場(chǎng)景示例。

集成方法

前文對(duì)微前端的定義相當(dāng)松散,所以有很多方法都可以劃入這個(gè)范疇。本節(jié)將展示一些示例并討論它們的優(yōu)劣。這些方法在架構(gòu)上有共通之處——通常應(yīng)用中的每個(gè)頁面都有一個(gè)微前端,還有一個(gè) 容器應(yīng)用,它有以下功能:

  • 呈現(xiàn)常見的頁面元素,如頁眉和頁腳。
  • 解決了身份認(rèn)證和跳轉(zhuǎn)等跨領(lǐng)域問題。
  • 在頁面上集成多個(gè)微前端,并告訴各個(gè)微前端該何時(shí)何地呈現(xiàn)自己。 
大前端時(shí)代下的微前端架構(gòu):增量升級(jí)、代碼解耦、獨(dú)立部署

服務(wù)器端模板組合

先來介紹一種非常新穎的前端開發(fā)方法——就是在服務(wù)器上使用多個(gè)模板或片段呈現(xiàn) HTML。首先我們要有一個(gè) index.html,其中包含所有常見的頁面元素;然后使用服務(wù)器端包含從 HTML 片段文件中插入的特定頁面內(nèi)容:

  1. <html lang="en" dir="ltr"
  2.  <head> 
  3.  <meta charset="utf-8"
  4.  <title>Feed me</title> 
  5.  </head> 
  6.  <body> 
  7.  <h1> Feed me</h1> 
  8.  <!--# include file="$PAGE.html" --> 
  9.  </body> 
  10. </html> 

我們使用 Nginx 提供此文件,通過匹配正在請(qǐng)求的 URL 來配置 $PAGE 變量:

  1. server { 
  2.  listen 8080; 
  3.  server_name localhost; 
  4.  root /usr/share/nginx/html; 
  5.  index index.html; 
  6.  ssi on
  7.  # Redirect / to /browse 
  8.  rewrite ^/$ http://localhost:8080/browse redirect; 
  9.  # Decide which HTML fragment to insert based on the URL 
  10.  location /browse { 
  11.  set $PAGE 'browse'
  12.  } 
  13.  location /order { 
  14.  set $PAGE 'order'
  15.  } 
  16.  location /profile { 
  17.  set $PAGE 'profile' 
  18.  } 
  19.  # All locations should render through index.html 
  20.  error_page 404 /index.html; 

這是相當(dāng)標(biāo)準(zhǔn)的服務(wù)器端組合方法。它之所以可以算作微前端,是因?yàn)槲覀兛梢杂纱藖矸指畲a,讓每部分代碼代表一個(gè)自包含的域概念,并由一個(gè)獨(dú)立的團(tuán)隊(duì)負(fù)責(zé)。這里沒有展示各個(gè) HTML 片段文件最終如何在 Web 服務(wù)器上呈現(xiàn),實(shí)際上它們都有自己的部署管道,改動(dòng)某個(gè)頁面并不會(huì)影響其他內(nèi)容。

想要更高獨(dú)立性的話,可以為每個(gè)微前端單獨(dú)安排一個(gè)服務(wù)器負(fù)責(zé)呈現(xiàn)和服務(wù),再安排一個(gè)服務(wù)器專門向其他服務(wù)器發(fā)出請(qǐng)求。如果能緩存好各個(gè)響應(yīng)就不會(huì)增大延遲。 

大前端時(shí)代下的微前端架構(gòu):增量升級(jí)、代碼解耦、獨(dú)立部署

這個(gè)例子說明微前端不一定是一種新技術(shù),也不一定很復(fù)雜。只要我們的設(shè)計(jì)決策能為代碼庫和團(tuán)隊(duì)賦予更多自主權(quán),那么不管怎樣的技術(shù)棧都能為我們帶來類似的收益。

構(gòu)建時(shí)集成

還有一種方法是將每個(gè)微前端作為一個(gè)包來發(fā)布,并讓容器應(yīng)用將它們?nèi)孔鳛閹煲蕾嚢M(jìn)去。下面展示了容器的 package.json 查找本文示例應(yīng)用的方法:

  1.  "name""@feed-me/container"
  2.  "version""1.0.0"
  3.  "description""A food delivery web app"
  4.  "dependencies": { 
  5.  "@feed-me/browse-restaurants""^1.2.3"
  6.  "@feed-me/order-food""^4.5.6"
  7.  "@feed-me/user-profile""^7.8.9" 
  8.  } 

這種辦法初看上去挺不錯(cuò)。它通常會(huì)生成一個(gè)可部署的 Javascript 包,允許我們從各種應(yīng)用中刪除常見的重復(fù)依賴。但這意味著我們修改產(chǎn)品的任何部分時(shí)都必須重新編譯和發(fā)布所有微前端。這種 齊步走的發(fā)布流程 在微服務(wù)里已經(jīng)夠讓我們好受了,所以我們強(qiáng)烈建議不要用它來實(shí)現(xiàn)微前端架構(gòu)。我們好不容易在開發(fā)和測(cè)試階段實(shí)現(xiàn)了解耦和獨(dú)立,可別再在發(fā)布階段又繞回去了。我們得在運(yùn)行時(shí)中也集成微前端。

通過 iframe 在運(yùn)行時(shí)集成

想要在瀏覽器中組合應(yīng)用,一種最簡(jiǎn)單的方法就是用 iframe。iframe 可以輕松地用一系列獨(dú)立的子頁面構(gòu)建整個(gè)頁面。它們的樣式和全局變量也能充分隔離,不會(huì)互相干擾。

  1. <html> 
  2.  <head> 
  3.  <title>Feed me!</title> 
  4.  </head> 
  5.  <body> 
  6.  <h1>Welcome to Feed me!</h1> 
  7.  <iframe id="micro-frontend-container"></iframe> 
  8.  <script type="text/javascript"
  9.  const microFrontendsByRoute = { 
  10.  '/''https://browse.example.com/index.html'
  11.  '/order-food''https://order.example.com/index.html'
  12.  '/user-profile''https://profile.example.com/index.html'
  13.  }; 
  14.  const iframe = document.getElementById('micro-frontend-container'); 
  15.  iframe.src = microFrontendsByRoute[window.location.pathname]; 
  16.  </script> 
  17.  </body> 
  18. </html> 

就像前文提到的服務(wù)器端包含方法一樣,用 iframe 構(gòu)建頁面并不是一種激動(dòng)人心的新技術(shù)。但只要我們能精心分割好應(yīng)用并組建好團(tuán)隊(duì),那么用 iframe 就能實(shí)現(xiàn)前面提到的一系列好處。

很多人不喜歡 iframe,它也的確有一些缺陷。上面提到的簡(jiǎn)單隔離方式確實(shí)降低了它的靈活性。用 iframe 在應(yīng)用的各個(gè)部分之間構(gòu)建集成可能會(huì)很困難,從而讓路由、歷史記錄和深層鏈接變得更加復(fù)雜;它還會(huì)影響頁面的響應(yīng)速度。

通過 JavaScript 在運(yùn)行時(shí)集成

這個(gè)方法非常靈活,應(yīng)用廣泛。每個(gè)微前端都使用<script>標(biāo)記包含在頁面上,并在加載時(shí)暴露全局函數(shù)作為其入口點(diǎn)。接下來容器應(yīng)用決定應(yīng)該加載哪個(gè)微前端,并調(diào)用相關(guān)函數(shù)來告訴微前端該何時(shí)何地呈現(xiàn)自己。

  1. <html> 
  2.  <head> 
  3.  <title>Feed me!</title> 
  4.  </head> 
  5.  <body> 
  6.  <h1>Welcome to Feed me!</h1> 
  7.  <!-- These scripts don't render anything immediately --> 
  8.  <!-- Instead they attach entry-point functions to `window` --> 
  9.  <script src="https://browse.example.com/bundle.js"></script> 
  10.  <script src="https://order.example.com/bundle.js"></script> 
  11.  <script src="https://profile.example.com/bundle.js"></script> 
  12.  <div id="micro-frontend-root"></div> 
  13.  <script type="text/javascript"
  14.  // These global functions are attached to window by the above scripts 
  15.  const microFrontendsByRoute = { 
  16.  '/': window.renderBrowseRestaurants, 
  17.  '/order-food': window.renderOrderFood, 
  18.  '/user-profile': window.renderUserProfile, 
  19.  }; 
  20.  const renderFunction = microFrontendsByRoute[window.location.pathname]; 
  21.  // Having determined the entry-point function, we now call it, 
  22.  // giving it the ID of the element where it should render itself 
  23.  renderFunction('micro-frontend-root'); 
  24.  </script> 
  25.  </body> 
  26. </html> 

上面是一個(gè)簡(jiǎn)單的示例,展示了基本的技巧。相比構(gòu)建時(shí)集成,這里我們可以獨(dú)立部署各個(gè) bundle.js 文件。相比 iframe,我們?cè)跇?gòu)建微前端之間的集成時(shí)有充分的靈活度。我們可以用多種方式擴(kuò)展上述代碼,例如按需下載各個(gè) JavaScript 包,或者在呈現(xiàn)微前端時(shí)傳遞出入數(shù)據(jù)。

這種方法同時(shí)具備靈活性與獨(dú)立可部署能力,是我們的***方案。后文將詳細(xì)探討這個(gè)方法。

通過 Web 組件在運(yùn)行時(shí)集成

之前方法的一個(gè)變體是為每個(gè)微前端定義用于容器實(shí)例化的 HTML 自定義元素,而非定義要調(diào)用的容器的全局函數(shù)。

  1. <html> 
  2.  <head> 
  3.  <title>Feed me!</title> 
  4.  </head> 
  5.  <body> 
  6.  <h1>Welcome to Feed me!</h1> 
  7.  <!-- These scripts don't render anything immediately --> 
  8.  <!-- Instead they each define a custom element type --> 
  9.  <script src="https://browse.example.com/bundle.js"></script> 
  10.  <script src="https://order.example.com/bundle.js"></script> 
  11.  <script src="https://profile.example.com/bundle.js"></script> 
  12.  <div id="micro-frontend-root"></div> 
  13.  <script type="text/javascript"
  14.  // These element types are defined by the above scripts 
  15.  const webComponentsByRoute = { 
  16.  '/''micro-frontend-browse-restaurants'
  17.  '/order-food''micro-frontend-order-food'
  18.  '/user-profile''micro-frontend-user-profile'
  19.  }; 
  20.  const webComponentType = webComponentsByRoute[window.location.pathname]; 
  21.  // Having determined the right web component custom element type, 
  22.  // we now create an instance of it and attach it to the document 
  23.  const root = document.getElementById('micro-frontend-root'); 
  24.  const webComponent = document.createElement(webComponentType); 
  25.  root.appendChild(webComponent); 
  26.  </script> 
  27.  </body> 
  28. </html> 

最終結(jié)果與前面的示例很像,主要區(qū)別在于這里以“Web 組件方式”操作。如果你喜歡 Web 組件規(guī)范,喜歡使用瀏覽器提供的功能,那么這也是個(gè)不錯(cuò)的選擇。如果你更喜歡在容器應(yīng)用和微前端之間定義自己的接口,那么前面的示例可能更合適。

樣式

CSS 本質(zhì)上是一種全局、繼承和級(jí)聯(lián)的語言,傳統(tǒng) CSS 也沒有模塊系統(tǒng)、命名空間或封裝;有些功能現(xiàn)在也可用了,但往往缺乏瀏覽器支持。在微前端領(lǐng)域,這些問題往往變得更為嚴(yán)重。例如,如果一個(gè)團(tuán)隊(duì)的微前端有一個(gè)樣式表,上面寫著 h2 { color: black; },另一個(gè)微前端的樣式表卻寫著 h2 { color: blue; },并且這兩個(gè)選擇器都附加到了同一個(gè)頁面,后果就很嚴(yán)重了!這類問題古已有之,但在微前端體系中由于這些選擇器是由不同團(tuán)隊(duì)在不同時(shí)間編寫的,而且代碼可能分散在不同的存儲(chǔ)庫中,所以就更難發(fā)現(xiàn)了。

多年來業(yè)界發(fā)明了很多方法來更好地管理 CSS。有些人會(huì)使用嚴(yán)格的命名約定(例如 BEM 規(guī)范:http://getbem.com/)以確保選擇器只在正確的位置起作用。還有人會(huì)使用 SASS(https://sass-lang.com/)之類的預(yù)處理器,其嵌套的選擇器可以用作一種命名空間。一種較新的方法是使用 CSS 模塊或某種 CSS-in-JS 庫(詳見下方鏈接),以編程方式應(yīng)用所有樣式,確保樣式只會(huì)直接應(yīng)用在開發(fā)者想要的位置上。此外還有 shadow DOM(詳見下方鏈接),它也提供樣式隔離。

具體用哪種方法并不重要,只要讓開發(fā)者可以獨(dú)立編寫樣式,然后各個(gè)樣式集成到同一個(gè)應(yīng)用中時(shí)不起沖突就行了。

共享組件庫

前文提到微前端的視覺一致性是很重要的。一種實(shí)現(xiàn)方法是開發(fā)一個(gè)共享的、可重用的 UI 組件庫。創(chuàng)建這樣一個(gè)庫的主要好處是通過重用代碼和來減少工作量,同時(shí)實(shí)現(xiàn)視覺一致性。此外,這個(gè)組件庫可以當(dāng)作樣式指南來用,它可以是開發(fā)者和設(shè)計(jì)人員之間的一個(gè)很好的協(xié)作橋梁。

但最容易犯的一個(gè)錯(cuò)誤就是過早地搞出來一大堆組件。我們都想創(chuàng)建一個(gè)基礎(chǔ)框架,其中包含所有應(yīng)用所需的所有常見視覺效果。但其實(shí)我們很難提前判斷組件應(yīng)該用什么 API,結(jié)果很多組件都是在白費(fèi)功夫。所以我們更愿意讓團(tuán)隊(duì)在需要的時(shí)候再去創(chuàng)建自己的組件,就算因此產(chǎn)生了一些重復(fù)工作也沒關(guān)系。應(yīng)該順其自然,等組件的 API 都確定下來以后再把重復(fù)代碼收集到共享庫里,這樣就不會(huì)徒勞無功了。

最常見的共享組件是“無聲”的視覺基本元素,如圖標(biāo)、標(biāo)簽和按鈕等。我們還可以共享可能包含大量 UI 邏輯的復(fù)雜組件,例如自動(dòng)完成、下拉搜索字段等;或者是可排序、可過濾的分頁表。但是,請(qǐng)確保共享組件僅包含 UI 邏輯,而不包含業(yè)務(wù)或域邏輯。將域邏輯放入共享庫會(huì)給應(yīng)用之間帶來高度耦合,改動(dòng)起來也更困難。例如,一般來說不該共享 ProductTable,因?yàn)樗鼤?huì)包含關(guān)于“產(chǎn)品”的定義及表現(xiàn)的各種內(nèi)容。這種域建模和業(yè)務(wù)邏輯應(yīng)該屬于微前端的應(yīng)用代碼,不應(yīng)該放到共享庫里。

作為一種內(nèi)部共享庫來說,它的所有權(quán)和管理也自然存在一些棘手的問題。一種管理模式是將其視為共享資產(chǎn),讓“每個(gè)人”都擁有它——實(shí)踐中這通常意味著沒人能真正擁有它。這個(gè)共享庫很快就會(huì)變成一堆不一致的代碼集合,也沒有明確的約定或技術(shù)愿景。反過來說,如果共享庫的開發(fā)工作完全中心化,那么組件的創(chuàng)建者與使用者之間就會(huì)嚴(yán)重脫節(jié)。***的模式應(yīng)該是允許所有人為庫做貢獻(xiàn),但要有一個(gè)保管人(一個(gè)人或一個(gè)團(tuán)隊(duì))負(fù)責(zé)確保這些貢獻(xiàn)的質(zhì)量、一致性和有效性。維護(hù)共享庫需要強(qiáng)大的技術(shù)技能,還需要能協(xié)調(diào)眾多團(tuán)隊(duì)的管理技能。

跨應(yīng)用通信

關(guān)于微前端最常見的一個(gè)問題是如何讓這些微前端互相通信。一般來說,我們建議盡可能減少這類通信需求,因?yàn)樗ǔ?huì)重新引入我們本想避免的耦合度。

換句話說,某種程度的跨應(yīng)用通信是必要的。自定義事件(詳見下方鏈接)允許微前端之間間接通信,從而盡量減少直接耦合;但它也會(huì)讓各個(gè)微前端之間已有的合約更難確定和增強(qiáng)。另一種方法是將回調(diào)和數(shù)據(jù)向下傳遞的 React 模型(這里是從容器應(yīng)用向下傳遞到微前端),它能讓模塊之間的合約更加明確。第三種方法是使用地址欄作為通信機(jī)制,我們將在后面詳細(xì)介紹。

如果你在使用 redux,常見方案是為整個(gè)應(yīng)用提供單個(gè)全局共享存儲(chǔ)。但如果每個(gè)微前端都是自包含的應(yīng)用,那么它們都應(yīng)該有自己的 redux 存儲(chǔ)。Redux 文檔甚至給出了“在大型應(yīng)用中將 Redux 應(yīng)用隔離為組件”的說明。

無論選擇哪種方法,我們都希望微前端可以彼此發(fā)送消息或事件來通信,同時(shí)避免任何共享狀態(tài)。就像在微服務(wù)之間共享數(shù)據(jù)庫一樣,一旦我們共享了數(shù)據(jù)結(jié)構(gòu)和域模型就會(huì)引入大量的耦合,改動(dòng)起來也會(huì)更困難。

這里也有幾種不錯(cuò)的方案選項(xiàng)。最重要的是要時(shí)刻考慮你正在引入怎樣的耦合,以及如何持續(xù)維持模塊之間的合約。就像微服務(wù)之間的集成一樣,你需要在不同應(yīng)用和團(tuán)隊(duì)之間協(xié)調(diào)升級(jí)流程,才能對(duì)集成做出重大改動(dòng)。

你還應(yīng)該考慮如何自動(dòng)驗(yàn)證集成的工作狀態(tài)。一種方法是功能測(cè)試,但它們的實(shí)施和維護(hù)成本很高。或者你可以實(shí)現(xiàn)某種形式的消費(fèi)者驅(qū)動(dòng)合約(詳見下方鏈接),這樣一來,無需在瀏覽器中集成全部微前端并運(yùn)行應(yīng)用,就能讓每個(gè)微前端確定其他微前端需要哪些內(nèi)容。

后端通信

前端應(yīng)用開發(fā)倒是分配給各個(gè)獨(dú)立團(tuán)隊(duì)了,可后端呢?這里就是全棧團(tuán)隊(duì)的價(jià)值所在了,他們從可視代碼到 API 開發(fā)及數(shù)據(jù)庫和基礎(chǔ)架構(gòu)代碼都能自己搞定。有一種不錯(cuò)的模式叫 BFF 模式,其中每個(gè)前端應(yīng)用都有一個(gè)對(duì)應(yīng)的后端,后者只用來滿足前者的需求。

這里有很多變量需要考慮。BFF 可能是自包含的,具有自己的業(yè)務(wù)邏輯和數(shù)據(jù)庫,或者它可能只是下游服務(wù)的聚合器。負(fù)責(zé)微前端及其 BFF 的團(tuán)隊(duì)是否應(yīng)該負(fù)責(zé)一部分下游服務(wù)也是個(gè)問題。如果微前端只有一個(gè)與之對(duì)話的 API,并且該 API 相當(dāng)穩(wěn)定,那么可能就不用構(gòu)建 BFF 了。這里的指導(dǎo)原則是,構(gòu)建某個(gè)微前端的團(tuán)隊(duì)不應(yīng)該依賴其他團(tuán)隊(duì)為他們構(gòu)建內(nèi)容。因此,如果每個(gè)添加到微前端的新功能都需要改動(dòng)后端,那么讓同一個(gè)團(tuán)隊(duì)負(fù)責(zé) BFF 就很合適了。 

大前端時(shí)代下的微前端架構(gòu):增量升級(jí)、代碼解耦、獨(dú)立部署

另一個(gè)常見問題是,如何通過服務(wù)器對(duì)微前端應(yīng)用的用戶進(jìn)行身份驗(yàn)證和授權(quán)操作?顯然,客戶應(yīng)該只需進(jìn)行一次身份驗(yàn)證過程,因此身份驗(yàn)證往往是跨領(lǐng)域問題,應(yīng)該由容器應(yīng)用負(fù)責(zé)。容器可能有某種登錄形式,我們通過它獲得某種令牌。該令牌將由容器控制,并且可以在初始化時(shí)注入各個(gè)微前端。***,微前端可以把令牌及其發(fā)送的請(qǐng)求發(fā)給服務(wù)器,而服務(wù)器可以按需完成驗(yàn)證操作。

測(cè)試

在測(cè)試方面,單體前端和微前端之間沒有太大區(qū)別。一般來說在單體前端上使用的測(cè)試策略都能用在各個(gè)微前端上;也就是說每個(gè)微前端都應(yīng)該有自己的自動(dòng)化全面測(cè)試套件,以確保代碼的質(zhì)量和正確性。

不一樣的是各種微前端與容器應(yīng)用的集成測(cè)試。可以使用你最喜歡的功能 / 端到端測(cè)試工具(例如 Selenium 或 Cypress)來做這部分測(cè)試;應(yīng)該使用單元測(cè)試來覆蓋你的低級(jí)業(yè)務(wù)邏輯和呈現(xiàn)邏輯,然后使用功能測(cè)試來驗(yàn)證頁面是否正確組裝。例如,你可以在特定 URL 上加載完全集成的應(yīng)用,并用硬編碼標(biāo)題來在頁面上聲明相關(guān)的微前端。

如果有跨越不同微前端的用戶操作,那么就可以使用功能測(cè)試來覆蓋它們。但記住功能測(cè)試的重點(diǎn)是驗(yàn)證前端的集成,而非每個(gè)微前端的內(nèi)部業(yè)務(wù)邏輯,后者應(yīng)該已經(jīng)被單元測(cè)試覆蓋了。如上所述,消費(fèi)者驅(qū)動(dòng)的合同可以用來直接指定微前端之間的交互,而不會(huì)破壞集成環(huán)境和功能測(cè)試。

責(zé)任編輯:武曉燕 來源: 今日頭條
相關(guān)推薦

2020-05-14 11:17:51

前端開發(fā)技術(shù)

2020-03-06 10:36:21

JavaScriptCSSHTML

2020-11-20 15:22:32

架構(gòu)運(yùn)維技術(shù)

2023-11-20 08:12:15

2022-10-17 15:21:18

2022-08-19 14:06:56

前端架構(gòu)技術(shù)

2020-10-18 12:00:27

前端開發(fā)架構(gòu)

2017-11-15 09:32:27

解耦戰(zhàn)術(shù)架構(gòu)

2018-04-18 08:47:17

Alluxio構(gòu)建存儲(chǔ)

2019-01-17 10:58:37

2017-11-06 07:01:04

2021-05-18 09:48:58

前端開發(fā)架構(gòu)

2024-04-28 00:00:00

前端代碼Vue

2024-04-18 00:26:14

AI模型語言

2023-04-28 07:41:38

Unity前端架構(gòu)

2023-12-06 07:36:27

前端開發(fā)

2022-08-10 06:52:28

RabbitMQ消息中間件

2025-06-23 11:50:06

2017-12-26 15:52:31

MQ互聯(lián)網(wǎng)耦合

2020-10-08 18:20:54

前端后端架構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 午夜激情在线视频 | 中文字幕亚洲精品 | 国产精品一区二区日韩 | 国产一区| 久久久久久亚洲 | 欧美亚洲视频 | 久久一区二区三区四区 | 成人网在线看 | 国产 欧美 日韩 一区 | 国精品一区二区 | 亚洲欧美日本在线 | 亚洲精品一区二区冲田杏梨 | 日本不卡一区 | 亚洲视频网 | 精品一区二区三区入口 | 日韩在线中文字幕 | www.夜夜骑 | a级性视频 | 国产电影精品久久 | 久久久久国产精品一区 | 亚洲一区二区在线视频 | 欧美久久一区二区三区 | 国产视频三级 | 亚洲成人网在线播放 | 999精品在线 | 国产成人99久久亚洲综合精品 | 一区二区三区四区在线 | 欧美xxxx色视频在线观看免费 | 丝袜久久| 精品日韩一区二区三区 | 一区二区在线不卡 | 久久国产精品无码网站 | 美女视频.| 亚洲激情av | 亚洲一区二区三区四区五区中文 | 在线观看视频91 | 成人久久久 | 亚洲一区二区三区免费视频 | 中文字幕高清av | 久久久久久国产 | 日本不卡视频在线播放 |