推開“微前端”的門

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/64/64dbf0fa74472f6491c3f1e54576c44e.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"導讀:","attrs":{}},{"type":"text","text":"“微前端”和“微服務”類似,是這兩年被頻繁提及的名詞。web開發從前後端放在一起的單體應用,演進成前後端分離的SPA,這些改變讓前後端實現了開發解耦、獨立發佈。解耦讓開發、調試、發佈的過程都更加自由靈活,但隨着業務的發展,中大型的SPA逐漸成爲了“巨石應用”(Monolithic Applications),當初因爲前後端分離帶來的“自由”也漸行漸遠,模塊的拆解越來越被需要。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"全文5574字,預計閱讀時間14分鐘","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"本文主要分享兩方面內容:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"思考什麼樣的系統或者前端需要微前端","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"簡述微前端工程中需要關注的一些設計要點","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文僅會從選型和設計上做一些思考總結,「不會」重點介紹以下內容:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"深入介紹某些開源框架並對比","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"如何設計一個非常通用的微前端框架","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"針對某個設計點的實現方式非常詳盡的介紹","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"希望能給正在躊躇是否使用微前端的你一些思路。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"一、什麼樣的系統或者前端團隊需要微前端?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你有下面案例的困境,微前端可能是你的一個選擇。假設一個SPA的前端模塊,包含了ABCD四個模塊,它們錯綜複雜地依賴了多個單獨部署的後端服務,如下圖:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7f/7f777ffca00961d833eaff4ee2909054.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"圖一","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上線當天,你們可能需要梳理一個模塊依賴圖譜,以確定當前的上線順序。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前端模塊A、B、C、D均屬於一個需要整體發佈的SPA;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"模塊C上線依賴服務2、3,模塊D依賴服務1、4;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲模塊C和D需要一起上線,和服務2毫無關係的模塊D、服務1、4都需要等待服務2的發佈。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個SPA模塊將成爲整個上線日忙碌的十字路口,上線、驗證操作需要排隊等待,相關同學不厭其煩。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"更可怕的是,當大家千辛萬苦完成深夜上線後,任何發現了問題需要緊急止損的模塊,都常會帶來雪崩似的回滾操作。如果上線方案准備不完備,還需要臨時確認影響面,梳理回滾操作的圖譜順序。這種消耗掉的止損時間,在一些系統、產品中常常是難以接受的,造成回滾反應鏈的模塊別提壓力有多大了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"結合這個例子,有以下幾個參考點,如果它們恰好命中了你的痛點,微前端會是你的一個「解決辦法」。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"並行開發的模塊數量多","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"當你有多個功能相對解耦獨立的模塊,需要並行開發、發佈。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"協同開發的人員數量多","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"當單個應用開發同學的人數超過一定規模(如大於10人),且每個人負責的子模塊相對固定,人數增多協同效率往往會指數增長。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"協同開發的團隊數量多","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","text":"這是一個有趣的點,和2的區別,主要在“屁股決定腦袋”的“屁股”上。如果你們由於種種原因,不得不跨團隊維護同一個工程,那你們可能面對着開發權限、規範、節奏等很多協同點,這些協同有時候會因爲跨團隊效率更低。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":7,"align":null,"origin":null},"content":[{"type":"text","text":"發佈頻率高","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":8,"align":null,"origin":null},"content":[{"type":"text","text":"這條需要與1和2結合起來看,人數多但是發佈頻率較低的模塊,協同溝通等成本就成了僞命題。當然,多人協同開發的應用,大概率是發展中的業務,需要頻繁的迭代。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":9,"align":null,"origin":null},"content":[{"type":"text","text":"需要跨技術棧","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":10,"align":null,"origin":null},"content":[{"type":"text","text":"這一點可能是很多團隊選擇微前端的重要理由。你們可能維護了一個經營多年、系統繁雜、技術棧較陳舊的系統,你希望能引入新的技術棧但又沒有人力一下子重寫系統。先把系統微前端化,再漸進式地分子模塊重寫,會是一個工作節奏可控、質量風險較低的辦法。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":11,"align":null,"origin":null},"content":[{"type":"text","text":"但是,針對跨技術棧這個命題,對於單獨產品或平臺來說,我個人認爲有個 「“陷阱”」 。從團隊長遠開發效率、平臺的性能優化空間、體驗一致等多種角度來看,長期不加管控的跨技術棧是有風險的。一些小粒度的抽象(組件、業務模塊等)難以被高效複用,一些升級難以被直接應用到全局等。這些問題導致的效率下降,可能會掩蓋獨立開發、發佈帶來的效率提升。Martin Fowler在介紹微前端的收益之一時時也寫的是 「Incremental upgrades」 ,漸進式更新不等同於永久區別。因此,除非你做的只是一個門戶,對子模塊的一致性沒有很高的協同要求,我更建議跨技術棧是漸進式遷移的中間態。","attrs":{}}]}]}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"二、選擇微前端需要關注的設計點","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經過了checklist,如果一些問題讓你決定微前端改造,下面的一些應用設計點也許能對你有所幫助。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2.1 主模塊與子模塊","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先你需要一個主模塊,它可能是一個HTML,一個或一組top app bundle,我們稱它爲「APP Shell」或「Nutshell」(https://martinfowler.com/articles/micro-frontends.html#InANutshell),你的頂層邏輯需要加載一些前置依賴,初始化entry內容,根據路由等信息加載渲染子模塊。關於運行時加載sub app的實現方式有很多,這裏簡單列舉幾個,就不一一展開了:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"subapp是一個iframe,最簡單暴力的實現方式,同時有iframe實現頁面的一切限制。優化空間、頂層控制力有限,個人不推薦。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"subapp是web component,跟隨路由切換實例化組件。你需要考慮瀏覽器的兼容性限制。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"subapp是一個獨立發佈的子bundle。子bundle需要定義一些生命週期hook,如register、mount、unmount等。這個方法應該是比較普及使用的。","attrs":{}}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2.2 單實例 vs 多實例","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據運行時實例的數量,業界有了「單實例」和「多實例」兩種APP。「單實例」是指,在運行時同一時刻僅一個子模塊被激活,下圖二是一個常見場景,子模塊通常跟隨路由裝載/卸載。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a3/a3b103433dd88973fc3c31516c235298.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"圖二","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"「多實例」則是運行時同一時刻可能會有多個子模塊實例在激活狀態。下圖三是一個場景,路由和子模塊不再是一對一關係,可能成了多對多。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/37/37698f8eca1828dc8aa155ae9abfd070.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"圖三","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實現方案是由業務和服務拆解來決定的,建議把功能內聚、維護團隊相對收斂的模塊單獨拆分,然後再看運行時是否需要在同個路由下同時出現「多實例」。無論運行時有幾個子模塊,你都需要一個路由和頁面內容的映射關係。這個關係對某些APP來說可能是開發編譯時可以預判的枚舉關係,那麼你可能需要維護一個如下的map:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"// 單實例-示意const subAppRoutes = { route1: 'https://your.static.server.com/app1/index.js', route2: 'https://my.static.server.com/app2/index.js', route3: 'https://other.static.server.com/app3/index.js'};\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"// 多實例-示意const subAppRoutes = { route1: [  {   subApp: 'https://your.static.server.com/app1/index.js',   layout: {              // 省去佈局描述信息          }  },  {   subApp: 'https://my.static.server.com/app2/index.js'  } ], route2: [  {   subApp: 'https://your.static.server.com/app2/index.js'  },  {   subApp: 'https://my.static.server.com/app3/index.js'  } ], route3: [  {   subApp: 'https://my.static.server.com/app3/index.js'  } ]}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"入口模塊會根據路由和相關配置,完成資源的加載,並根據生命週期協議掛載子APP。前面已經提到,你需要一個路由。可以通過一些路由前綴約束規則來避免子模塊衝突。關於路由的設計實現文章很多,不是本文重點,暫且按下不表。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你的系統非常靈活、頁面類型和數量不可收斂,那麼你可能需要一個動態存儲的數據結構以及服務來代替瀏覽器中的location 路由,通過定義系統的頁面結構schema,來描述當前頁面中每個微前端模塊需要放置的位置和佈局(如上面“多實例-示意”中的route1)。schema的引入讓低碼(Low-code)開發成爲可能,未來你通過簡單的數據結構配置,即可用微前端模塊拼湊出一個APP頁。當然,這需要有額外的存儲和業務邏輯,系統的複雜度會增加。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2.3 子模塊通信","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大多數系統的子模塊還是不能規避一些互相影響的。假設你有一個功能模塊A,和內容推薦模塊B,B的內容需要根據A的操作連鎖反應。有幾種常見的做法:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"全局數據共享","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"有個全局store,A將數據變更寫入store,B監聽store的change並做出響應,單向數據流的設計能讓開發調試變的更加容易,當然你需要規避分模塊對單一store內容的衝突問題,這個和路由衝突的解決方案類似,比如增加一些命名空間。剩下的內容和你接觸過的各種單一store的設計都類同,不再贅述。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"事件通信","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"提供全局的EventBus能力,A派發事件,B接受事件並響應。這是個平平無奇的事件通信,但在微前端實現中有一個點需要關注。各子模塊之間加載和實例化的過程大多是獨立的、異步的,A發佈事件時,B還沒有實例化完成,那麼這個消息可能會被漏掉。通過1中共享數據的方式可以解決大部分問題,如果你更喜歡用事件通信來解決,則需要在設計實現EventBus的時候考慮這個功能,例如緩存事件隊列,當B在啓用事件監聽的時刻,回顧一下緩存事件隊列中有沒有已經派發且需要被響應的事件。","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然,你可能還有一些思路,比如二次封裝。在A和B之上封裝C,負責兩個模塊的調度,但這個辦法和上述兩條不是一個層面,比如C的實現也往往需要和A、B的通信或者數據共享。同時過度二次封裝缺點也很明顯,封裝的模塊數量會隨着業務的組合膨脹,難以收斂維護。在實踐過程中,1和2的能力你應該「都需要建設」。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"三、性能優化小貼士","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了平日性能優化的常規操作,在微前端架構下,有兩個小點值得關注。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3.1 多實例按需渲染","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你的APP頁面子模塊較多,可以考慮按需渲染。如下圖四的場景,可以根據視口和模塊位置決策當前模塊是否需要實例化渲染。以此來減少首屏瀏覽器scripting和資源加載的時間。當然,隨着視窗滾動延遲渲染模塊的體驗,也可以通過閒時預渲染來優化解決。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/73/73da4f6f858f892d86842833fb31c26e.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"圖四","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3.2 重複打包優化","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微前端的一個問題是,一些公共能力如何處理。“微”帶來了自由,但自由過頭了可能就是重複、難以規範要求。比如一些基礎的UI、動態請求能力等,每個模塊單獨實現打包,都會成爲線上運行時的負累。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於公共能力的抽取也有一些“套路”:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"Entry提供的全局實例。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"你的頂層APP可以給每個實例化的子模塊注入一個全局能力引用,提供一些幾乎每個模塊都要使用的能力,例如ajax請求能力、業務埋點監控能力等。好處是一些底層能力你可以很好地控制起來,缺點是子模塊和entry之間的耦合會更深一些。如果你有一個子模塊需要被應用在不同的entry APP中,那針對每個APP,你可能需要一個適配器層來屏蔽差異。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"抽取公共內容打包。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"公共內容中最常見的就是polyfill了,每個模塊通過使用**「usage」**(https://babeljs.io/docs/en/babel-preset-env#usebuiltins)單獨打包,好處是開發、線上環境一致,缺點是polyfill內容會高度重複,如圖五。","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/45/45b575b252c2892246492e867a00cb10.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"圖五","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"優化的思路也顯而易見,將polyfill以「entry」方式僅引入一次,如圖六。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/fc/fce0157058ecb3f59d477585ce9c6419.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"圖六","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但如何保證這個公共polyfill也是按需優化的呢?有一些辦法,例如:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據運行時環境實時加載polyfill,如polyfill.io。比較重的方案,個人覺得收益不一定非常划算。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"webpack@5的「module federation」。還在beta階段,建議生產環境慎重(如果你webpack升5之後工程還跑得起來的話)。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"約定polyfill的白名單/黑名單。這個是我們工程中最後使用的方案,理由是輕量、穩妥,工程角度覺得“划算”。當然這個方式會有一個問題呼之欲出,單獨開發的模塊怎麼保證使用的語法不會超出白名單?我們的解決方案是:爲了在靈活性上有一定的規範約束,開發微服務子模塊需要使用一個我們封裝的dev cli workspace,在開發時完成App級別的變量注入、語法校驗。發佈編譯階段也會有相應的控制,因此「開發環境工具」也是微服務改造的利器。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上是我們在微前端工程化技術選型、架構設計中的部分思考和經驗,篇幅限制不再展開更多。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3.3 自由 vs 規範","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後,總結一下我對微前端的一些個人看法。靈活、自由可能是很多大型項目、團隊設計的期望。但我認爲,一個APP在靈活同時,控制力和規範也是非常重要的。你可能會希望系統能從數據上規範、交互視覺上高度一致、體驗優良、性能良好。那麼,在一開始向微服務改造的時候,就需要提前設計規範控制。否則,微前端熱潮之下不久,可能又要開啓一片“微前端治理”相關的討論。畢竟,從“限制”到“自由”容易,從“自由”到“限制”可難了。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"參考文獻","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Micro Frontends: https://martinfowler.com/articles/micro-frontends.html#IncrementalUpgrades","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"@babel/preset-env · Babel: https://babeljs.io/docs/en/babel-preset-env#usebuiltins","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Polyfill.io: http://polyfill.io/","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Module Federation | webpack: https://webpack.js.org/concepts/module-federation/","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"嘉賓介紹:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"馬海娜,百度商業平臺研發部前端資深研發工程師,主要負責廣告託管業務的前端架構。專注於在快速迭代的業務中打造高效、可擴展、體驗良好的前端業務架構。愛好擼碼和擼貓。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"推薦閱讀:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"http://mp.weixin.qq.com/s?__biz=Mzg5MjU0NTI5OQ==&mid=2247506720&idx=1&sn=fafc2be64d476fa4bb0a93f286cbf9ab&chksm=c03eeb5cf749624a146a29150750b1194c605044e65dae0221dbd3f8501a21030519dae5c56b&scene=21#wechat_redirect","title":"","type":null},"content":[{"type":"text","text":"|百度商業大規模高性能全息日誌檢索技術揭祕","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"http://mp.weixin.qq.com/s?__biz=Mzg5MjU0NTI5OQ==&mid=2247506407&idx=1&sn=3e65787b30cd6ab2b06d784cc5d0698a&chksm=c03ee99bf749608d3f7c74b4e2370d3b7d3ff102d57763ec8c6d4abdf22ee6229829f3f12d93&scene=21#wechat_redirect","title":"","type":null},"content":[{"type":"text","text":"|快速剪輯-助力度咔智能剪輯提效實踐","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"http://mp.weixin.qq.com/s?__biz=Mzg5MjU0NTI5OQ==&mid=2247506362&idx=1&sn=7d2e59b8daed0b4adf369f6656dc09da&chksm=c03ee9c6f74960d00a859de5f33d936fc92cc8a9a9eaa49a221b8afa5118ae6312cbd590e7e7&scene=21#wechat_redirect","title":"","type":null},"content":[{"type":"text","text":"|短視頻個性化Push工程精進之路","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"---------- END ----------","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"百度 Geek 說","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"百度官方技術公衆號上線啦!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"技術乾貨 · 行業資訊 · 線上沙龍 · 行業大會","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"招聘信息 · 內推信息 · 技術書籍 · 百度周邊","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"歡迎各位同學關注","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章