项目实践
主应用
主应用不限技术栈,只需要提供一个容器 DOM,然后注册微应用并 start 即可。
注册微应用并启动:
// 项目入口页
/**
* 主应用 **可以使用任意技术栈**
* react create-react-app为例,可切换尝试
*/
import render from './render/ReactRender';
**
* Step1 初始化应用(可选)
*/
render({ loading: true });
const loader = (loading: boolean) => render({ loading });
/**
* Step2 注册子应用
*/
registerMicroApps([
{ // RegistrableApp
name: 'reactApp', // 子应用唯一name
entry: '//192.168.3.10:3001', // html entry
container: '#subapp-viewport', // 子应用挂载点
loader,
activeRule: '/sub-app-react', // 子应用激活规则,对应子应用base
// props - object - 可选,主应用需要传递给微应用的数据。
},
{
name: 'vueApp',
entry: '//192.168.3.10:8080',
container: '#subapp-viewport',
loader,
activeRule: '/sub-app-vue',
// props - object - 可选,主应用需要传递给微应用的数据。
},
{
name: 'vue3App',
entry: '//192.168.3.10:8081',
container: '#subapp-viewport',
loader,
activeRule: '/sub-app-vue3',
// props - object - 可选,主应用需要传递给微应用的数据。
},
{
name: 'purehtml',
entry: '//localhost:7104',
container: '#subapp-viewport',
loader,
activeRule: '/purehtml',
},
],
// loader - (loading: boolean) => void - 可选,loading 状态发生变化时会调用的方法。
{// LifeCycles 全局生命周期钩子函数
// @ts-ignore
beforeLoad: (app) => console.log('[LifeCycle] before load %c%s', 'color: green;', app.name),
// @ts-ignore
beforeMount: [(app) => console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name)],
// @ts-ignore
afterUnmount: (app) => console.log('[LifeCycle] before unmount %c%s', 'color: green;', app.name),
},
);
// 数据通讯
const { onGlobalStateChange, setGlobalState } = initGlobalState({
user: 'qiankun',
});
onGlobalStateChange((value, prev) => console.log('[onGlobalStateChange - master]:', value, prev));
setGlobalState({
ignore: 'master',
user: {
name: 'master',
},
});
/**
* Step3 设置默认进入的子应用
*/
setDefaultMountApp('/sub-app-react');
/**
* Step4 启动应用
*/
start({
sandbox: {
experimentalStyleIsolation: true
}
}
);
//第一个微应用 mount 后需要调用的方法,比如开启一些监控或者埋点脚本。
runAfterFirstMounted(() => {
console.log('[MainApp] first app mounted');
});
React 微应用
以 create react app 生成的 react 17.x 项目为例,搭配 react-router-dom 5.x。
1. 在 src 目录新增 public-path.js:
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
2. 设置 history 模式路由的 base:
<BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/app-react' : '/'}>
// 入口文件 index.js 修改,为了避免根 id #root 与其他的 DOM 冲突,需要限制查找范围。
import './public-path';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
function render(props) {
const { container } = props;
ReactDOM.render(<App />, container ? container.querySelector('#root') : document.querySelector('#root'));
}
function storeTest(props) {
props.onGlobalStateChange((value, prev) => console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev), true);
props.setGlobalState({
ignore: props.name,
user: {
name: props.name,
},
});
}
if (!window.__POWERED_BY_QIANKUN__) {
render({});
}
export async function bootstrap() {
console.log('[react17] react app bootstraped');
}
export async function mount(props) {
console.log('[react17] props from main framework', props);
storeTest(props); // 应用拿到数据
render(props);
}
export async function unmount(props) {
const { container } = props;
ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));
}