是什么
是一個用來解決多個應(yīng)用之間代碼共享的問題丁稀,可以讓我們更加優(yōu)雅的實現(xiàn)跨應(yīng)用的代碼共享的webpack插件送矩。
解決了什么
解決如何高效實現(xiàn)模塊共享的問題
怎么發(fā)展來的
1. npm
這是最原始簡單的方法悯搔,也是我們最常用的方法轧钓。
缺點: npm發(fā)包需要經(jīng)歷構(gòu)建蓬坡,發(fā)布骏啰,引用三階段离斩,過程繁瑣银舱。
2. UMD模式打包
根據(jù)傳入的參數(shù),判斷是執(zhí)行AMD模式還是CommonJs模式跛梗。
缺點:包體積很大寻馏,不同庫之間容易沖突
3. 微服務(wù)
可以做到多個獨立的構(gòu)建組成一個應(yīng)用程序,這些獨立的構(gòu)建之間不存在依賴關(guān)系核偿,因此可以單獨開發(fā)和部署它們诚欠。
缺點:獨立的構(gòu)建一多,就需要多次部署維護(hù)漾岳,對運維來說工作量很大轰绵。另外微服務(wù)一般是子應(yīng)用獨立打包,耦合度很低尼荆,但是沒辦法做到公共模塊共用左腔。
4. MouduleFedration
直接將一個應(yīng)用的包應(yīng)用于另一個應(yīng)用,同時具備整體應(yīng)用一起打包的公共依賴抽取能力捅儒。(因為之前的方案液样,大都是無法抽離出公共模塊,導(dǎo)致整體打包很慢)野芒。讓應(yīng)用具備模塊化輸出能力蓄愁,其實開辟了一種新的應(yīng)用形態(tài),即 “中心應(yīng)用”狞悲,這個中心應(yīng)用用于在線動態(tài)分發(fā) Runtime 子模塊撮抓,并不直接提供給用戶使用。
有什么重要的概念
- webpack構(gòu)建:一個獨立項目通過webpack打包編譯而產(chǎn)生資源包摇锋。
- remote:一個暴露模塊供其他webpakc構(gòu)建消費的webpack構(gòu)建丹拯。(生產(chǎn)者)
- host:一個消費其他remote模塊的webpack構(gòu)建。(消費者)
一個構(gòu)件既可以是生產(chǎn)者荸恕,也可以是消費者乖酬。
怎么用
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
// other webpack configs...
plugins: [
new ModuleFederationPlugin({
name: "app_one_remote",
remotes: {
app_two: "app_two_remote",
app_three: "app_three_remote"
},
exposes: {
AppContainer: "./src/App"
},
shared: ["react", "react-dom", "react-router-dom"]
}),
new HtmlWebpackPlugin({
template: "./public/index.html",
chunks: ["main"]
})
]
};
配置說明
- name 當(dāng)前應(yīng)用名稱,需要全局唯一融求。(到時候別的應(yīng)用來消費就是根據(jù)這個name找到這個應(yīng)用的)
- remotes 可以將其他項目的 name 映射到當(dāng)前項目中咬像。(消費)
- exposes 表示導(dǎo)出的模塊,只有在此申明的模塊才可以作為遠(yuǎn)程依賴被使用。(生產(chǎn))
- shared 是非常重要的參數(shù)县昂,制定了這個參數(shù)肮柜,可以讓遠(yuǎn)程加載的模塊對應(yīng)依賴改為使用本地項目的 React 或 ReactDOM。如果不配就容易出現(xiàn)多次加載的情況(也就是說消費者知道生產(chǎn)者自己擁有這樣的模塊時才會用到這個配置)
使用場景
一共有三個微應(yīng)用:lib-app倒彰、component-app审洞、main-app,角色分別是:
- lib-appas remote,暴露了兩個模塊react和react-dom
- component-app as remote and host,依賴lib-app,暴露了一些組件供main-app消費
- main-app as host,依賴lib-app和component-app
lib-app
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痴晦;
- 第一個是本項目的入口文件(該項目只是暴露接口,所以該文件為空)
- 第二個是遠(yuǎn)程入口文件辩昆,其他webpack構(gòu)建使用阅酪、訪問本項目暴露的模塊時,須通過它來加載
- 第三個和第四個是暴露的模塊汁针,供其他項目消費
component-app
module.exports = {
//...省略
plugins:[
new ModuleFederationPlugin({
name: "component_app",
filename: "remoteEntry.js",
exposes: {
"./Button":"./src/Button.jsx"
},
remotes:{
"lib-app":"lib_app@http://localhost:3000/remoteEntry.js"
}
}),
]
}
這里依賴了lib-app术辐,后面的值就是lib-app ModuleFederation應(yīng)用的入口文件地址。暴露出來Button組件施无。
main-app
//webpack.config.js
module.exports = {
//...省略
plugins:[
new ModuleFederationPlugin({
name: "main_app",
filename: "remoteEntry.js",
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",
})
]
}
這里和上面的ModuleFederation配置基本一樣辉词,重點是建立依賴之后怎么用?
由于需要等待基礎(chǔ)模塊加載完畢猾骡,所以需要配置懶加載入口bootstrap.js.
bootstrap.js
import ReactDOM from 'lib-app/react-dom';
import React from 'lib-app/react'
ReactDOM.render(<App />, document.getElementById("app"));
App.jsx
import Button from 'component-app/Button'
export default class App extends React.Component{
constructor(props) {
super(props)
//省略...
}
//省略...
render(){
return (<div>
//省略...
</div>)
}
}