最近搭了一个qiankun的微前端项目,总结一下整个搭建过程; 我是基于webpack5.0, reactv18.2.0
- 微前端项目文件布局如下,qiankun-base是主应用,qiankun-react,qiankun-vue是子应用;
- 主应用
npx create-react-app qiankun-main
我是用craco来配置webpack的,故需要引入@craco/craco和craco-less 所以package.json修改为:
"scripts": {
"mock": "cross-env REACT_APP_MOCK=true craco start",
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject"
},
将缺的依赖补齐 qiankun、react-dom、babel-plugin-import等等 App.js 修改为
import React, { useEffect } from 'react';
import { HashRouter as Router, Link } from 'react-router-dom';
import { registerMicroApps, start, initGlobalState } from 'qiankun';
import { ConfigProvider } from 'antd';
import zhCN from 'antd/es/locale/zh_CN';
import './App.less';
const App = () => {
const apps = [{
// 微应用名称,在微应用的打包配置文件种library的名称,微应用之间必须确保唯一
name: 'react',
// 微应用地址,子应用必须支持跨域 fetch
entry: '//localhost:8081/#/',
// 微应用挂载的容器节点
container: '#appContainer',
// 微应用的激活规则,访问到react的时候跳转子应用
activeRule: '#/react',
// 主应用需要传递给微应用的数据
props: { token: 'gaiery-token-xxx'}
}]
useEffect(() => {
// 注册app
registerMicroApps(apps);
// 开启
start();
}, [])
return (
<ConfigProvider locale={zhCN}>
<Router>
<div>
<div className='app-menu'>
<Link to='/react'>react</Link>
<Link to='/vue'>vue</Link>
</div>
<div id="appContainer"></div>
</div>
</Router>
</ConfigProvider>
);
};
export default App;
到此为止,主应用算配置基本配置完成
- react子应用的配置 先在src里新建一个public-path.js文件
if(window.__POWERED_BY_QIANKUN__) {
// 动态设置 webpack publicPath,防止资源加载出错
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
index.js修改为如下
import './public-path';
import { createRoot } from 'react-dom/client';
import App from './App';
let root;
// 将render方法用函数定义,供后续主应用与独立运行调用
function render (props) {
const { container } = props; console.log('container---', container);
const dom = container ? container.querySelector('#appRoot') : document.getElementById('appRoot');
root = createRoot(dom);
root.render(
<App />
)
}
// 判断是否在qiankun环境下,非qiankun环境下独立运行
if(!(window).__POWERED_BY_QIANKUN__){
render({});
}
// 各个生命周期
// bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
export async function bootstrap() {
console.log('react app bootstraped');
}
// 应用每次进入都会调 mount 方法,通常我们在这里触发应用的渲染方法
export async function mount(props) {
console.log('mount----');
render(props);
}
// 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
export async function unmount(props) {
console.log('unmount----');
root.unmount();
}
App.js 将HashRouter的basename改为basename={window.POWERED_BY_QIANKUN? '/react' : '/'},如下
import React from 'react';
import { HashRouter as Router, Route, Switch } from 'react-router-dom';
// import AntdPresetsConfigProvider from '@components/AntdPresetsConfigProvider';
import { reactLoadable } from 'utils';
import { ConfigProvider } from 'antd';
import 'antd/lib/style';
// import zhCN from 'antd/es/locale/zh_CN';
import './App.less';
import packageJson from './../package.json';
const prefixCls = packageJson.antdConfig.prefixCls;
// import Home from './routes/home';
const Home = reactLoadable(() => import('./routes/home'));
const Login = reactLoadable(() => import('./routes/login'));
const App = () => (
<ConfigProvider prefixCls={prefixCls}>
<Router basename={window.__POWERED_BY_QIANKUN__? '/react' : '/'}>
<div>
<Switch>
<Route exact path="/login" component={Login} />
<Route path="/home" component={Home} />
<Route path="/" component={Home} />
</Switch>
</div>
</Router>
</ConfigProvider>
);
export default App;
为了让主应用识别到子应用,子应用需要暴露出来一下信息,webpack需要改动一下output
const packageName = require('./package.json').name;
configure: (webpackConfig, {env, path}) => {
console.log('env', env);
console.log('path', path);
console.log('webpackConfig--', webpackConfig);
webpackConfig.output = {
...webpackConfig.output,
library: `${packageName}-[name]`,
libraryTarget: 'umd',
chunkLoadingGlobal: `webpackJsonp_${packageName}`
}
return webpackConfig;
}
这里的packageName是对应主应用里的这个name
最终的效果如图
以下是qiankun的开发指南,链接如下 qiankun.umijs.org/zh/guide