@microup/master
v1.0.0-main-1.0.0.20230823203256
Published
The Master package for the microup platform is the entry to the Micro-frontends code.
Downloads
1
Maintainers
Readme
master
master 包主要负责状态数据的存储,权限校验与重定向、顶层路由处理、多语言等工作。所有的 remote 模块都会经过 master 代码控制然后渲染。
目前未添加权限,多语言模块,如有需要可自行在master添加。
全局的事件触发,通用数据也都可以添加到 master 内,并且这些数据共享,可完成不同模块间数据通信。
store
masterStore 是通过 mobx 进行实现的状态数据池,它可以存储一些全局动态的状态数据,并且在所有 remote 模块中都可以访问到这些数据。
源码
masterStore 的核心代码如下,用于注册一个 Store
import React from 'react';
import {observer, useLocalObservable} from 'mobx-react';
import {runInAction} from 'mobx';
import { Provider } from 'mobx-react';
interface IIssue {
// data
}
const useMainLocalStore = () => {
const masterStore = useLocalObservable(() => ({
data: {} as IIssue,
get(key: string) {
return masterStore.data[key]
},
set(key, value) {
runInAction(function () {
masterStore.data[key] = value
})
}
}));
return masterStore;
};
const MasterStoreProvider: React.FC<any> = observer((props) => {
const {children} = props;
const masterStore = useMainLocalStore();
return <Provider masterStore={masterStore}>{children}</Provider>;
});
export {MasterStoreProvider};
包裹跟节点
import React from 'react';
……
import {MasterStoreProvider} from './store';
……
const InnerIndex = (props) => {
const {match, AutoRouter} = props
return (
<MasterStoreProvider>
……
</MasterStoreProvider>
)
};
export default withRouter(InnerIndex);
使用
可以实现不同 remote 模块间数据通信,也可以使得一个模块触发另一个模块的视图渲染。
测试案例如下:
开启一个 app1 服务作为 host 模块,它可以以一下两种方式访问 remote 模块,一种访问其中 remote 模块注册的一个组件,第二种访问整个的 remote 模块,详情也可查看 app1 与 app2 包的代码。
app1 作为 host 模块
import React from 'react';
import {ExternalComponent} from '@microup/utils';
import {inject, observer} from 'mobx-react';
export default inject('masterStore')(observer((props) => {
const {history, masterStore} = props
return (
<div>
<button onClick={() => {
masterStore.set('app2Test', !masterStore.get('app2Test'))
}}>点击改变 app2Test 变量
</button>
<h5>引入 app2 externalize 组件</h5>
<ExternalComponent
system={{
scope: 'app2', module: 'App2EC',
}}
/>
<h5>全量引入 app2 模块</h5>
<div style={{borderLeft: "2px black solid",paddingLeft:'8px'}}>
<ExternalComponent
{...props}
match={{...props.master, url: '/app1'}}
system={{
scope: 'app2', module: './app2',
}}
/>
</div>
</div>
)
}))
app2 externalize 组件
import React from 'react';
import {observer,inject} from 'mobx-react';
export default inject('masterStore')(observer(({match, masterStore}) => {
return (<div>app2-externalize-components | app2Test: {String(!!masterStore.get('app2Test'))}</div>);
}))
/* externalize: App2EC */
app2 根目录
import React from 'react';
import {Route, Switch} from 'react-router-dom';
import {inject} from 'mobx-react';
import {ErrorPage} from '@microup/master';
const Page1 = React.lazy(() => import('./routes/Page1'));
const Page2 = React.lazy(() => import('./routes/Page2'));
function Index({match, masterStore}) {
return (
<Switch>
<Route path={`${match.url}/page1`} component={Page1}/>
<Route path={`${match.url}/page2`} component={Page2}/>
<Route path={`${match.url}`} component={Page1}/>
<Route path="*" component={ErrorPage}/>
</Switch>
);
}
export default inject('masterStore')(Index);
app2 page1
import React from 'react';
import {inject, observer} from 'mobx-react';
export default inject('masterStore')(observer((props) => {
const {match, masterStore} = props
return (<div>
app2 page1 | app2Test: {String(!!masterStore.get('app2Test'))}
</div>)
}));
master router
moaster router 控制这整个项目的跟路由,路由优先判断是否是/error_page
或者/undefined
进行全局的错误路由拦截,过滤后的路由会转入 AutoRouter,在 AutoRouter 组件中会处理一级路由并分发到对应 remote 模块
master.jsx
const InnerIndex = (props) => {
const {match, AutoRouter} = props
return (
<MasterStoreProvider>
<Switch>
<Route exact path={`${match.url}${match.url === '/' ? '' : '/'}error_page`} component={ErrorPage}/>
<Route exact path={`${match.url}${match.url === '/' ? '' : '/'}undefined`} component={Empty}/>
<Route path={match.url} component={AutoRouter}/>
</Switch>
</MasterStoreProvider>
)
};
AutoRouter 组件位置在 routes.nunjucks.jsx 编译后在 tmp 目录中的 router.index.js 中,tmp 详情参考 boot 包文档。
一级路由处理
在 AutoRouter 中执行了这样代码,其作用是优先匹配 config.js 中设置的 routes 中的路由,如果匹配成功,则访问本地路由组件。
routes 作用是一级路由与对应组件位置的描述
# 只是本地开发 routes: { a1: './src/exampleMaster/App1', # 路由为 http://192.168.20.133:9091/#/a1/ a2: './src/exampleMaster/App2' # 路由为 http://192.168.20.133:9091/#/a2/ }
const AutoRouter = () => (
<Suspense fallback={<Loading/>}>
<CacheSwitch>
{'{{ routes }}'}
<CacheRoute path="*">
<ExternalRoute />
</CacheRoute>
</CacheSwitch>
</Suspense>
);
如果没有匹配成功,则执行 <ExternalRoute />
组件,匹配对应 remote 模块。
<ExternalRoute />
组件中,根据.env
中配置的STATIC_URL
作为基地址,后边拼接 url 地址中的一级路由与/importManifest.js
生成一个新的地址,类似http://192.168.20.101:9095/a1/importManifest.js
。
然后创建一个新的script
标签src
填入上边生成的地址。远程拉取 importManifest.js 代码,实现 remote 模块间连接。(实现方案详情参考动态远程容器)
通常我们会有很多 remote 模块,在 host 模块访问多个 remote 模块时,我们不想在代码中把所有地址都写出来( 类似 STATIC_URL-a1,STATIC_URL-a2 这样,会比较麻烦)。这时我可以创建一个独立的 nginx 帮我们代理。
例如所有模块都访问
http://192.168.20.101:9095/a1/importManifest.js
,而 192.168.20.133:9095 中设置 nginx 做对应的反向代理# STATIC_URL 对应的 nginx location /a1/ { # app1 remote 模块 proxy_pass http://192.168.20.101:9091/; } location /a2/ { # app2 remote 模块 proxy_pass http://192.168.20.123:9092/; }
这样我们访问
http://192.168.20.101:9095/a1/importManifest.js
他会根据一级路由a1
判断访问http://192.168.20.101:9091/a1/importManifest.js
然后拿到 js 静态文件做对应渲染。