我目前正在使用 React、webpack 和模块联合开发一个微前端应用程序。它由一个容器应用程序和两个“子”/遥控器组成。容器应用程序由一个应用程序栏、空的侧抽屉和主 div 组成,它导入的遥控器为实际页面内容公开一个独立的 React 应用程序,以及一个应为侧抽屉呈现导航项的单个组件,每个组件:
远程 MFE 每个都有自己的 Redux 存储,这对于页面内容部分来说效果很好,因为这包括带有 Redux 存储提供程序的 App.tsx。到目前为止,导航组件也工作得很好,因为它所做的只是将路线推送到浏览器历史记录中。
现在我遇到了一个问题:一个遥控器的公开导航组件还必须从其 Redux 存储和调度操作中选择数据。到目前为止这不起作用,因为它是一个公开的组件,并且当它在容器应用程序中渲染时,子 Redux 存储没有 Redux 存储提供程序。我该如何解决这个问题?我读过几次,在微前端之间共享 redux 状态是一种不好的做法,所以到目前为止我一直在努力避免这种情况。导航需要访问的数据基本上只是一个布尔值,这表明应用程序处于提升的“服务模式”,使远程 MFE 呈现更多项目(例如通常隐藏的“删除全部”按钮)。所以也许这也可以通过本地存储或类似的方式共享,这里有哪些最佳实践?
这是我的 webpack 配置和一些相关代码,以便更好地理解:
// container app webpack config (development)
...
plugins: [
new ModuleFederationPlugin({
name: "container_app",
remotes: {
config_app: "config_app@http://localhost:3001/remoteEntry.js",
commissioning_app: "commissioning_app@http://localhost:3002/remoteEntry.js",
},
shared: {
...
},
}),
],
...
// config_app webpack config (development)
...
plugins: [
new ModuleFederationPlugin({
name: "config_app",
filename: "remoteEntry.js",
exposes: {
"./ConfigApp": "./src/bootstrap",
"./ConfigAppNavigation": "./src/components/UI/Misc/ConfigAppNavigation",
},
shared: {
...
},
}),
],
...
// MiniDrawer.tsx in container_app, which uses ConfigAppNavigation to render the navigation items
// (depending on current route, otherwise navigation items are rendered from other MFE child)
...
const ConfigAppNavigation = lazy(() => import("config_app/ConfigAppNavigation"));
const MiniDrawer: React.FC = () => {
...
<Switch>
...
<Route path="/">
<Suspense fallback={<span>loading ...</span>}>
<ConfigAppNavigation onNavigate={onDrawerClose} />
</Suspense>
</Route>
</Switch>
...
}
...
如上所述,在切换到 MFE 设计之前,现在的组件 ConfigAppNavigation 从 config_app 的 Redux 存储中选择/更改了提到的布尔值,现在使用此设置不起作用。
第 1 步:在 Shell/主机应用程序中设置 Redux 安装 Redux 包:在您的 shell 或主机应用程序中,安装必要的 Redux 库。
npm install @reduxjs/toolkit react-redux
创建 Redux 存储:在主机应用程序中,使用 Redux 存储配置创建 store.js 文件。
// store.js
import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './rootReducer';
const store = configureStore({
reducer: rootReducer,
});
export default store;
设置 RootReducer:创建一个 rootReducer.js 文件,在其中组合不同的切片。在这里您可以为应用程序的不同部分添加多个减速器。
// rootReducer.js
import { combineReducers } from 'redux';
import someSliceReducer from './someSlice';
const rootReducer = combineReducers({
someSlice: someSliceReducer,
// add other reducers here as needed
});
export default rootReducer;
提供 Store:使用 Redux Provider 包装您的主机应用程序,以使本地和导入的组件都可以访问该商店。
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import App from './App';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
第 2 步:创建并公开 Redux 切片 每个切片代表您所在州的一部分。为您想要跨微前端共享的特定状态定义一个切片。
定义切片:为共享状态创建新切片,例如会议数据。
// someSlice.js
import { createSlice } from '@reduxjs/toolkit';
const someSlice = createSlice({
name: 'someSlice',
initialState: {
sharedData: null,
},
reducers: {
setSharedData: (state, action) => {
state.sharedData = action.payload;
},
},
});
export const { setSharedData } = someSlice.actions;
export default someSlice.reducer;
导出操作:您需要 setSharedData 来更新主机和微前端组件中的状态。
第 3 步:配置微前端以使用主机存储 现在,我们希望微前端能够访问和使用主机的商店。以下是一些方法:
将商店公开为模块:在主机应用程序的index.js中,公开商店,以便其他应用程序可以导入它。
// index.js
import store from './store';
window.store = store; // Expose globally
在微前端访问Store:在每个微前端中,通过window.store访问全局store并将其传递给Provider。
import React from 'react';
import { Provider } from 'react-redux';
import YourComponent from './YourComponent';
function MicroFrontendApp() {
return (
<Provider store={window.store}>
<YourComponent />
</Provider>
);
}
export default MicroFrontendApp;
第四步:在组件中使用共享状态 现在您的商店可以访问,您可以在本地和远程组件中与其交互。
访问组件中的状态:使用useSelector访问状态并使用useDispatch修改它。
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { setSharedData } from './someSlice';
const SomeComponent = () => {
const sharedData = useSelector((state) => state.someSlice.sharedData);
const dispatch = useDispatch();
const handleUpdate = () => {
dispatch(setSharedData('New Data'));
};
return (
<div>
<h1>Shared Data: {sharedData}</h1>
<button onClick={handleUpdate}>Update Shared Data</button>
</div>
);
};
export default SomeComponent;
第五步:测试和调试 确保状态同步:检查主机应用程序中所做的更改是否反映在微前端中,反之亦然。 调试:Redux DevTools 可以帮助可视化状态并跟踪操作。确保浏览器安装了 Redux DevTools 扩展。