webpack 5 模块联邦实现微前端疑难问题解决
生活随笔
收集整理的這篇文章主要介紹了
webpack 5 模块联邦实现微前端疑难问题解决
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
webpack 5 模塊聯邦實現微前端疑難問題解決
說明
webpack 5 新增 Module Federation(模塊聯邦)功能,他可以幫助將多個獨立的構建組成一個應用程序,不同的構建可以獨立的開發與部署。
借助模塊聯邦我們可以一定程度的實現微前端
- 微前端官網
- Module Federation 模塊聯邦官網
- Module Federation 案例
概念
1. 什么是微前端?
- 微前端將微服務理念擴展到前端開發,一般來講一個微服務架構中會有多個后端團隊開發不同的業務服務,而前端通常只有一個團隊,集中維護一個 SPA 單頁應用,隨著時間累加,前端團隊維護的 SPA 會隨著業務增長越來越大,變得難以維護(項目啟動耗時、CI\CD 耗時等);
- 微前端可以幫助我們像后端一樣將 SPA 應用按照業務拆分為多個可獨立維護部署的應用,這樣一方面我們可以實現,哪個業務改變就近更新哪個業務的前端;又可以幫助搭建從前端到后端單一業務領域的團隊
- 可以理解為微前端是一種將多個可獨立交付的小型前端應用聚合為一個整體的架構風格
2. 什么樣的產品適合微前端?
- 大型前端項目
- 多個項目間跨應用模塊共享
- 有拆分出多個獨立的子系統,獨立部署維護的需求
3. 模塊聯邦 module Federation 能干什么?
- 模塊聯邦提供了可以在當前應用中遠程加載其他服務器上應用的能力
- 使用 module Federation 可以實現一個去中心話的應用部署群,每個應用都單獨部署,每個應用都可以引用其他應用,也能被其他應用所引用(js 級別)
- 借助第三方工具可實現跨技術棧開發 (react + vue 2)
- 可以用來實現前端項目平滑迭代(舊項目是一個應用,新項目是一個應用,兩者之間通過遠程連接,這樣我們可以逐漸豐富 新項目來替換舊項目的內容)
- 又可以幫助搭建從前端到后端單一業務領域的團隊
4. 什么是本地模塊與遠程模塊,什么是 host \ remote?
- host:消費其他 remote 的 webpack 構建(即使用遠程模塊的應用)
- remote: 被 host 消費的 webpack 構建(即提供可用的模塊給其他應用使用)
- host 與 remote 是相對的,一個應用既可以是 host 有可以是 remote,因此 模塊聯邦實現的微前端又是去中心化的。
- 本地模塊即為我們本地開發時當前項目內的模塊
- 遠程模塊不屬于當前構建,它是在運行時被加載進來的 remote 應用提供的模塊
5. 模塊聯邦的負面影響
- 運行時加載遠程模塊等邏輯,可能導致一定的性能問題
- 本地開發需要開啟多個端口的服務,比較麻煩
- 按需加載第三方依賴比較難實現
- 比起傳統 spa 項目結構上有些復雜
- 迭代時的版本控制需要更多關注
模塊聯邦 Api
// webpack.config.js export default {plugins: [new ModuleFederationPlugin({name: "app_two", // 當前應用名filename: "remoteEntry.js", // 外部應用引用當前應用模塊時的加載入口exposes: {Search: "./src/Search" // 輸出給外部應用使用的模塊},remotes: {appOne: 'appOne@http://localhost:3003/remoteEntry.js' // 當前應用會用到的遠程應用地址}shared: [ // 與遠程模塊共享的模塊,與遠程模塊共同配置,這樣在頁面中就只會加載一次這個library, 用來避免重復加載第三方依賴"react", "react-dom"]})] };// /index.js 注意,入口一定要動態引入模塊 import('./bootstrap');// /bootstrap.js ReactDOM.render(<App/>document.getElementById('root'), );// /App.js 中使用遠程應用的模塊 import('遠程應用/遠程應用模塊') const AppOne = React.lazy(() => import('appOne/Button'));shared 說明
/ ***應該在共享范圍內共享的模塊的高級配置。* / declare interface SharedConfig {/ ***直接在異步請求后面包含提供的和后備模塊。這也允許在初始加載中使用此共享模塊。所有可能的共享模塊也都需要急切。* /eager?: boolean; / ***應提供共享范圍的提供的模塊。如果在共享作用域中找不到共享模塊或版本無效,則還充當回退模塊。默認為屬性名稱。* /import?: DevTool; / ***軟件包名稱,用于從描述文件中確定所需的版本。僅當無法根據請求自動確定包名稱時才需要。* /packageName?: string; / ***共享范圍中來自模塊的版本要求。* /requiredVersion?: DevTool; / ***在共享范圍內的此鍵下查找模塊。* /shareKey?: string; / ***共享范圍名稱。* /shareScope?: string; / ***在共享范圍內僅允許共享模塊的單個版本(默認情況下處于禁用狀態)。* /singleton?: boolean; / ***如果版本無效,則不接受共享模塊(默認為是,如果本地后備模塊可用并且共享模塊不是單例,否則為no,如果未指定所需的版本,則無效)。* /strictVersion?: boolean; / ***所提供模塊的版本。將替換較低的匹配版本,但不會替換較高的版本。* /version?: DevTool; }開發中遇到的問題
1. 怎么仿照微服務中的服務發現,實現對不同應用版本的運行時管理?
由于 關于“遠程依賴應用的引用”是在 build 打包時,打包到代碼中的固定值(url),為了通過文件名區分是否有更新,我們需要給remoteEntry.[contenthash].js 加上 hash,;這個時候如果, “遠程依賴應用”有版本更新,那么使用這個“遠程依賴應用“的應用也要更新(否則拿到的時是期的資源),如此一來我們 “只發布有改動的應用” 這個目標就沒辦法達成了。
為了實現這個目標,最好是將 remoteEntry 的確切地址(url)在項目運行時注入,這樣就避免了改動一個應用,其他應用也跟著更新的窘境
參考案例
除此之外還可以借助 __webpack_init_sharing__ __webpack_share_scopes__ 實現 “動態遠程容器” 參考 通過這套方案我們可以實現:項目線上運行時,動態決定要渲染哪些遠程應用模塊
2. 模塊聯邦對 Tree shaking 有什么影響
- 因為模塊聯邦中的各個應用是各自打包的,沒有辦法綜合所有應用來做 Tree shaking(僅能各自應用各自 Tree-shaking );
- 這樣會造成 應用間對于某個依賴的冗余引用,例如多個應用都使用了 Antd 的 Button 組件,就會在每個應用中都打包一份 Button 組件
- 如果把依賴提取為公共依賴,則只能全量引用,同樣造成代碼體積過大(可以成為公共依賴的都需要是全量的,例如 react react-dom 等);
- 考慮到以上問題,我們可以使用一個麻煩一點的方案:再增加一個”庫應用“,這個應用專門用來做需要 Tree-shaking 依賴的打包,所有關于這個依賴的引用都要指向這個項目中,麻煩點在于需要確定是否能都通過腳本實現自動化追加,以及 TS 項目中類型檢查怎么辦
- 注意: 模塊聯邦中不可使用 webpack 的 module.noParse 來處理工具庫的解析,否則會引發模塊引用的 報錯
3. 模塊聯邦對 context 使用有什么影響?
- 使用 context, 我們可以避免通過中間元素來向下傳遞 props
- Context 在模塊聯邦的模塊(應用)之間無法自動傳遞
- 因此需要在每個應用的入口處重新提供一個 Provider 來將與上層同樣的context 數據傳遞下去
4. 模塊聯邦對狀態管理有什么影響?
- 與 context 類似,例如 redux 這樣的狀態管理工具內部也是使用的 context 實現
- 以 redux 為例,一個 SPA 項目中我們會借助 react-redux 提供的 Provider 來將業務組件包裹,然后通過 connect 將 store 中的狀態釋放到被包裹的組件(及其子組件)中。
- 對于 模塊聯邦中的各個應用來說,即使一個應用充當了另外一個含有 Provider 應用的子組件,store 也不會傳遞下去,這是因為兩個應用是分別構建的。
- 為了所有模塊聯邦中的應用都共用一套狀態,我們可以在每個應用的頂層都通過 Provider 包裹一下,然后給每個應用都傳入 store 這樣就可以實現連接了
5. 模塊聯邦中 Typescript 類型檢查怎么用,eslint 會有影響么?
如果我們的項目使用 lerna 做一個monorepo 的倉庫,每個子 package 代表著一個 應用,預期這些應用都會用 TS 編寫,而且 TS 的配置應該是一樣的,所以,我按照如下配置
- 整個倉庫只有最外層會有 tsconfig.json 以及 .eslintrc.js 配置文件,所有子package 中都會使用外層的配置
- .eslintrc.js 中引用正確的 ts 的配置文件路徑即可
- 因為應用之間的模塊引用會是一個類似webpack alias 的形式,引用的遠程模塊是跨包的,也需要ts 提供的屬性提示,所以我們要給 tsconfig.json 中配置一下路徑解析,否則 ts 找不到模塊位置
- 如下 如果請求模塊 @module/library/antd 則會映射到 /modules/library/antd/index 這個文件上,ts 的 paths 會提供一定的路徑解析支持,但是沒辦法更細致的解析(如:沒有辦法把 @module/library/antd 解析到 /modules/library/src/antd.ts)因此,我們開發時就不要在每個子 package 中創建 src 文件夾了,而是直接展開寫,這樣我們就不用在增加遠程模塊時頻繁的改動 tsconfig.json 文件了
6. 模塊聯邦對 code split 有什么影響?
- Runtime chunk 不會被提取出來了(會造成解析錯誤)runtimeChunk 用來將運行時代碼提取出來,避免因運行時代碼改變導致的其他chunk hash 改變,從而影響瀏覽器緩存的問題。
7. 模塊聯邦對樣式文件有什么要求?
- 每個普通應用在 build 后都會生成各自的 main-style.css 如果使用了 css-module 則不用擔心樣式覆蓋和引用問題
- 如果是 global(或者不使用 css-module) 的樣式則存在覆蓋問題,因此建議使用 css-module (dev 開發和 build 兩個環境都會樣式覆蓋,行為表現一致,這樣可以確保,我們開發時什么表現,build 到生成環境后還是什么表現)
- 如果是 ”庫應用“ (例如 antd)的打包則需要注意,收到 babel-plugin-import 插件的影響 在 ”庫應用“ 中類似 export {Button} from ‘antd’; 這種寫法,會導致 樣式文件輸出不了 ISSUE
- 目前沒找到更好的方案,只能先用下邊的方案來做
8. 模塊聯邦對 路由有什么影響?
- 基本沒什么影響,react-router 的 Hash 路由可以正常使用
9. DLL 與模塊聯邦結合?
- 兩者可以結合使用
- 但是因為 DLL 與其他業務代碼是分開打包的,使用時又是一起使用的,這樣會有一個問題:DLL 打包模塊中的 chunkid moduleId與 業務代碼打包模塊中的 chunkid 重復了,這會導致模塊識別報錯。解決的方法是將 DLL 打包的 moduleId chunkId 生成方式由 (deterministic => named), 減少重復的可能
10. React 技術棧與 Vue 技術棧結合能否實現
- 借助 vuera 可以實現在 react 項目中引用 vue 組件,或者在 Vue 項目中引入 React 組件,但是優越 vuera 這個項目缺少維護,目前僅在 vue 2.x 中試驗成功。
- 再結合模塊聯邦,我們可以實現某個遠程應用通過 Vue 技術棧實現(單獨一個 倉庫維護),這樣可以保證其他遠程應用的干凈
11. 模塊聯邦對日常 dev 開發會有什么影響?
- 模塊聯邦項目中,可能需要有多個應用需要同時啟動,聯合調試,且需要 HMR 來幫助刷新瀏覽器,例如:我測試的項目中會有一個 host 主應用,其余有若干個遠程應用
- webpack 支持 MultiCompiler (即多個 webpack 配置文件一起執行)但是 webpack-dev-server 對 MultiCompiler 的支持卻不太好
- 截止 2021.04.07 webpack-dev-server 的最新發行版本還是 v3.x.x ,這個版本有一個問題,就是對 MultiCompiler 支持不好(會導致只有一個 compiler 有 HMR 支持,其他構建沒有實時刷新),而 webpack-dev-server@4.0.0-beta.2 雖然支持 MultiCompiler,但是對于模塊聯邦會出現模塊解析失敗問題
- 經過多次嘗試最終提供一個解決方案如下
- 主應用通過 webpack-dev-server@4.0.0-beta.2 來實現源碼編譯、源碼改動監控、編譯后文件映射到頁面、頁面實時刷新
- 其他應用 直接使用 webpack 的 watch 來實現源碼編譯、源碼改動監控, 至于編譯后文件映射到頁面、頁面實時刷新 這兩項則借助注意用的 DevServer 實例順便實現
- 注意:非主應用的實時刷新的全量的,主應用的實時刷新是增量的
- 遺留問題1:經過如上配置后留下一個問題:webpack-dev-server 提供的 proxy 只能在 主應用中使用,其他應用中沒法使用。但是,還使用這個方案的原因是:我們的 proxy mock server 代理應該不會使用 webpack-dev-server 實現,所以這個功能是低概率使用的,而且即使使用也不會上到生成環境,影響不大。
- 遺留問題2:非主應用的應用由于是通過 webpack 直接構建出來的,其編譯結果會存在真實的文件(即在項目中生成一個編譯后結果的文件夾,而 webpack-dev-server 會將編譯后結果存放到虛擬內存中,虛擬內存要比真正文件讀寫性能要好)
總結
以上是生活随笔為你收集整理的webpack 5 模块联邦实现微前端疑难问题解决的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: antlr java_Antlr4 入门
- 下一篇: QPI与GMI/Infinity Fab