關於其構建過程理解,主要學習這篇文章:https://blog.csdn.net/qq_40882724/article/details/116860994
一、三個概念
首先,要理解三個重要的概念:
(1)webpack構建:一個獨立項目通過 webpack 打包編譯而產生資源包。
(2)remote:一個暴露模塊供其他 webpack 構建消費的webpack構建。
(3)host:一個消費其他 remote 模塊的 webpack 構建。
簡言之:一個webpack構建可以是 remote(即服務的提供方),也可以是 host(即服務的消費方),也可以同時扮演服務提供者和服務消費者,完全看項目的架構。
二、host 與 remote 兩個角色的依賴關系
可用下圖表示:
需要指出的是,任何一個webpack構建既可以作為host消費方,也可以作為remote提供方,區別在於職責和webpack配置的不同。
三、案例實操解析
項目依賴關系介紹,一共有三個微應用:lib-app、component-app、main-app,角色分別是:
(1)lib-app 作為 remote, 暴露了兩個模塊 react 和 react-dom
(2)component-app 作為 remote 和 host, 依賴 lib-app,暴露了一些組件供 main-app 消費
(3)main-app 作為 host,依賴lib-app和component-app
1、lib-app 暴露模塊
//webpack.config.js module.exports = { //...省略 plugins: [ new ModuleFederationPlugin({ name: "lib_app", filename: "remoteEntry.js", exposes: { "./react":"react", "./react-dom":"react-dom" } }) ], //...省略 }
編譯后的結果如下:
除去生成的 map文件,有四個文件:main.js、remoteEntry.js、...react_index.js、...react-dom_index.js;這里我們需要關注下:編譯生成的各文件的意思。
(1)第一個是本項目的入口文件(該項目只是暴露接口,所以該文件為空)
(2)第二個是遠程入口文件,其他webpack構建使用、訪問本項目暴露的模塊時,須通過它來加載
(3)第三個和第四個是暴露的模塊,供其他項目消費
2、component-app的配置
依賴 lib-app
暴露三個模塊組件 Button
、Dialog
、Logo
//webpack.config.js
module.exports = { //...省略
plugins:[ new ModuleFederationPlugin({ name: "component_app", filename: "remoteEntry.js", exposes: { "./Button":"./src/Button.jsx", "./Dialog":"./src/Dialog.jsx", "./Logo":"./src/Logo.jsx" }, remotes:{ "lib-app":"lib_app@http://localhost:3000/remoteEntry.js" } }), ] }
三個暴露的組件看原文章咯,這里只摘錄我覺得有用的了解學習。
3、main-app的配置
main-app 依賴兩個項目 lin-app
、component-app
。這里的 remotes 就加載了 2 個。
///webpack.config.js
module.exports = { //省略...
plugins: [ new ModuleFederationPlugin({ name: "main_app", remotes:{ "lib-app":"lib_app@http://localhost:3000/remoteEntry.js", "component-app":"component_app@http://localhost:3001/remoteEntry.js" }, }), new HtmlWebpackPlugin({ template: "./public/index.html", }) ] //省略...
};
由於需要等待基礎模塊加載完畢,所以需要配置懶加載入口 bootstrap.js。
// 1、webpack打包入口文件
import("./bootstrap.js") // 2、bootstrap.js
import App from './App.jsx' import ReactDOM from 'lib-app/react-dom'; import React from 'lib-app/react' ReactDOM.render(<App />, document.getElementById("app"));
// 3、根組件App.jsx
import React from 'lib-app/react'; import Button from 'component-app/Button' import Dialog from 'component-app/Dialog' import Logo from 'component-app/Logo' export default class App extends React.Component{ constructor(props) { super(props) //省略...
} //省略...
render(){ return (<div>
//省略...
</div>) } }
然后就會看到結論:
1、從對應項目加載到了需要的組件;
2、從查看控制台可以看到所需資源進行了很好的分離:
四、基本原理
從 host 的代碼着手,簡單分析這一切是如何交互、工作的。程序從 main.js 里的一段代碼開始:
__webpack_require__.e("bootstrap_js").then(__webpack_require__.bind(__webpack_require__,"./bootstrap.js"))
__webpack_require__.e("bootstrap_js")
是加載 id 為bootstrap_js
的chunk的所有依賴,返回一個promise。
等一切依賴就緒,再獲取 ./bootstrap.js
模塊並執行。這里是__webpack_require__.e
的代碼:
__webpack_require__.e = (chunkId) => { return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => { __webpack_require__.f[key](chunkId, promises); return promises; }, [])); };
上面一段代碼做了一件事,遍歷__webpack_require__.f
對象並依次執行對象里的成員函數,此時該對象有兩個成員:
{ remotes:(chunkId, promises) => { //查找chunkId bootstrap_js對應的所有遠程模塊並加載
var chunkMapping = { "bootstrap_js": [ "webpack/container/remote/lib-app/react", "webpack/container/remote/component-app/Button", //省略...
] }; var idToExternalAndNameMapping = { "webpack/container/remote/lib-app/react": [ "default", "./react", "webpack/container/reference/lib-app" ], "webpack/container/remote/component-app/Button": [ "default", "./Button", "webpack/container/reference/component-app" ], //...省略
}; }, j:(chunkId,promises)=>{ //負責加載chunkId對應的本地模塊
} }
綜上,bootstrap_js 對應了兩個promises:
1、一個負責遠程依賴加載
2、另一個負責本地加載
等到所有依賴模塊加載完准備就緒,才會 require 模塊並執行。
當然,細節遠不止此。現在這里先簡單了解下,以后真要技術選型,或項目中使用的話,再按需學習、深入研究吧。