我正在尝试按照微前端文章创建一个小项目来实现微前端架构。我正在为每个 MFE(微前端)创建多个存储库,并为该应用程序使用 Redux。我有以下架构:
Frame
- 一个集中式(主)存储库,负责基于路由渲染主要应用程序部分和 MFE(我正在使用 connected-react-router
)。它初始化 Redux 存储,并添加一个 injectReducer
函数来动态添加减速器,如 redux 文档中的 code splitting 中所述。该框架获取特定 MFE 的清单并呈现它。该框架还有一些用于身份验证目的的 redux 数据。
MFEs
- 这些是单独的功能应用程序,并按帧渲染。
injectReducer
函数来动态添加要存储的减速器。为此,我需要访问由框架创建的相同
store
实例。 正如这个答案中提到的,理想的方法是导出创建的商店实例并使用它,但是我们如何在多个存储库中共享相同的商店实例?
如果您使用 redux,通常的方法是为整个应用程序建立一个单一的、全局的、共享的存储。然而,如果每个微前端都应该是自己独立的应用程序,那么每个微前端都有自己的 redux 存储是有意义的。 redux 文档甚至提到“将 Redux 应用程序隔离为更大应用程序中的组件”作为拥有多个商店的正当理由。
长话短说:不要分享你的 redux store
在微前端之间共享
任何东西不再使它们成为独立的实体,并且违背了它的全部目的。现在它只是一个过度设计的庞然大物。只需将这些各自的存储库转换为整体存储库中定义明确的模块即可。仍然可以在统一的存储库中将职责划分到各自的孤岛中。只是需要更多纪律。
eventListeners
和
CustomEvent
使用合成事件进行跨微前端通信,如下所示:MF-1:
function Cart() {
const [cartItems, addCartItems] = useState([]);
const handleAddToCart = (event) => {
addCartItems((currentItems) => [...currentItems,
event.detail.item]);
};
useEffect(() => {
window.addEventListener('addToCart', handleAddToCart);
return () => {
window.removeEventListener('addToCart', handleAddToCart)
}
}, [handleAddToCart]);
...
}
function itemLookUp() {
const handleSubmit = (item) => {
const customEvent = new CustomEvent('message', { detail: item });
window.dispatchEvent(customEvent)
}
...
}
MF-1ModuleFederationPlugin
从一个微前端 (MFE) 公开模块,如
。在主机 MFE 中,您可以使用'./remoteEntry.js'
属性来使用此公开的模块,并指定其位置。最后,在主机 MFE 代码中导入共享模块,将其视为本地模块。remotes
/webpack.config.js
plugins: [
new ModuleFederationPlugin({
name: "MF1",
filename: "remoteEntry.js",
remotes: {},
exposes: {
"./store": "./src/Redux/store.ts"
}
)]
MF-2
/webpack.config.js
plugins: [
new ModuleFederationPlugin({
name: "MF2",
filename: "remoteEntry.js",
remotes: {
MF1: "MF1@http://localhost:8080/remoteEntry.js",
},
exposes: {}
)
]
你想用在哪里
import store from "MF1/store";
import React from "react";
import ReactDOM from "react-dom/client";
import { Provider } from "react-redux";
import { BrowserRouter as Router } from "react-router-dom";
import "./index.css";
const App = () => {
console.log(store.getState(), "store");
return (
<Provider store={store}>
<Router>
// routes
</Router>
</Provider>
);
};
const rootElement = document.getElementById("app");
if (!rootElement) throw new Error("Failed to find the root element");
const root = ReactDOM.createRoot(rootElement as HTMLElement);
root.render(<App />);